Add 'Leave Team' action

This commit is contained in:
Ramon Wenger 2021-05-25 23:30:19 +02:00
parent a43dffc7f7
commit 9582773378
16 changed files with 114 additions and 49 deletions

View File

@ -28,11 +28,16 @@
v-for="member in activeMembers">
<span class="member-item__name">{{ fullName(member) }}</span>
<span class="member-item__role">{{ role(member) }}</span>
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="remove-from-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('remove', member)">Deaktivieren</a>-->
<a
class="member-item__action simple-list__action"
data-cy="leave-group"
v-if="member.isMe"
@click="$emit('leave')">Verlassen</a>
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="remove-from-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('remove', member)">Deaktivieren</a>-->
</li>
</ul>
<!-- <template v-if="inactiveMembers.length">-->

View File

@ -0,0 +1,11 @@
fragment TeamParts on TeamNode {
name
code
id
members {
firstName
lastName
id
isMe
}
}

View File

@ -1,9 +1,9 @@
#import "gql/fragments/teamParts.gql"
mutation CreateTeamMutation($input: CreateTeamInput!) {
createTeam(input: $input) {
success
team {
name
code
...TeamParts
}
}
}

View File

@ -1,9 +1,9 @@
#import "gql/fragments/teamParts.gql"
mutation JoinTeamMutation($input: JoinTeamInput!) {
joinTeam(input: $input) {
success
team {
name
code
...TeamParts
}
}
}

View File

@ -0,0 +1,5 @@
mutation LeaveTeam {
leaveTeam {
success
}
}

View File

@ -1,16 +1,10 @@
#import "../fragments/userParts.gql"
#import "../fragments/teamParts.gql"
query MeQuery {
me {
...UserParts
team {
name
code
id
members {
firstName
lastName
id
}
...TeamParts
}
isTeacher
permissions

View File

@ -0,0 +1,18 @@
import ME_QUERY from 'gql/queries/meQuery';
const addTeam = (store, team) => {
const query = ME_QUERY;
const data = store.readQuery({query});
store.writeQuery({
query,
data: {
...data,
me: {
...data.me,
team: team,
},
},
});
};
export default addTeam;

View File

@ -1,8 +0,0 @@
export default {
methods: {
addTeam(store, team) {
// todo
throw new Error('NotImplemented');
}
}
};

View File

View File

@ -13,18 +13,17 @@
</template>
<script>
import addTeamMixin from '@/mixins/add-team';
import JoinForm from '@/components/profile/JoinForm';
import CREATE_TEAM_MUTATION from '@/graphql/gql/mutations/createTeam.gql';
import {MY_TEAM} from '@/router/me.names';
import ME_QUERY from 'gql/queries/meQuery';
import addTeam from '@/helpers/add-team';
export default {
mixins: [addTeamMixin],
components: {
JoinForm
JoinForm,
},
data: () => ({
@ -37,7 +36,7 @@
this.name = event.target.value;
this.error = '';
// todo: pass error to component
throw new Error('NotImplemented');
// throw new Error('NotImplemented');
},
createTeam(name) {
this.$apollo.mutate({
@ -48,7 +47,8 @@
},
},
update: (store, {data: {createTeam: {team}}}) => {
this.addTeam(store, team);
addTeam(store, team);
this.$router.push({
name: MY_TEAM,
});

View File

@ -12,10 +12,10 @@
<script>
import JOIN_TEAM_MUTATION from '@/graphql/gql/mutations/joinTeam.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import JoinForm from '@/components/profile/JoinForm';
import {MY_TEAM} from '@/router/me.names';
import addTeam from '@/helpers/add-team';
export default {
components: {
@ -49,18 +49,7 @@
},
},
update: (store, {data: {joinTeam: {team}}}) => {
const query = ME_QUERY;
const data = store.readQuery({query});
store.writeQuery({
query,
data: {
...data,
me: {
...data.me,
team: team,
},
},
});
addTeam(store, team);
this.$router.push({name: MY_TEAM});
},
});

View File

@ -9,6 +9,7 @@
:name="me.team.name"
title="Mein Team"
@edit="editTeamName"
@leave="leaveTeam"
/>
</template>
<template v-else>
@ -36,6 +37,8 @@
import {CREATE_TEAM, JOIN_TEAM, SHOW_TEAM_CODE} from '@/router/me.names';
import me from '@/mixins/me';
import GroupList from '@/components/profile/GroupList';
import LEAVE_TEAM_MUTATION from 'gql/mutations/me/leaveTeam.gql';
import addTeam from '@/helpers/add-team';
export default {
mixins: [me],
@ -50,16 +53,24 @@
name: CREATE_TEAM,
},
showCodeRoute: {
name: SHOW_TEAM_CODE
}
name: SHOW_TEAM_CODE,
},
};
},
methods: {
editTeamName() {
this.$store.dispatch('editTeamName');
}
}
},
leaveTeam() {
this.$apollo.mutate({
mutation: LEAVE_TEAM_MUTATION,
update(store, {data: {leaveTeam: {success}}}) {
addTeam(store, null);
},
});
},
},
};
</script>

View File

@ -388,6 +388,7 @@ type CustomMutation {
createTeam(input: CreateTeamInput!): CreateTeamPayload
joinTeam(input: JoinTeamInput!): JoinTeamPayload
updateTeam(input: UpdateTeamInput!): UpdateTeamPayload
leaveTeam: LeaveTeam
addProject(input: AddProjectInput!): AddProjectPayload
updateProject(input: UpdateProjectInput!): UpdateProjectPayload
deleteProject(input: DeleteProjectInput!): DeleteProjectPayload
@ -614,6 +615,10 @@ type JoinTeamPayload {
clientMutationId: String
}
type LeaveTeam {
success: Boolean
}
type Logout {
success: Boolean
}
@ -1436,6 +1441,7 @@ type UserNode implements Node {
isTeacher: Boolean
oldClasses(offset: Int, before: String, after: String, first: Int, last: Int, name: String): SchoolClassNodeConnection
recentModules(offset: Int, before: String, after: String, first: Int, last: Int, recentModules: [ID], orderBy: String): ModuleNodeConnection
isMe: Boolean
}
type UserNodeConnection {

View File

@ -14,6 +14,7 @@ from users.serializers import PasswordSerialzer, AvatarUrlSerializer
logger = get_logger(__name__)
class CodeNotFoundException(Exception):
pass
@ -281,6 +282,17 @@ class JoinTeam(TeacherOnlyMutation):
raise CodeNotFoundException('[CNV] Code ist nicht gültig') # CNV = Code Not Valid
class LeaveTeam(graphene.Mutation):
success = graphene.Boolean()
@classmethod
def mutate(cls, root, info, **kwargs):
user = info.context.user
user.team = None
user.save()
return cls(success=True)
class UpdateOnboardingProgress(graphene.Mutation):
success = graphene.Boolean()
@ -306,3 +318,4 @@ class ProfileMutations:
create_team = CreateTeam.Field()
join_team = JoinTeam.Field()
update_team = UpdateTeam.Field()
leave_team = LeaveTeam.Field()

View File

@ -77,6 +77,7 @@ class UserNode(DjangoObjectType):
old_classes = DjangoFilterConnectionField(SchoolClassNode)
recent_modules = DjangoFilterConnectionField(ModuleNode, filterset_class=RecentModuleFilter)
team = graphene.Field(TeamNode)
is_me = graphene.Boolean()
class Meta:
model = User
@ -120,6 +121,9 @@ class UserNode(DjangoObjectType):
def resolve_team(self, info, **kwargs):
return self.team
def resolve_is_me(self, info, **kwargs):
return info.context.user.pk == self.pk
class ClassMemberNode(ObjectType):
"""

View File

@ -7,7 +7,7 @@ from api.schema import schema
from api.utils import get_graphql_mutation
from core.factories import TeacherFactory, UserFactory
from users.factories import TeamFactory
from users.models import Team
from users.models import Team, User
ME_QUERY = """
query MeQuery {
@ -23,6 +23,7 @@ ME_QUERY = """
CREATE_TEAM_MUTATION = get_graphql_mutation('createTeam.gql')
JOIN_TEAM_MUTATION = get_graphql_mutation('joinTeam.gql')
UPDATE_TEAM_MUTATION = get_graphql_mutation('updateTeam.gql')
LEAVE_TEAM_MUTATION = get_graphql_mutation('me/leaveTeam.gql')
class TeamTest(TestCase):
@ -169,3 +170,19 @@ class TeamTest(TestCase):
result = self.client.execute(JOIN_TEAM_MUTATION, context=context, variables=variables)
self.permission_error(result)
self.assertIsNone(self.student.team)
def test_leave_team(self):
self.student.team = self.team
self.student.save()
student_before = User.objects.get(pk=self.student.pk)
self.assertIsNotNone(student_before.team)
context = Context(user=student_before)
result = self.client.execute(LEAVE_TEAM_MUTATION, context=context)
self.no_error(result)
leave_team = result.get('data').get('leaveTeam')
success = leave_team.get('success')
self.assertTrue(success)
student_after = User.objects.get(pk=self.student.pk)
self.assertIsNone(student_after.team)