Add mutations and query, fix popover
This commit is contained in:
parent
90b919c839
commit
91967ceb21
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="class-selection__selected-class" @click="showPopover = !showPopover">{{currentClassSelection}}
|
<div class="class-selection__selected-class" @click="showPopover = !showPopover">{{currentClassSelection}}
|
||||||
</div>
|
</div>
|
||||||
<widget-popover v-if="showPopover"
|
<widget-popover v-if="showPopover"
|
||||||
@hide-me="showPopover == false"
|
@hide-me="showPopover = false"
|
||||||
class="user-widget__popover">
|
class="user-widget__popover">
|
||||||
<li class="popover-links__link" v-for="schoolClass in schoolClasses"
|
<li class="popover-links__link" v-for="schoolClass in schoolClasses"
|
||||||
:key="schoolClass.id"
|
:key="schoolClass.id"
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
<!--span class="user-widget__name">{{firstName}} {{lastName}}</span>
|
<!--span class="user-widget__name">{{firstName}} {{lastName}}</span>
|
||||||
<span class="user-widget__date" v-if="date">{{date}}</span-->
|
<span class="user-widget__date" v-if="date">{{date}}</span-->
|
||||||
<widget-popover v-if="showPopover"
|
<widget-popover v-if="showPopover"
|
||||||
@hide-me="showPopover == false"
|
@hide-me="showPopover = false"
|
||||||
class="user-widget__popover">
|
class="user-widget__popover">
|
||||||
<li class="popover-links__link">{{firstName}} {{lastName}}</li>
|
<li class="popover-links__link">{{firstName}} {{lastName}}</li>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<router-link to="/me/activity">Aktivität</router-link>
|
<router-link to="/me/activity">Aktivität</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<router-link to="/me/profile" >Profil</router-link>
|
<router-link to="/me/profile">Profil</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="popover-links__link">
|
<li class="popover-links__link">
|
||||||
<router-link to="/me/myclasses">Klassenliste</router-link>
|
<router-link to="/me/myclasses">Klassenliste</router-link>
|
||||||
|
|
@ -68,8 +68,10 @@
|
||||||
.user-widget {
|
.user-widget {
|
||||||
color: $color-silver-dark;
|
color: $color-silver-dark;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-right: $medium-spacing;
|
||||||
|
|
||||||
&__popover {
|
&__popover {
|
||||||
top: 40px;
|
top: 40px;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import graphene
|
import graphene
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
from graphql_relay import to_global_id, from_global_id
|
from graphql_relay import to_global_id
|
||||||
|
|
||||||
from api.utils import get_object
|
from api.utils import get_object
|
||||||
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument
|
from rooms.inputs import UpdateRoomArgument, AddRoomArgument, AddRoomEntryArgument, UpdateRoomEntryArgument
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 2.0.6 on 2019-07-24 20:25
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0006_auto_20190703_0959'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserSetting',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('selected_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='users.SchoolClass')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_setting', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import AbstractUser, Permission
|
from django.contrib.auth.models import AbstractUser, Permission
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
@ -38,6 +37,18 @@ class User(AbstractUser):
|
||||||
def users_in_same_school_class(self):
|
def users_in_same_school_class(self):
|
||||||
return User.objects.filter(school_classes__users=self.pk)
|
return User.objects.filter(school_classes__users=self.pk)
|
||||||
|
|
||||||
|
def selected_class(self):
|
||||||
|
try:
|
||||||
|
settings = UserSetting.objects.get(user=self)
|
||||||
|
return settings.selected_class
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
if self.school_classes.count() > 0:
|
||||||
|
default_selected_class = self.school_classes.first()
|
||||||
|
UserSetting.objects.create(selected_class=default_selected_class, user=self)
|
||||||
|
return default_selected_class
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def full_name(self):
|
||||||
return self.get_full_name()
|
return self.get_full_name()
|
||||||
|
|
@ -115,3 +126,9 @@ class UserRole(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s: %s' % (self.role, self.user)
|
return '%s: %s' % (self.role, self.user)
|
||||||
|
|
||||||
|
|
||||||
|
class UserSetting(models.Model):
|
||||||
|
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='user_setting')
|
||||||
|
selected_class = models.ForeignKey(SchoolClass, blank=True, null=True, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
import graphene
|
import graphene
|
||||||
from django.contrib.auth import update_session_auth_hash
|
from django.contrib.auth import update_session_auth_hash
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
|
|
||||||
|
from api.utils import get_object
|
||||||
from users.inputs import PasswordUpdateInput
|
from users.inputs import PasswordUpdateInput
|
||||||
|
from users.models import SchoolClass
|
||||||
from users.serializers import PasswordSerialzer, AvatarUrlSerializer
|
from users.serializers import PasswordSerialzer, AvatarUrlSerializer
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,7 +80,28 @@ class UpdateAvatar(relay.ClientIDMutation):
|
||||||
return cls(success=False, errors=errors)
|
return cls(success=False, errors=errors)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSetting(relay.ClientIDMutation):
|
||||||
|
class Input:
|
||||||
|
id = graphene.ID(required=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||||
|
class_id = kwargs.get('id')
|
||||||
|
school_class = get_object(SchoolClass, class_id)
|
||||||
|
user = info.context.user
|
||||||
|
if school_class not in user.school_classes.all():
|
||||||
|
raise PermissionDenied('Permission denied: Incorrect school class')
|
||||||
|
|
||||||
|
user.user_setting.selected_class = school_class
|
||||||
|
user.user_setting.save()
|
||||||
|
return cls(success=True)
|
||||||
|
|
||||||
|
success = graphene.Boolean()
|
||||||
|
errors = graphene.List(UpdateError)
|
||||||
|
|
||||||
|
|
||||||
class ProfileMutations:
|
class ProfileMutations:
|
||||||
update_password = UpdatePassword.Field()
|
update_password = UpdatePassword.Field()
|
||||||
update_avatar = UpdateAvatar.Field()
|
update_avatar = UpdateAvatar.Field()
|
||||||
|
update_setting = UpdateSetting.Field()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,6 @@ from graphene_django.filter import DjangoFilterConnectionField
|
||||||
from users.models import SchoolClass, User
|
from users.models import SchoolClass, User
|
||||||
|
|
||||||
|
|
||||||
class UserNode(DjangoObjectType):
|
|
||||||
pk = graphene.Int()
|
|
||||||
permissions = graphene.List(graphene.String)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
filter_fields = ['username', 'email']
|
|
||||||
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module', 'avatar_url']
|
|
||||||
interfaces = (relay.Node,)
|
|
||||||
|
|
||||||
def resolve_pk(self, info, **kwargs):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def resolve_permissions(self, info):
|
|
||||||
return self.get_all_permissions()
|
|
||||||
|
|
||||||
|
|
||||||
class SchoolClassNode(DjangoObjectType):
|
class SchoolClassNode(DjangoObjectType):
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
|
|
||||||
|
|
@ -35,6 +18,28 @@ class SchoolClassNode(DjangoObjectType):
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
|
|
||||||
|
class UserNode(DjangoObjectType):
|
||||||
|
pk = graphene.Int()
|
||||||
|
permissions = graphene.List(graphene.String)
|
||||||
|
selected_class = graphene.Field(SchoolClassNode)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
filter_fields = ['username', 'email']
|
||||||
|
only_fields = ['username', 'email', 'first_name', 'last_name', 'school_classes', 'last_module', 'avatar_url',
|
||||||
|
'selected_class']
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
def resolve_pk(self, info, **kwargs):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
def resolve_permissions(self, info):
|
||||||
|
return self.get_all_permissions()
|
||||||
|
|
||||||
|
def resolve_selected_class(self, info):
|
||||||
|
return self.selected_class()
|
||||||
|
|
||||||
|
|
||||||
class UsersQuery(object):
|
class UsersQuery(object):
|
||||||
me = graphene.Field(UserNode)
|
me = graphene.Field(UserNode)
|
||||||
all_users = DjangoFilterConnectionField(UserNode)
|
all_users = DjangoFilterConnectionField(UserNode)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# ITerativ GmbH
|
||||||
|
# http://www.iterativ.ch/
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 ITerativ GmbH. All rights reserved.
|
||||||
|
#
|
||||||
|
# Created on 2019-07-24
|
||||||
|
# @author: chrigu <christian.cueni@iterativ.ch>
|
||||||
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
|
from django.test import TestCase, RequestFactory
|
||||||
|
from graphene.test import Client
|
||||||
|
from api.schema import schema
|
||||||
|
from core.factories import UserFactory
|
||||||
|
from users.factories import SchoolClassFactory
|
||||||
|
from users.models import UserSetting
|
||||||
|
|
||||||
|
|
||||||
|
class UserSettingTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = UserFactory(username='aschi')
|
||||||
|
self.class1 = SchoolClassFactory(users=[self.user])
|
||||||
|
self.class2 = SchoolClassFactory(users=[self.user])
|
||||||
|
self.class3 = SchoolClassFactory(users=[])
|
||||||
|
|
||||||
|
request = RequestFactory().get('/')
|
||||||
|
request.user = self.user
|
||||||
|
|
||||||
|
# adding session
|
||||||
|
middleware = SessionMiddleware()
|
||||||
|
middleware.process_request(request)
|
||||||
|
request.session.save()
|
||||||
|
self.client = Client(schema=schema, context_value=request)
|
||||||
|
|
||||||
|
def make_request(self, class_id):
|
||||||
|
mutation = '''
|
||||||
|
mutation UpdateSettings($input: UpdateSettingInput!) {
|
||||||
|
updateSetting(input: $input) {
|
||||||
|
success
|
||||||
|
errors {
|
||||||
|
field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
return self.client.execute(mutation, variables={
|
||||||
|
'input': {
|
||||||
|
'id': class_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
def test_selects_first_class_on_first_call(self):
|
||||||
|
query = '''
|
||||||
|
query MeQuery {
|
||||||
|
me {
|
||||||
|
selectedClass {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
first_class = self.user.school_classes.first()
|
||||||
|
result = self.client.execute(query)
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'), first_class.name)
|
||||||
|
|
||||||
|
def test_returns_selected_class(self):
|
||||||
|
query = '''
|
||||||
|
query MeQuery {
|
||||||
|
me {
|
||||||
|
selectedClass {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
selected_class = self.user.school_classes.all()[1]
|
||||||
|
setting = UserSetting.objects.create(user=self.user, selected_class=selected_class)
|
||||||
|
setting.save()
|
||||||
|
result = self.client.execute(query)
|
||||||
|
self.assertIsNone(result.get('errors'))
|
||||||
|
self.assertEqual(result.get('data').get('me').get('selectedClass').get('name'),
|
||||||
|
selected_class.name)
|
||||||
Loading…
Reference in New Issue