Add visibility actions to objective group
This commit is contained in:
parent
6bab4320ec
commit
e10481ce49
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
v-if="editModule || !hidden">
|
||||
<visibility-action
|
||||
:block="objective"
|
||||
:type="type"
|
||||
v-if="editModule"/>
|
||||
<div
|
||||
class="block-actions"
|
||||
|
|
@ -28,16 +29,19 @@
|
|||
import UserWidget from '@/components/UserWidget';
|
||||
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
|
||||
|
||||
import {mapState} from 'vuex';
|
||||
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,
|
||||
|
|
@ -46,17 +50,16 @@
|
|||
|
||||
data() {
|
||||
return {
|
||||
me: {}
|
||||
type: OBJECTIVE_TYPE
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
hidden() {
|
||||
return hidden({
|
||||
block: this.objective,
|
||||
schoolClass: this.schoolClass,
|
||||
type: OBJECTIVE_TYPE
|
||||
type: this.type
|
||||
});
|
||||
},
|
||||
canEdit() {
|
||||
|
|
@ -94,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,12 +27,14 @@
|
|||
|
||||
<script>
|
||||
import Objective from '@/components/objective-groups/Objective';
|
||||
|
||||
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: {
|
||||
|
|
@ -35,17 +44,27 @@
|
|||
}
|
||||
},
|
||||
|
||||
mixins: [me],
|
||||
mixins: [me, editModule],
|
||||
|
||||
components: {
|
||||
AddContentButton,
|
||||
Objective
|
||||
Objective,
|
||||
VisibilityAction
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
type: OBJECTIVE_GROUP_TYPE
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -1,5 +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, CHAPTER_TITLE_TYPE, CHAPTER_DESCRIPTION_TYPE];
|
||||
export const TYPES = [
|
||||
CONTENT_TYPE,
|
||||
OBJECTIVE_TYPE,
|
||||
OBJECTIVE_GROUP_TYPE,
|
||||
CHAPTER_TITLE_TYPE,
|
||||
CHAPTER_DESCRIPTION_TYPE
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/objectiveGroupParts.gql"
|
||||
mutation UpdateObjectiveGroupVisibility($input: UpdateObjectiveGroupVisibilityInput!) {
|
||||
updateObjectiveGroupVisibility(input: $input) {
|
||||
objectiveGroup {
|
||||
...ObjectiveGroupParts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,13 @@
|
|||
import {CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE, CONTENT_TYPE, OBJECTIVE_TYPE} from '@/consts/types';
|
||||
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) => {
|
||||
|
|
@ -26,6 +33,15 @@ export const createVisibilityMutation = (type, 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;
|
||||
|
|
@ -67,6 +83,8 @@ export const hidden = ({
|
|||
? !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:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
import {mapState} from 'vuex';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState(['editModule']),
|
||||
}
|
||||
};
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ from wagtail.core.models import Page
|
|||
from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
||||
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)
|
||||
class ObjectiveAdmin(admin.ModelAdmin):
|
||||
list_display = ('text', 'get_topic', 'group', 'order', 'owner')
|
||||
list_display = ('text', 'get_topic', 'group', 'order', 'owner')
|
||||
list_filter = ('group', 'owner')
|
||||
|
||||
def get_topic(self, obj):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,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 +124,9 @@ class DeleteObjective(relay.ClientIDMutation):
|
|||
|
||||
|
||||
class ObjectiveMutations:
|
||||
# todo: remove?
|
||||
update_objective_progress = UpdateObjectiveProgress.Field()
|
||||
update_objective_visibility = UpdateObjectiveVisibility.Field()
|
||||
update_objective_group_visibility = UpdateObjectiveGroupVisibility.Field()
|
||||
add_objective = AddObjective.Field()
|
||||
delete_objective = DeleteObjective.Field()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ from objectives.models import ObjectiveGroup, Objective, ObjectiveProgressStatus
|
|||
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')
|
||||
|
|
|
|||
Loading…
Reference in New Issue