Merged in feature/chapter-visibility (pull request #78)
Feature/chapter visibility Approved-by: Christian Cueni
This commit is contained in:
commit
3660a282a3
2
Pipfile
2
Pipfile
|
|
@ -23,7 +23,7 @@ python-dotenv = "==0.13.0"
|
|||
dj-database-url = "==0.4.1"
|
||||
raven = "==6.9.0"
|
||||
django-extensions = "==1.9.8"
|
||||
graphene-django = "==2.2.0"
|
||||
graphene-django = "==2.15.0"
|
||||
django-filter = "==2.0.0"
|
||||
djangorestframework = "==3.8.2"
|
||||
pillow = "==5.0.0"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "a5323ad6907e180d4901fe31c0edc344ee9890f7a95577b2d60e5a1ee19be2f3"
|
||||
"sha256": "58d8faf7e03679ac7b0053dd01e54288d3a719c8ee25c1edf20a74ebcbf87951"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
|
@ -25,9 +25,10 @@
|
|||
},
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094"
|
||||
"sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea",
|
||||
"sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"
|
||||
],
|
||||
"version": "==1.5.4"
|
||||
"version": "==1.5.5"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
|
|
@ -54,18 +55,18 @@
|
|||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:2a39bd5e5f2d50ce9267d682cc92750f8771399665021f47e80f9c8d2fb812a6",
|
||||
"sha256:b4860f56bc585d3d1fde90d288da5eb4d1198401d72201dc3e25de8887b080e2"
|
||||
"sha256:1709ff5feb363fee7fcaa2330e659fcbc2b4c03a14f75a884ed682ee66011fc4",
|
||||
"sha256:80a761eff3b1cb0798d7e1a41b7c8e6d85c9647a8f7b6105335201a69404caa2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.17.0"
|
||||
"version": "==1.17.10"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:634b39ab0d55477cfbffb0e5dff31b7ab4bb171b04a0c69f8bcf65135f26ba94",
|
||||
"sha256:a608d6d644b852f3c154fc433eaae52febbebc7c474fa8f4d666797d0931770a"
|
||||
"sha256:8c84eac6daf38890714e005623083106d68e9b2088e62132fdbf7d2b1228ecbd",
|
||||
"sha256:a601ee5a4ae66832f328ca362b5404d22b75f1c181f6cc0934f3cfca749eb27d"
|
||||
],
|
||||
"version": "==1.20.0"
|
||||
"version": "==1.20.10"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
|
|
@ -189,10 +190,10 @@
|
|||
},
|
||||
"django-treebeard": {
|
||||
"hashes": [
|
||||
"sha256:214ae3ab331a7de11fb055a2015c201e34f3fa14255b667e1e07752231a7a398",
|
||||
"sha256:f50e4eea146f7af6702decf7ef198ac1eee1fb9bb4af2c5dba276c3c48f76623"
|
||||
"sha256:313fb61843e1ad025262014c382bc0c58fefc064cf5401421dcb5a2cfdacdc9d",
|
||||
"sha256:8085928debdd187e9919afc522ea40069bb9f090fa804c7ae9a324b0f62843c6"
|
||||
],
|
||||
"version": "==4.4"
|
||||
"version": "==4.5"
|
||||
},
|
||||
"djangorestframework": {
|
||||
"hashes": [
|
||||
|
|
@ -219,10 +220,10 @@
|
|||
},
|
||||
"faker": {
|
||||
"hashes": [
|
||||
"sha256:0783729c61501d52efea2967aff6e6fcb8370f0f6b5a558f2a81233642ae529a",
|
||||
"sha256:6b2995ffff6c2b02bc5daad96f8c24c021e5bd491d9d53d31bcbd66f348181d4"
|
||||
"sha256:3971803f32728314c54ba051139cd433fc93fde371e18d07a2cec960a7a2222a",
|
||||
"sha256:b27f9bc97490a11f14c1501cc25f1109cf68c75f11c6ef97714757a4298c33e5"
|
||||
],
|
||||
"version": "==5.8.0"
|
||||
"version": "==6.3.0"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
|
|
@ -245,11 +246,11 @@
|
|||
},
|
||||
"graphene-django": {
|
||||
"hashes": [
|
||||
"sha256:3afd81d47c8b702650e05cc1179fac1cfceae991d241bb164d51f28bed9ec95c",
|
||||
"sha256:760a18068feb5457e2ec00d2447c09b2fbac2a6b8c32cc8be2abce3752107ad3"
|
||||
"sha256:02671d195f0c09c8649acff2a8f4ad4f297d0f7d98ea6e6cdf034b81bab92880",
|
||||
"sha256:b78c9b05bc899016b9cc5bf13faa1f37fe1faa8c5407552c6ddd1a28f46fc31a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.2.0"
|
||||
"version": "==2.15.0"
|
||||
},
|
||||
"graphql-core": {
|
||||
"hashes": [
|
||||
|
|
@ -500,10 +501,10 @@
|
|||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:7e966747c18ececaec785699626b771c1ba8344c8d31759a1915d6b12fad6525",
|
||||
"sha256:c96b30925025a7635471dc083ffb6af0cc67482a00611bd81aeaeeeb7e5a5e12"
|
||||
"sha256:0fa02fa80363844a4ab4b8d6891f62dd0645ba672723130423ca4037b80c1974",
|
||||
"sha256:62c811e46bd09130fb11ab759012a4ae385ce4fb2073442d1898867a824183bd"
|
||||
],
|
||||
"version": "==3.0.14"
|
||||
"version": "==3.0.16"
|
||||
},
|
||||
"psycopg2": {
|
||||
"hashes": [
|
||||
|
|
@ -542,10 +543,10 @@
|
|||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435",
|
||||
"sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
|
||||
"sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0",
|
||||
"sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"
|
||||
],
|
||||
"version": "==2.7.4"
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
|
|
@ -641,10 +642,10 @@
|
|||
},
|
||||
"sendgrid": {
|
||||
"hashes": [
|
||||
"sha256:499a4910623c03e73cb27bd9ef7cadd0968eb2c811afd5c83cfb517f76163f65",
|
||||
"sha256:5a87682dba540b706683d4b4a3a605e11fbe24f340ecff5fd502bfb17dfa7ef8"
|
||||
"sha256:2eb1dcb1f7d8656eed4db586e428c2c86f347590b8511d7f92993882d0e4fab9",
|
||||
"sha256:e422c8263563ac7d664066d2f87b90bcb005b067eb7c33a9b1396442b2ed285b"
|
||||
],
|
||||
"version": "==6.5.0"
|
||||
"version": "==6.6.0"
|
||||
},
|
||||
"sentry-sdk": {
|
||||
"hashes": [
|
||||
|
|
@ -711,10 +712,10 @@
|
|||
},
|
||||
"unidecode": {
|
||||
"hashes": [
|
||||
"sha256:4c9d15d2f73eb0d2649a151c566901f80a030da1ccb0a2043352e1dbf647586b",
|
||||
"sha256:a039f89014245e0cad8858976293e23501accc9ff5a7bdbc739a14a2b7b85cdc"
|
||||
"sha256:12435ef2fc4cdfd9cf1035a1db7e98b6b047fe591892e81f34e94959591fad00",
|
||||
"sha256:8d73a97d387a956922344f6b74243c2c6771594659778744b2dbdaad8f6b727d"
|
||||
],
|
||||
"version": "==1.1.2"
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"unittest-xml-reporting": {
|
||||
"hashes": [
|
||||
|
|
@ -794,17 +795,18 @@
|
|||
},
|
||||
"autopep8": {
|
||||
"hashes": [
|
||||
"sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094"
|
||||
"sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea",
|
||||
"sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"
|
||||
],
|
||||
"version": "==1.5.4"
|
||||
"version": "==1.5.5"
|
||||
},
|
||||
"awscli": {
|
||||
"hashes": [
|
||||
"sha256:2bdc2ef330f9334dbeefb91c942061feb6a53646bd55c663e0056f8d0dc66fed",
|
||||
"sha256:5a28b63869261c5c2f4ee83f7c43d8ec9622f790d0daf73f91643dcf7148bcf8"
|
||||
"sha256:299161d80c226fea405a69fda44fa90cec3d5cf2e484021e9c323d6454246d20",
|
||||
"sha256:f04edb9f34308a84541ba125387bb9d7f4ae03c066b03d46af306e995c4c5e42"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.19.0"
|
||||
"version": "==1.19.10"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
|
|
@ -815,10 +817,10 @@
|
|||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:634b39ab0d55477cfbffb0e5dff31b7ab4bb171b04a0c69f8bcf65135f26ba94",
|
||||
"sha256:a608d6d644b852f3c154fc433eaae52febbebc7c474fa8f4d666797d0931770a"
|
||||
"sha256:8c84eac6daf38890714e005623083106d68e9b2088e62132fdbf7d2b1228ecbd",
|
||||
"sha256:a601ee5a4ae66832f328ca362b5404d22b75f1c181f6cc0934f3cfca749eb27d"
|
||||
],
|
||||
"version": "==1.20.0"
|
||||
"version": "==1.20.10"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
|
|
@ -1064,10 +1066,10 @@
|
|||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:7e966747c18ececaec785699626b771c1ba8344c8d31759a1915d6b12fad6525",
|
||||
"sha256:c96b30925025a7635471dc083ffb6af0cc67482a00611bd81aeaeeeb7e5a5e12"
|
||||
"sha256:0fa02fa80363844a4ab4b8d6891f62dd0645ba672723130423ca4037b80c1974",
|
||||
"sha256:62c811e46bd09130fb11ab759012a4ae385ce4fb2073442d1898867a824183bd"
|
||||
],
|
||||
"version": "==3.0.14"
|
||||
"version": "==3.0.16"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
|
|
@ -1103,10 +1105,10 @@
|
|||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435",
|
||||
"sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
|
||||
"sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0",
|
||||
"sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"
|
||||
],
|
||||
"version": "==2.7.4"
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"targets": {
|
||||
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
||||
}
|
||||
}],
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-vue-jsx",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -17,12 +17,12 @@ const topic = {
|
|||
return {
|
||||
node: module,
|
||||
__typename: 'ModuleNodeEdge'
|
||||
}
|
||||
};
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Cypress.Commands.add('checkHome', (n, skipHome) => {
|
||||
if (!skipHome) {
|
||||
|
|
@ -71,7 +71,7 @@ describe('Current Module', () => {
|
|||
'__typename': 'UserNode',
|
||||
'permissions': []
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
AssignmentsQuery: {
|
||||
assignments
|
||||
|
|
@ -79,7 +79,7 @@ describe('Current Module', () => {
|
|||
ModulesQuery: variables => {
|
||||
return {
|
||||
module: fullModules[variables.slug]
|
||||
}
|
||||
};
|
||||
},
|
||||
TopicsQuery: topics,
|
||||
Topic: topic,
|
||||
|
|
@ -95,7 +95,7 @@ describe('Current Module', () => {
|
|||
lastModule: moduleTeasers[variables.input.id],
|
||||
__typename: 'UpdateLastModulePayload'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -150,5 +150,5 @@ describe('Current Module', () => {
|
|||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Lerntipps');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Random');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(2).should('contain', 'Geld');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,20 @@
|
|||
<div
|
||||
:data-scrollto="chapter.id"
|
||||
class="chapter">
|
||||
<h3 :id="'chapter-' + index">{{ chapter.title }}</h3>
|
||||
<div
|
||||
:class="{'hideable-element--greyed-out': titleGreyedOut}"
|
||||
class="hideable-element"
|
||||
v-if="!titleHidden">
|
||||
<h3
|
||||
:id="'chapter-' + index"
|
||||
>{{ chapter.title }}</h3>
|
||||
</div>
|
||||
|
||||
<visibility-action
|
||||
:block="chapter"
|
||||
type="chapter-title"
|
||||
v-if="editModule"
|
||||
/>
|
||||
|
||||
<bookmark-actions
|
||||
:bookmarked="chapter.bookmark"
|
||||
|
|
@ -12,9 +25,22 @@
|
|||
@edit-note="editNote"
|
||||
@bookmark="bookmark(!chapter.bookmark)"
|
||||
/>
|
||||
<p class="chapter__description intro">
|
||||
<div
|
||||
:class="{'hideable-element--greyed-out': descriptionGreyedOut}"
|
||||
class="chapter__intro intro hideable-element"
|
||||
v-if="!descriptionHidden"
|
||||
>
|
||||
<visibility-action
|
||||
:block="chapter"
|
||||
:chapter="true"
|
||||
type="chapter-description"
|
||||
v-if="editModule"
|
||||
/>
|
||||
<p
|
||||
class="chapter__description">
|
||||
{{ chapter.description }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<add-content-button
|
||||
:parent="chapter"
|
||||
|
|
@ -29,32 +55,32 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ContentBlock from '@/components/ContentBlock';
|
||||
import AddContentButton from '@/components/AddContentButton';
|
||||
import BookmarkActions from '@/components/notes/BookmarkActions';
|
||||
import ContentBlock from '@/components/ContentBlock';
|
||||
import AddContentButton from '@/components/AddContentButton';
|
||||
import BookmarkActions from '@/components/notes/BookmarkActions';
|
||||
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
||||
|
||||
import {mapState} from 'vuex';
|
||||
import {isHidden} from '@/helpers/content-block';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
import {mapState} from 'vuex';
|
||||
import {hidden} from '@/helpers/visibility';
|
||||
import {CONTENT_TYPE, CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE} from '@/consts/types';
|
||||
|
||||
import UPDATE_CHAPTER_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateChapterBookmark.gql';
|
||||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
import UPDATE_CHAPTER_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateChapterBookmark.gql';
|
||||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
|
||||
export default {
|
||||
import me from '@/mixins/me';
|
||||
|
||||
export default {
|
||||
props: ['chapter', 'index'],
|
||||
|
||||
mixins: [me],
|
||||
|
||||
components: {
|
||||
BookmarkActions,
|
||||
VisibilityAction,
|
||||
ContentBlock,
|
||||
AddContentButton
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
me: {}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
filteredContentBlocks() {
|
||||
|
|
@ -64,18 +90,32 @@
|
|||
if (this.editModule) {
|
||||
return this.chapter.contentBlocks;
|
||||
}
|
||||
return this.chapter.contentBlocks.filter(contentBlock => !isHidden(contentBlock, this.schoolClass));
|
||||
},
|
||||
schoolClass() {
|
||||
return this.me.selectedClass;
|
||||
return this.chapter.contentBlocks.filter(contentBlock => !hidden({
|
||||
block: contentBlock,
|
||||
schoolClass: this.schoolClass,
|
||||
type: CONTENT_TYPE
|
||||
}));
|
||||
},
|
||||
note() {
|
||||
if (!(this.chapter && this.chapter.bookmark)) {
|
||||
return;
|
||||
}
|
||||
if (this.chapter && this.chapter.bookmark) {
|
||||
return this.chapter.bookmark.note;
|
||||
}
|
||||
},
|
||||
titleGreyedOut() {
|
||||
return this.textHidden(CHAPTER_TITLE_TYPE) && this.editModule;
|
||||
},
|
||||
// never hidden when editing the module
|
||||
titleHidden() {
|
||||
return this.textHidden(CHAPTER_TITLE_TYPE) && !this.editModule;
|
||||
},
|
||||
descriptionGreyedOut() {
|
||||
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && this.editModule;
|
||||
},
|
||||
// never hidden when editing the module
|
||||
descriptionHidden() {
|
||||
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && !this.editModule;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
bookmark(bookmarked) {
|
||||
|
|
@ -133,28 +173,36 @@
|
|||
editNote() {
|
||||
this.$store.dispatch('editNote', this.chapter.bookmark.note);
|
||||
},
|
||||
textHidden(type) {
|
||||
return hidden({
|
||||
block: this.chapter,
|
||||
schoolClass: this.schoolClass,
|
||||
type
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/_mixins.scss";
|
||||
@import "@/styles/_mixins.scss";
|
||||
|
||||
.chapter {
|
||||
.chapter {
|
||||
position: relative;
|
||||
|
||||
&__bookmark-actions {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
&__intro {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__description {
|
||||
@include lead-paragraph;
|
||||
|
||||
margin-bottom: $large-spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
:class="{'hideable-element--hidden': hidden}"
|
||||
:class="{'hideable-element--greyed-out': hidden}"
|
||||
class="content-block__container hideable-element">
|
||||
<div
|
||||
:class="specialClass"
|
||||
|
|
@ -60,11 +60,11 @@
|
|||
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
|
||||
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
|
||||
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
|
||||
import {mapState} from 'vuex';
|
||||
|
||||
import {isHidden} from '@/helpers/content-block';
|
||||
import me from '@/mixins/me';
|
||||
import {hidden} from '@/helpers/visibility';
|
||||
import {CONTENT_TYPE} from '@/consts/types';
|
||||
|
||||
const instruments = {
|
||||
base_communication: 'Sprache & Kommunikation',
|
||||
|
|
@ -77,6 +77,8 @@
|
|||
name: 'ContentBlock',
|
||||
props: ['contentBlock', 'parent'],
|
||||
|
||||
mixins: [me],
|
||||
|
||||
components: {
|
||||
ContentComponent,
|
||||
AddContentButton,
|
||||
|
|
@ -85,13 +87,6 @@
|
|||
UserWidget
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showVisibility: false,
|
||||
me: {}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
canEditModule() {
|
||||
|
|
@ -164,11 +159,12 @@
|
|||
contents: this.removeSingleContentListItem(newContent, startingIndex)
|
||||
});
|
||||
},
|
||||
schoolClass() {
|
||||
return this.me.selectedClass;
|
||||
},
|
||||
hidden() {
|
||||
return isHidden(this.contentBlock, this.schoolClass);
|
||||
return hidden({
|
||||
block: this.contentBlock,
|
||||
schoolClass: this.schoolClass,
|
||||
type: CONTENT_TYPE
|
||||
});
|
||||
},
|
||||
root() {
|
||||
// we need the root content block id, not the generated content block if inside a content list block
|
||||
|
|
@ -235,10 +231,6 @@
|
|||
}
|
||||
return [...content.slice(0, listIndex), ...content[listIndex].contents[0].value, ...content.slice(listIndex + 1)];
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -47,14 +47,11 @@
|
|||
import ObjectiveGroups from '@/components/objective-groups/ObjectiveGroups.vue';
|
||||
import Chapter from '@/components/Chapter.vue';
|
||||
|
||||
import UPDATE_OBJECTIVE_PROGRESS_MUTATION from '@/graphql/gql/mutations/updateObjectiveProgress.gql';
|
||||
import UPDATE_LAST_MODULE_MUTATION from '@/graphql/gql/mutations/updateLastModule.gql';
|
||||
import UPDATE_MODULE_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateModuleBookmark.gql';
|
||||
import OBJECTIVE_QUERY from '@/graphql/gql/objectiveQuery.gql';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
import MODULE_FRAGMENT from '@/graphql/gql/fragments/moduleParts.gql';
|
||||
|
||||
import {withoutOwnerFirst} from '@/helpers/sorting';
|
||||
import BookmarkActions from '@/components/notes/BookmarkActions';
|
||||
import meMixin from '@/mixins/me';
|
||||
|
||||
|
|
@ -82,18 +79,15 @@
|
|||
computed: {
|
||||
languageCommunicationObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'LANGUAGE_COMMUNICATION')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
.filter(group => group.title === 'LANGUAGE_COMMUNICATION') : [];
|
||||
},
|
||||
societyObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'SOCIETY')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
.filter(group => group.title === 'SOCIETY') : [];
|
||||
},
|
||||
interdisciplinaryObjectiveGroups() {
|
||||
return this.module.objectiveGroups ? this.module.objectiveGroups
|
||||
.filter(group => group.title === 'INTERDISCIPLINARY')
|
||||
.sort(withoutOwnerFirst) : [];
|
||||
.filter(group => group.title === 'INTERDISCIPLINARY') : [];
|
||||
},
|
||||
isStudent() {
|
||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
|
|
@ -145,28 +139,6 @@
|
|||
}
|
||||
});
|
||||
},
|
||||
updateObjectiveProgress(done, objectiveId) {
|
||||
this.$apollo.mutate({
|
||||
mutation: UPDATE_OBJECTIVE_PROGRESS_MUTATION,
|
||||
variables: {
|
||||
input: {
|
||||
id: objectiveId,
|
||||
done: done
|
||||
}
|
||||
},
|
||||
update(store, {data: {updateObjectiveProgress: {objective}}}) {
|
||||
if (objective) {
|
||||
const variables = {id: objectiveId};
|
||||
const query = OBJECTIVE_QUERY;
|
||||
const data = store.readQuery({query, variables});
|
||||
if (data && data.objective.objectiveProgress.edges.length > 0) {
|
||||
data.objective.objectiveProgress.edges[0].node.done = done;
|
||||
}
|
||||
store.writeQuery({query: OBJECTIVE_QUERY, data, variables});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
bookmark(bookmarked) {
|
||||
const slug = this.module.slug;
|
||||
this.$apollo.mutate({
|
||||
|
|
|
|||
|
|
@ -59,21 +59,20 @@
|
|||
|
||||
<script>
|
||||
import {moduleQuery} from '@/graphql/queries';
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
|
||||
import SubNavigationItem from '@/components/book-navigation/SubNavigationItem';
|
||||
import ToggleSolutionsForModule from '@/components/toggle-menu/ToggleSolutionsForModule';
|
||||
import ToggleEditing from '@/components/toggle-menu/ToggleEditing';
|
||||
import ChevronLeft from '@/components/icons/ChevronLeft';
|
||||
import me from '@/mixins/me';
|
||||
|
||||
export default {
|
||||
apollo: {
|
||||
module: moduleQuery,
|
||||
me: {
|
||||
query: ME_QUERY
|
||||
}
|
||||
},
|
||||
|
||||
mixins: [me],
|
||||
|
||||
components: {
|
||||
SubNavigationItem,
|
||||
ToggleSolutionsForModule,
|
||||
|
|
@ -87,9 +86,6 @@
|
|||
assignments: [],
|
||||
topic: {}
|
||||
},
|
||||
me: {
|
||||
permissions: []
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -103,9 +99,6 @@
|
|||
showResults() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
assignments() {
|
||||
if (!this.module.chapters) {
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<template>
|
||||
<li
|
||||
:class="{'hideable-element--hidden': hidden}"
|
||||
:class="{'hideable-element--greyed-out': hidden}"
|
||||
class="objective hideable-element"
|
||||
v-if="editModule || !hidden">
|
||||
<visibility-action
|
||||
:block="objective"
|
||||
:type="type"
|
||||
v-if="editModule"/>
|
||||
<div
|
||||
class="block-actions"
|
||||
|
|
@ -28,15 +29,19 @@
|
|||
import UserWidget from '@/components/UserWidget';
|
||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||
|
||||
import {mapState} from 'vuex';
|
||||
import {isHidden} from '@/helpers/content-block';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
import DELETE_OBJECTIVE_MUTATION from '@/graphql/gql/mutations/deleteObjective.gql';
|
||||
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
|
||||
import {hidden} from '@/helpers/visibility';
|
||||
import {OBJECTIVE_TYPE} from '@/consts/types';
|
||||
|
||||
import editModule from '@/mixins/edit-module';
|
||||
import me from '@/mixins/me';
|
||||
|
||||
export default {
|
||||
props: ['objective', 'schoolClass'],
|
||||
|
||||
mixins: [me, editModule],
|
||||
|
||||
components: {
|
||||
MoreOptionsWidget,
|
||||
VisibilityAction,
|
||||
|
|
@ -45,14 +50,17 @@
|
|||
|
||||
data() {
|
||||
return {
|
||||
me: {}
|
||||
type: OBJECTIVE_TYPE
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
hidden() {
|
||||
return isHidden(this.objective, this.schoolClass);
|
||||
return hidden({
|
||||
block: this.objective,
|
||||
schoolClass: this.schoolClass,
|
||||
type: this.type
|
||||
});
|
||||
},
|
||||
canEdit() {
|
||||
return this.objective.mine;
|
||||
|
|
@ -89,17 +97,14 @@
|
|||
// }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: meQuery
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.objective {
|
||||
min-height: 50px;
|
||||
|
||||
&__user-widget {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<div class="objective-group">
|
||||
<div
|
||||
:class="{'hideable-element--greyed-out': hidden}"
|
||||
class="objective-group hideable-element"
|
||||
v-if="editModule || !hidden">
|
||||
<visibility-action
|
||||
:block="group"
|
||||
:type="type"
|
||||
v-if="editModule"/>
|
||||
|
||||
<h4>{{ group.displayTitle }}</h4>
|
||||
|
||||
|
|
@ -7,7 +14,7 @@
|
|||
<objective
|
||||
:key="objective.id"
|
||||
:objective="objective"
|
||||
:school-class="currentFilter"
|
||||
:school-class="schoolClass"
|
||||
class="objective-group__objective"
|
||||
v-for="objective in group.objectives"/>
|
||||
</ul>
|
||||
|
|
@ -20,11 +27,14 @@
|
|||
|
||||
<script>
|
||||
import Objective from '@/components/objective-groups/Objective';
|
||||
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
import VisibilityAction from '@/components/visibility/VisibilityAction';
|
||||
import AddContentButton from '@/components/AddContentButton';
|
||||
|
||||
import {mapState} from 'vuex';
|
||||
import me from '@/mixins/me';
|
||||
import editModule from '@/mixins/edit-module';
|
||||
|
||||
import {OBJECTIVE_GROUP_TYPE} from '@/consts/types';
|
||||
import {hidden} from '@/helpers/visibility';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -34,24 +44,27 @@
|
|||
}
|
||||
},
|
||||
|
||||
mixins: [me, editModule],
|
||||
|
||||
components: {
|
||||
AddContentButton,
|
||||
Objective
|
||||
Objective,
|
||||
VisibilityAction
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: {
|
||||
query: ME_QUERY,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
type: OBJECTIVE_GROUP_TYPE
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
currentFilter() {
|
||||
return this.me.selectedClass;
|
||||
hidden() {
|
||||
return hidden({
|
||||
block: this.group,
|
||||
schoolClass: this.schoolClass,
|
||||
type: this.type
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
<script>
|
||||
import ObjectiveGroup from '@/components/objective-groups/ObjectiveGroup';
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
import {withoutOwnerFirst} from '@/helpers/sorting';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -47,19 +46,13 @@
|
|||
return this.groups;
|
||||
} else {
|
||||
// todo: maybe this can be done a bit more elegantly
|
||||
const groups = [...this.groups].sort(withoutOwnerFirst);
|
||||
let groups = this.groups;
|
||||
const objectives = groups.map(g => g.objectives).flat(); // get all objectives in one array
|
||||
const firstGroup = Object.assign({}, groups.shift(), {objectives});
|
||||
|
||||
return [firstGroup];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateObjectiveProgress(checked, id) {
|
||||
this.$emit('updateObjectiveProgress', checked, id);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<script>
|
||||
import Checkbox from '@/components/Checkbox';
|
||||
import {mapGetters, mapActions} from 'vuex';
|
||||
import {mapState, mapActions} from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
...mapState({
|
||||
checked: 'editModule',
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,79 +18,51 @@
|
|||
import EyeIcon from '@/components/icons/EyeIcon';
|
||||
import ClosedEyeIcon from '@/components/icons/ClosedEyeIcon';
|
||||
|
||||
import ME_QUERY from '@/graphql/gql/meQuery.gql';
|
||||
import CHANGE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/mutateContentBlock.gql';
|
||||
import UPDATE_OBJECTIVE_VISIBILITY_MUTATION from '@/graphql/gql/mutations/updateObjectiveVisibility.gql';
|
||||
import me from '@/mixins/me';
|
||||
|
||||
import {TYPES, CONTENT_TYPE} from '@/consts/types';
|
||||
import {createVisibilityMutation, hidden} from '@/helpers/visibility';
|
||||
|
||||
export default {
|
||||
props: ['block'],
|
||||
props: {
|
||||
block: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: CONTENT_TYPE,
|
||||
validator: value => {
|
||||
// value must be one of TYPES
|
||||
return TYPES.indexOf(value) !== -1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mixins: [me],
|
||||
|
||||
components: {
|
||||
EyeIcon,
|
||||
ClosedEyeIcon
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showVisibility: false,
|
||||
me: {
|
||||
permissions: []
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
isContentBlock() {
|
||||
return this.block.__typename === 'ContentBlockNode';
|
||||
},
|
||||
schoolClass() {
|
||||
return this.me.selectedClass;
|
||||
},
|
||||
hidden() {
|
||||
// is this content block / objective group user created?
|
||||
return this.block.userCreated
|
||||
// if so, is visibility not explicitly set for this school class?
|
||||
? this.block.visibleFor.findIndex(el => el.id === this.schoolClass.id) === -1
|
||||
// otherwise, is it explicitly hidden for this school class?
|
||||
: this.block.hiddenFor.findIndex(el => el.id === this.schoolClass.id) > -1;
|
||||
return hidden({type: this.type, block: this.block, schoolClass: this.schoolClass});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleVisibility() {
|
||||
let hidden = !this.hidden;
|
||||
let schoolClassId = this.schoolClass.id;
|
||||
const hidden = !this.hidden;
|
||||
const schoolClassId = this.schoolClass.id;
|
||||
|
||||
const visibility = [{
|
||||
schoolClassId,
|
||||
hidden
|
||||
}];
|
||||
|
||||
let mutation, variables;
|
||||
const id = this.block.id;
|
||||
|
||||
if (this.isContentBlock) {
|
||||
mutation = CHANGE_CONTENT_BLOCK_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
contentBlock: {
|
||||
visibility
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mutation = UPDATE_OBJECTIVE_VISIBILITY_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
visibility
|
||||
}
|
||||
};
|
||||
}
|
||||
const {mutation, variables} = createVisibilityMutation(this.type, this.block.id, visibility);
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation,
|
||||
|
|
@ -98,12 +70,6 @@
|
|||
});
|
||||
},
|
||||
},
|
||||
|
||||
apollo: {
|
||||
me: {
|
||||
query: ME_QUERY,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
export const CONTENT_TYPE = 'content';
|
||||
export const OBJECTIVE_TYPE = 'objective';
|
||||
export const OBJECTIVE_GROUP_TYPE = 'objective-group';
|
||||
export const CHAPTER_TITLE_TYPE = 'chapter-title';
|
||||
export const CHAPTER_DESCRIPTION_TYPE = 'chapter-description';
|
||||
export const TYPES = [
|
||||
CONTENT_TYPE,
|
||||
OBJECTIVE_TYPE,
|
||||
OBJECTIVE_GROUP_TYPE,
|
||||
CHAPTER_TITLE_TYPE,
|
||||
CHAPTER_DESCRIPTION_TYPE
|
||||
];
|
||||
|
|
@ -16,4 +16,20 @@ fragment ChapterParts on ChapterNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
titleHiddenFor {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
descriptionHiddenFor {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@ fragment ObjectiveGroupParts on ObjectiveGroupNode {
|
|||
id
|
||||
title
|
||||
displayTitle
|
||||
mine
|
||||
owner {
|
||||
id
|
||||
}
|
||||
hiddenFor {
|
||||
edges {
|
||||
node {
|
||||
|
|
@ -14,12 +10,4 @@ fragment ObjectiveGroupParts on ObjectiveGroupNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
visibleFor {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,4 @@ fragment ObjectiveParts on ObjectiveNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
objectiveProgress {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
done
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,5 @@ mutation MutateContentBlock($input: MutateContentBlockInput!) {
|
|||
contentBlock {
|
||||
...ContentBlockParts
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/chapterParts.gql"
|
||||
mutation UpdateChapterVisibility($input: UpdateChapterVisibilityInput!) {
|
||||
updateChapterVisibility(input: $input) {
|
||||
chapter {
|
||||
...ChapterParts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/objectiveGroupParts.gql"
|
||||
mutation UpdateObjectiveGroupVisibility($input: UpdateObjectiveGroupVisibilityInput!) {
|
||||
updateObjectiveGroupVisibility(input: $input) {
|
||||
objectiveGroup {
|
||||
...ObjectiveGroupParts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,17 +1,3 @@
|
|||
export function setUserBlockType(isAssignment) {
|
||||
return isAssignment ? 'TASK' : 'NORMAL';
|
||||
}
|
||||
|
||||
export const isHidden = (contentBlock, schoolClass) => {
|
||||
if (!contentBlock.id || !contentBlock.visibleFor || !contentBlock.hiddenFor) {
|
||||
return false;
|
||||
}
|
||||
if (contentBlock.userCreated) {
|
||||
if (schoolClass.id === '') {
|
||||
return false;
|
||||
}
|
||||
return !contentBlock.visibleFor.map(entry => entry.id).includes(schoolClass.id);
|
||||
} else {
|
||||
return contentBlock.hiddenFor.map(entry => entry.id).includes(schoolClass.id);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
import {
|
||||
CHAPTER_DESCRIPTION_TYPE,
|
||||
CHAPTER_TITLE_TYPE,
|
||||
CONTENT_TYPE,
|
||||
OBJECTIVE_GROUP_TYPE,
|
||||
OBJECTIVE_TYPE
|
||||
} from '@/consts/types';
|
||||
import CHANGE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/mutateContentBlock.gql';
|
||||
import UPDATE_OBJECTIVE_VISIBILITY_MUTATION from '@/graphql/gql/mutations/updateObjectiveVisibility.gql';
|
||||
import UPDATE_OBJECTIVE_GROUP_VISIBILITY_MUTATION from '@/graphql/gql/mutations/updateObjectiveGroupVisibility.gql';
|
||||
import UPDATE_CHAPTER_VISIBILITY_MUTATION from '@/graphql/gql/mutations/updateChapterVisibility.gql';
|
||||
|
||||
export const createVisibilityMutation = (type, id, visibility) => {
|
||||
let mutation, variables;
|
||||
switch (type) {
|
||||
case CONTENT_TYPE:
|
||||
mutation = CHANGE_CONTENT_BLOCK_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
contentBlock: {
|
||||
visibility
|
||||
}
|
||||
}
|
||||
};
|
||||
break;
|
||||
case OBJECTIVE_TYPE:
|
||||
mutation = UPDATE_OBJECTIVE_VISIBILITY_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
visibility
|
||||
}
|
||||
};
|
||||
break;
|
||||
case OBJECTIVE_GROUP_TYPE:
|
||||
mutation = UPDATE_OBJECTIVE_GROUP_VISIBILITY_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
visibility
|
||||
}
|
||||
};
|
||||
break;
|
||||
case CHAPTER_TITLE_TYPE:
|
||||
case CHAPTER_DESCRIPTION_TYPE:
|
||||
mutation = UPDATE_CHAPTER_VISIBILITY_MUTATION;
|
||||
variables = {
|
||||
input: {
|
||||
id,
|
||||
type,
|
||||
visibility
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
mutation,
|
||||
variables
|
||||
};
|
||||
};
|
||||
|
||||
const containsClass = (arr = [], schoolClass) => arr.map(entry => entry.id).includes(schoolClass.id);
|
||||
|
||||
export const hidden = ({
|
||||
type,
|
||||
block: {
|
||||
userCreated,
|
||||
visibleFor,
|
||||
hiddenFor,
|
||||
titleHiddenFor,
|
||||
descriptionHiddenFor
|
||||
},
|
||||
schoolClass
|
||||
}) => {
|
||||
switch (type) {
|
||||
case CONTENT_TYPE:
|
||||
case OBJECTIVE_TYPE:
|
||||
// is this content block / objective group user created?
|
||||
return userCreated
|
||||
// if so, is visibility not explicitly set for this school class?
|
||||
? !containsClass(visibleFor, schoolClass)
|
||||
// otherwise, is it explicitly hidden for this school class?
|
||||
: containsClass(hiddenFor, schoolClass);
|
||||
case OBJECTIVE_GROUP_TYPE:
|
||||
return containsClass(hiddenFor, schoolClass);
|
||||
case CHAPTER_TITLE_TYPE:
|
||||
return containsClass(titleHiddenFor, schoolClass);
|
||||
case CHAPTER_DESCRIPTION_TYPE:
|
||||
return containsClass(descriptionHiddenFor, schoolClass);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import {mapState} from 'vuex';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
}
|
||||
};
|
||||
|
|
@ -26,7 +26,13 @@ export default {
|
|||
};
|
||||
}
|
||||
return '/book/topic/berufliche-grundbildung';
|
||||
}
|
||||
},
|
||||
schoolClass() {
|
||||
return this.me.selectedClass;
|
||||
},
|
||||
canManageContent() {
|
||||
return this.me.permissions.includes('users.can_manage_school_class_content');
|
||||
},
|
||||
},
|
||||
|
||||
apollo: {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters, mapActions} from 'vuex';
|
||||
import {mapState, mapActions} from 'vuex';
|
||||
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
|
||||
|
||||
import SCROLL_POSITION from '@/graphql/gql/local/scrollPosition.gql';
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
...mapState({
|
||||
editModule: 'editModule'
|
||||
}),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.hideable-element {
|
||||
position: relative;
|
||||
|
||||
&--hidden {
|
||||
&--greyed-out {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from api import graphene_wagtail # Keep this import exactly here, it's necessar
|
|||
from assignments.schema.mutations import AssignmentMutations
|
||||
from assignments.schema.queries import AssignmentsQuery, StudentSubmissionQuery
|
||||
from basicknowledge.queries import BasicKnowledgeQuery
|
||||
from books.schema.mutations.main import BookMutations
|
||||
from books.schema.mutations import BookMutations
|
||||
from books.schema.queries import BookQuery
|
||||
from core.schema.mutations.coupon import CouponMutations
|
||||
from core.schema.mutations.main import CoreMutations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.2.17 on 2021-02-18 13:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0025_auto_20210126_1343'),
|
||||
('books', '0023_auto_20200707_1501'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chapter',
|
||||
name='description_hidden_for',
|
||||
field=models.ManyToManyField(related_name='hidden_chapter_descriptions', to='users.SchoolClass'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='chapter',
|
||||
name='title_hidden_for',
|
||||
field=models.ManyToManyField(related_name='hidden_chapter_titles', to='users.SchoolClass'),
|
||||
),
|
||||
]
|
||||
|
|
@ -4,6 +4,7 @@ from django.db import models
|
|||
from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList
|
||||
|
||||
from core.wagtail_utils import StrictHierarchyPage
|
||||
from users.models import SchoolClass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -34,3 +35,5 @@ class Chapter(StrictHierarchyPage):
|
|||
parent_page_types = ['books.Module']
|
||||
subpage_types = ['books.ContentBlock']
|
||||
|
||||
title_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_titles')
|
||||
description_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_descriptions')
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Iterativ GmbH
|
||||
# http://www.iterativ.ch/
|
||||
#
|
||||
# Copyright (c) 2018 Iterativ GmbH. All rights reserved.
|
||||
#
|
||||
# Created on 25.09.18
|
||||
# @author: Ramon Wenger <ramon.wenger@iterativ.ch>
|
||||
from books.schema.mutations.chapter import UpdateChapterVisibility
|
||||
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
|
||||
from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, UpdateLastTopic
|
||||
|
||||
|
||||
class BookMutations(object):
|
||||
mutate_content_block = MutateContentBlock.Field()
|
||||
add_content_block = AddContentBlock.Field()
|
||||
delete_content_block = DeleteContentBlock.Field()
|
||||
update_solution_visibility = UpdateSolutionVisibility.Field()
|
||||
update_last_module = UpdateLastModule.Field()
|
||||
update_last_topic = UpdateLastTopic.Field()
|
||||
update_chapter_visibility = UpdateChapterVisibility.Field()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import graphene
|
||||
from graphene import relay
|
||||
|
||||
from api.utils import get_object
|
||||
from books.models import Chapter
|
||||
from books.schema.inputs import UserGroupBlockVisibility
|
||||
from books.schema.queries import ChapterNode
|
||||
from users.models import SchoolClass
|
||||
|
||||
|
||||
class UpdateChapterVisibility(relay.ClientIDMutation):
|
||||
class Input:
|
||||
id = graphene.ID(required=True)
|
||||
visibility = graphene.List(UserGroupBlockVisibility)
|
||||
type = graphene.String(required=True)
|
||||
|
||||
chapter = graphene.Field(ChapterNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
id_param = kwargs['id']
|
||||
visibility_list = kwargs.get('visibility', None)
|
||||
mutation_type = kwargs['type']
|
||||
|
||||
chapter = get_object(Chapter, id_param)
|
||||
|
||||
if visibility_list is not None:
|
||||
for v in visibility_list:
|
||||
school_class = get_object(SchoolClass, v.school_class_id)
|
||||
if v.hidden:
|
||||
if mutation_type == 'chapter-title':
|
||||
chapter.title_hidden_for.add(school_class)
|
||||
else:
|
||||
chapter.description_hidden_for.add(school_class)
|
||||
else:
|
||||
if mutation_type == 'chapter-title':
|
||||
chapter.title_hidden_for.remove(school_class)
|
||||
else:
|
||||
chapter.description_hidden_for.remove(school_class)
|
||||
|
||||
chapter.save()
|
||||
|
||||
return cls(chapter=chapter)
|
||||
|
|
@ -10,7 +10,6 @@ from books.models import ContentBlock, Chapter, SchoolClass
|
|||
from books.schema.inputs import ContentBlockInput
|
||||
from books.schema.queries import ContentBlockNode
|
||||
from core.utils import set_hidden_for, set_visible_for
|
||||
from notes.models import ContentBlockBookmark
|
||||
from .utils import handle_content_block, set_user_defined_block_type
|
||||
|
||||
|
||||
|
|
@ -19,7 +18,6 @@ class MutateContentBlock(relay.ClientIDMutation):
|
|||
id = graphene.ID(required=True)
|
||||
content_block = graphene.Argument(ContentBlockInput)
|
||||
|
||||
errors = graphene.List(graphene.String)
|
||||
content_block = graphene.Field(ContentBlockNode)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -52,17 +50,16 @@ class MutateContentBlock(relay.ClientIDMutation):
|
|||
if contents is not None:
|
||||
content_block.contents = json.dumps([handle_content_block(c, info.context, module) for c in contents])
|
||||
|
||||
|
||||
content_block.save()
|
||||
|
||||
return cls(content_block=content_block)
|
||||
|
||||
except ValidationError as e:
|
||||
errors = get_errors(e)
|
||||
raise errors
|
||||
except Exception as e:
|
||||
errors = ['Error: {}'.format(e)]
|
||||
|
||||
return cls(content_block=None, errors=errors)
|
||||
raise errors
|
||||
|
||||
|
||||
class AddContentBlock(relay.ClientIDMutation):
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
|
||||
from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, UpdateLastTopic
|
||||
|
||||
|
||||
class BookMutations(object):
|
||||
mutate_content_block = MutateContentBlock.Field()
|
||||
add_content_block = AddContentBlock.Field()
|
||||
delete_content_block = DeleteContentBlock.Field()
|
||||
update_solution_visibility = UpdateSolutionVisibility.Field()
|
||||
update_last_module = UpdateLastModule.Field()
|
||||
update_last_topic = UpdateLastTopic.Field()
|
||||
|
|
@ -77,7 +77,7 @@ class ChapterNode(DjangoObjectType):
|
|||
class Meta:
|
||||
model = Chapter
|
||||
only_fields = [
|
||||
'slug', 'title', 'description',
|
||||
'slug', 'title', 'description', 'title_hidden_for', 'description_hidden_for'
|
||||
]
|
||||
filter_fields = [
|
||||
'slug', 'title',
|
||||
|
|
@ -183,9 +183,7 @@ class ModuleNode(DjangoObjectType):
|
|||
|
||||
def resolve_objective_groups(self, root, **kwargs):
|
||||
return self.objective_groups.all() \
|
||||
.prefetch_related('hidden_for') \
|
||||
.prefetch_related('visible_for') \
|
||||
.prefetch_related('objectives__objective_progress')
|
||||
.prefetch_related('hidden_for')
|
||||
|
||||
|
||||
class RecentModuleNode(DjangoObjectType):
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class Command(BaseCommand):
|
|||
for objective_group_idx, objective_group_entry in enumerate(objective_group_data):
|
||||
factory_params = self.filter_data(objective_group_entry, 'objectives')
|
||||
objective_group = ObjectiveGroupFactory.create(module=module,
|
||||
owner=None,
|
||||
|
||||
**factory_params)
|
||||
|
||||
default_objectives = [{} for i in range(0, 4)]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from django.contrib import admin
|
||||
from wagtail.core.models import Page
|
||||
|
||||
from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
||||
from objectives.models import ObjectiveGroup, Objective
|
||||
from books.models import Topic
|
||||
|
||||
|
||||
@admin.register(ObjectiveGroup)
|
||||
class ObjectiveGroupAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'module', 'owner')
|
||||
list_filter = ('title', 'module', 'owner')
|
||||
list_display = ('title', 'module')
|
||||
list_filter = ('title', 'module')
|
||||
|
||||
|
||||
@admin.register(Objective)
|
||||
|
|
@ -36,9 +37,3 @@ class ObjectiveAdmin(admin.ModelAdmin):
|
|||
return topic.title
|
||||
|
||||
get_topic.short_description = 'Thema'
|
||||
|
||||
|
||||
@admin.register(ObjectiveProgressStatus)
|
||||
class ObjectiveProgressStatusAdmin(admin.ModelAdmin):
|
||||
list_display = ('objective', 'user', 'done')
|
||||
list_filter = ('objective', 'user', 'done')
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class ObjectiveGroupFactory(factory.django.DjangoModelFactory):
|
|||
title = factory.Iterator([ObjectiveGroup.LANGUAGE_COMMUNICATION, ObjectiveGroup.SOCIETY])
|
||||
|
||||
module = factory.SubFactory(ModuleFactory)
|
||||
owner = factory.Iterator(get_user_model().objects.all())
|
||||
|
||||
|
||||
class ObjectiveFactory(factory.django.DjangoModelFactory):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 2.2.18 on 2021-02-22 15:08
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('objectives', '0012_auto_20210210_1109'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='objectivegroup',
|
||||
name='owner',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='objectivegroup',
|
||||
name='visible_for',
|
||||
),
|
||||
]
|
||||
|
|
@ -23,11 +23,8 @@ class ObjectiveGroup(models.Model):
|
|||
|
||||
title = models.CharField('title', blank=True, null=False, max_length=255, choices=TITLE_CHOICES, default=LANGUAGE_COMMUNICATION)
|
||||
module = models.ForeignKey(Module, blank=False, null=False, on_delete=models.CASCADE, related_name='objective_groups')
|
||||
# a user can define her own objectives, hence this optional param
|
||||
owner = models.ForeignKey(get_user_model(), blank=True, null=True, on_delete=models.PROTECT)
|
||||
|
||||
hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_objective_groups', blank=True)
|
||||
visible_for = models.ManyToManyField(SchoolClass, related_name='visible_objective_groups', blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return '{} - {}'.format(self.module, self.title)
|
||||
|
|
@ -49,18 +46,3 @@ class Objective(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return 'Objective {}-{}'.format(self.id, self.text)
|
||||
|
||||
|
||||
# todo: delete
|
||||
class ObjectiveProgressStatus(models.Model):
|
||||
class Meta:
|
||||
verbose_name = 'Lernzielstatus'
|
||||
verbose_name_plural = 'Lernzielstatus'
|
||||
|
||||
done = models.BooleanField('Lernziel erledigt?', default=False)
|
||||
objective = models.ForeignKey(Objective, blank=False, null=False, on_delete=models.CASCADE,
|
||||
related_name='objective_progress')
|
||||
user = models.ForeignKey(get_user_model(), blank=True, null=True, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return 'Lernzielstatus {}-{}'.format(self.objective, self.done)
|
||||
|
|
|
|||
|
|
@ -6,34 +6,10 @@ from api.utils import get_object
|
|||
from books.schema.inputs import UserGroupBlockVisibility
|
||||
from core.utils import set_visible_for, set_hidden_for
|
||||
from objectives.inputs import AddObjectiveArgument
|
||||
from objectives.models import ObjectiveProgressStatus, Objective, ObjectiveGroup
|
||||
from objectives.models import Objective, ObjectiveGroup
|
||||
from objectives.schema import ObjectiveNode, ObjectiveGroupNode
|
||||
|
||||
|
||||
class UpdateObjectiveProgress(relay.ClientIDMutation):
|
||||
class Input:
|
||||
id = graphene.ID(required=True, description="The ID of the objective")
|
||||
done = graphene.Boolean(required=True)
|
||||
|
||||
errors = graphene.List(graphene.String)
|
||||
objective = graphene.Field(ObjectiveNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
objective_id = kwargs.get('id')
|
||||
done = kwargs.get('done')
|
||||
objective = get_object(Objective, objective_id) # info.context.user = django user
|
||||
objective_progress, created = ObjectiveProgressStatus.objects.get_or_create(
|
||||
objective=objective,
|
||||
user=info.context.user
|
||||
)
|
||||
|
||||
objective_progress.done = done
|
||||
objective_progress.save()
|
||||
|
||||
return cls(objective=objective)
|
||||
|
||||
|
||||
class UpdateObjectiveVisibility(relay.ClientIDMutation):
|
||||
class Input:
|
||||
id = graphene.ID(required=True, description='The ID of the objective')
|
||||
|
|
@ -58,6 +34,26 @@ class UpdateObjectiveVisibility(relay.ClientIDMutation):
|
|||
return cls(objective=objective)
|
||||
|
||||
|
||||
class UpdateObjectiveGroupVisibility(relay.ClientIDMutation):
|
||||
class Input:
|
||||
id = graphene.ID(required=True, description='The ID of the objective group')
|
||||
visibility = graphene.List(UserGroupBlockVisibility)
|
||||
|
||||
objective_group = graphene.Field(ObjectiveGroupNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
objective_group_id = kwargs.get('id')
|
||||
visibility_list = kwargs.get('visibility')
|
||||
objective_group = get_object(ObjectiveGroup, objective_group_id) # info.context.user = django user
|
||||
|
||||
if visibility_list is not None:
|
||||
set_hidden_for(objective_group, visibility_list)
|
||||
objective_group.save()
|
||||
|
||||
return cls(objective_group=objective_group)
|
||||
|
||||
|
||||
class AddObjective(relay.ClientIDMutation):
|
||||
class Input:
|
||||
objective = graphene.Argument(AddObjectiveArgument)
|
||||
|
|
@ -104,7 +100,7 @@ class DeleteObjective(relay.ClientIDMutation):
|
|||
|
||||
|
||||
class ObjectiveMutations:
|
||||
update_objective_progress = UpdateObjectiveProgress.Field()
|
||||
update_objective_visibility = UpdateObjectiveVisibility.Field()
|
||||
update_objective_group_visibility = UpdateObjectiveGroupVisibility.Field()
|
||||
add_objective = AddObjective.Field()
|
||||
delete_objective = DeleteObjective.Field()
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ from graphene import relay
|
|||
from graphene_django import DjangoObjectType
|
||||
from graphene_django.filter import DjangoFilterConnectionField
|
||||
|
||||
from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
||||
from objectives.models import ObjectiveGroup, Objective
|
||||
|
||||
|
||||
class ObjectiveGroupNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
display_title = graphene.String()
|
||||
mine = graphene.Boolean()
|
||||
|
||||
class Meta:
|
||||
model = ObjectiveGroup
|
||||
|
|
@ -23,9 +22,6 @@ class ObjectiveGroupNode(DjangoObjectType):
|
|||
def resolve_display_title(self, *args, **kwargs):
|
||||
return self.get_title_display()
|
||||
|
||||
def resolve_mine(self, info, **kwargs):
|
||||
return self.owner is not None and self.owner.pk == info.context.user.pk
|
||||
|
||||
def resolve_objectives(self, info, **kwargs):
|
||||
user = info.context.user
|
||||
school_classes = user.school_classes.values_list('pk')
|
||||
|
|
@ -59,17 +55,6 @@ class ObjectiveNode(DjangoObjectType):
|
|||
def resolve_mine(self, info, **kwargs):
|
||||
return self.owner is not None and self.owner.pk == info.context.user.pk
|
||||
|
||||
class ObjectiveProgressStatusNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
||||
class Meta:
|
||||
model = ObjectiveProgressStatus
|
||||
filter_fields = ['objective__text', 'user__username', 'done']
|
||||
interfaces = (relay.Node,)
|
||||
|
||||
def resolve_pk(self, *args, **kwargs):
|
||||
return self.id
|
||||
|
||||
|
||||
class ObjectivesQuery(object):
|
||||
objective_group = relay.Node.Field(ObjectiveGroupNode)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class ObjectiveOrderTestCase(TestCase):
|
|||
|
||||
self.user = user = User.objects.get(username='teacher')
|
||||
|
||||
self.objective_group = ObjectiveGroupFactory(owner=None)
|
||||
self.objective_group = ObjectiveGroupFactory()
|
||||
|
||||
|
||||
request = RequestFactory().get('/')
|
||||
|
|
@ -29,10 +29,10 @@ class ObjectiveOrderTestCase(TestCase):
|
|||
|
||||
self.client = Client(schema=schema, context_value=request)
|
||||
|
||||
Objective.objects.create(owner=None, text='first', group=self.objective_group, order=0)
|
||||
Objective.objects.create(owner=None, text='second', group=self.objective_group, order=1)
|
||||
Objective.objects.create(owner=None, text='third', group=self.objective_group)
|
||||
Objective.objects.create(owner=user, text='fourth', group=self.objective_group)
|
||||
Objective.objects.create(text='first', group=self.objective_group, order=0)
|
||||
Objective.objects.create(text='second', group=self.objective_group, order=1)
|
||||
Objective.objects.create(text='third', group=self.objective_group)
|
||||
Objective.objects.create(text='fourth', group=self.objective_group)
|
||||
|
||||
def test_objective_order(self):
|
||||
query = """
|
||||
|
|
|
|||
Loading…
Reference in New Issue