Remove and add members in client

This commit is contained in:
Ramon Wenger 2020-03-05 16:20:57 +01:00
parent 4a3d08203d
commit 9dab0856e8
6 changed files with 16170 additions and 15693 deletions

View File

@ -437,15 +437,19 @@
}, },
{ {
"name": "topic", "name": "topic",
"description": null, "description": "The ID of the object",
"args": [ "args": [
{ {
"name": "slug", "name": "id",
"description": null, "description": null,
"type": { "type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR", "kind": "SCALAR",
"name": "String", "name": "ID",
"ofType": null "ofType": null
}
}, },
"defaultValue": null "defaultValue": null
} }
@ -701,71 +705,11 @@
"ofType": null "ofType": null
}, },
"defaultValue": null "defaultValue": null
},
{
"name": "slug",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "slug_Icontains",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "slug_In",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title_Icontains",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title_In",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
} }
], ],
"type": { "type": {
"kind": "OBJECT", "kind": "OBJECT",
"name": "TopicNodeConnection", "name": "TopicConnection",
"ofType": null "ofType": null
}, },
"isDeprecated": false, "isDeprecated": false,
@ -3362,6 +3306,119 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "moduleSet",
"description": null,
"args": [
{
"name": "before",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": null,
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": null,
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "slug",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "slug_Icontains",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "slug_In",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title_Icontains",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "title_In",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ModuleNodeConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "hiddenContentBlocks", "name": "hiddenContentBlocks",
"description": null, "description": null,
@ -3864,6 +3921,22 @@
}, },
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "members",
"description": null,
"args": [],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ClassMemberNode",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"inputFields": null, "inputFields": null,
@ -3973,6 +4046,92 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "ModuleNodeConnection",
"description": null,
"fields": [
{
"name": "pageInfo",
"description": "Pagination data for this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "edges",
"description": "Contains the nodes in this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ModuleNodeEdge",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ModuleNodeEdge",
"description": "A Relay edge containing a `ModuleNode` and its cursor.",
"fields": [
{
"name": "node",
"description": "The item at the end of the edge",
"args": [],
"type": {
"kind": "OBJECT",
"name": "ModuleNode",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "cursor",
"description": "A cursor for use in pagination",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "ContentBlockNodeConnection", "name": "ContentBlockNodeConnection",
@ -6617,6 +6776,89 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "ClassMemberNode",
"description": "We need to build this ourselves, because we want the active property on the node, because providing it on the\nConnection or Edge for a UserNodeConnection is difficult.",
"fields": [
{
"name": "user",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "UserNode",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "active",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "firstName",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "lastName",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "isTeacher",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "StudentSubmissionNode", "name": "StudentSubmissionNode",
@ -7239,92 +7481,6 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "ModuleNodeConnection",
"description": null,
"fields": [
{
"name": "pageInfo",
"description": "Pagination data for this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "edges",
"description": "Contains the nodes in this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ModuleNodeEdge",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ModuleNodeEdge",
"description": "A Relay edge containing a `ModuleNode` and its cursor.",
"fields": [
{
"name": "node",
"description": "The item at the end of the edge",
"args": [],
"type": {
"kind": "OBJECT",
"name": "ModuleNode",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "cursor",
"description": "A cursor for use in pagination",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "StudentSubmissionNodeConnection", "name": "StudentSubmissionNodeConnection",
@ -8825,6 +8981,92 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "TopicConnection",
"description": null,
"fields": [
{
"name": "pageInfo",
"description": "Pagination data for this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "edges",
"description": "Contains the nodes in this connection.",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "TopicEdge",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TopicEdge",
"description": "A Relay edge containing a `Topic` and its cursor.",
"fields": [
{
"name": "node",
"description": "The item at the end of the edge",
"args": [],
"type": {
"kind": "OBJECT",
"name": "TopicNode",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "cursor",
"description": "A cursor for use in pagination",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "DjangoDebug", "name": "DjangoDebug",
@ -9397,6 +9639,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "addRemoveMember",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "AddRemoveMemberInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "AddRemoveMemberPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "addProject", "name": "addProject",
"description": null, "description": null,
@ -11628,6 +11897,104 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "AddRemoveMemberPayload",
"description": null,
"fields": [
{
"name": "success",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "clientMutationId",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "AddRemoveMemberInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "member",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "schoolClass",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "active",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "AddProjectPayload", "name": "AddProjectPayload",

View File

@ -0,0 +1,29 @@
{
"me": {
"id": "VXNlck5vZGU6Mg==",
"selectedClass": {
"id": "U2Nob29sQ2xhc3NOb2RlOjE=",
"name": "Moordale",
"members": [
{
"id": "VXNlck5vZGU6Mw==",
"firstName": "Otis",
"lastName": "Milburn",
"isTeacher": false,
"active": true,
"__typename": "ClassMemberNode"
},
{
"id": "VXNlck5vZGU6NA==",
"firstName": "Maeve",
"lastName": "Wiley",
"isTeacher": false,
"active": true,
"__typename": "ClassMemberNode"
}
],
"__typename": "SchoolClassNode"
},
"__typename": "UserNode"
}
}

View File

@ -1,7 +1,8 @@
const schema = require('../fixtures/schema.json'); const schema = require('../fixtures/schema.json');
const me = require('../fixtures/me.join-class.json'); const me = require('../fixtures/me.join-class.json');
const selectedClass = require('../fixtures/selected-school-class.json');
describe('Join Class', () => { describe('Class Management', () => {
beforeEach(() => { beforeEach(() => {
cy.server(); cy.server();
@ -31,7 +32,6 @@ describe('Join Class', () => {
} }
}); });
cy.visit('/me/profile'); cy.visit('/me/profile');
cy.get('[data-cy=header-user-widget]').within(() => { cy.get('[data-cy=header-user-widget]').within(() => {
@ -51,5 +51,49 @@ describe('Join Class', () => {
cy.get('[data-cy=class-selection]').click(); cy.get('[data-cy=class-selection]').click();
cy.get('[data-cy=class-selection-entry]').should('have.length', 2); cy.get('[data-cy=class-selection-entry]').should('have.length', 2);
}) });
})
it('should leave and re-join class', () => {
const teacher = {
...me,
permissions: ['users.can_manage_school_class_content']
};
cy.mockGraphqlOps({
operations: {
MeQuery: teacher,
AddRemoveMember: {
addRemoveMember: {
success: true
}
},
MySchoolClassQuery: selectedClass
}
});
cy.visit('/me/my-class');
cy.get('[data-cy=active-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 2)
});
cy.get('[data-cy=inactive-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 0)
});
cy.get('[data-cy=remove-from-class]').first().click();
cy.get('[data-cy=active-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 1)
});
cy.get('[data-cy=inactive-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 1)
});
cy.get('[data-cy=add-to-class]').first().click();
cy.get('[data-cy=active-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 2)
});
cy.get('[data-cy=inactive-class-members-list]').within(() => {
cy.get('[data-cy=school-class-member]').should('have.length', 0)
});
});
});

View File

@ -22,7 +22,7 @@
:key="member.id"> :key="member.id">
<span class="member-item__name">{{fullName(member)}}</span> <span class="member-item__name">{{fullName(member)}}</span>
<span class="member-item__role">{{role(member)}}</span> <span class="member-item__role">{{role(member)}}</span>
<a class="member-item__action" @click="$emit('add', member)">Aktivieren</a> <a class="member-item__action" data-cy="add-to-class" @click="$emit('add', member)">Aktivieren</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -30,7 +30,6 @@
</template> </template>
<script> <script>
export default { export default {
props: ['members', 'name'], props: ['members', 'name'],
methods: { methods: {

View File

@ -1,18 +1,55 @@
<template> <template>
<div class="my-class"> <div class="my-class">
<h1 class="my-class__header" data-cy="class-list-title">Klassenliste</h1> <h1 class="my-class__header" data-cy="class-list-title">Klassenliste</h1>
<classlist class="my-class__class" :name="selectedClass.name" :members="selectedClass.members"></classlist> <class-list
class="my-class__class"
:name="selectedClass.name"
:members="selectedClass.members"
@remove="remove"
@add="add"
></class-list>
</div> </div>
</template> </template>
<script> <script>
import ClassList from '@/components/profile/ClassList';
import MY_SCHOOL_CLASS_QUERY from '@/graphql/gql/mySchoolClass.gql'; import MY_SCHOOL_CLASS_QUERY from '@/graphql/gql/mySchoolClass.gql';
import Classlist from '@/components/profile/Classlist'; import ADD_REMOVE_MEMBER_MUTATION from '@/graphql/gql/mutations/addRemoveMember.gql';
export default { export default {
components: { components: {
Classlist ClassList
},
methods: {
changeMember(member, active) {
this.$apollo.mutate({
mutation: ADD_REMOVE_MEMBER_MUTATION,
variables: {
input: {
member: member.id,
schoolClass: this.selectedClass.id,
active
}
},
update(store, {data: {addRemoveMember: {success}}}) {
if (success) {
const query = MY_SCHOOL_CLASS_QUERY;
const data = store.readQuery({query});
let memberIndex = data.me.selectedClass.members.findIndex(m => m.id === member.id);
data.me.selectedClass.members.splice(memberIndex, 1, {...member, active});
store.writeQuery({query, data});
}
}
});
},
add(member) {
this.changeMember(member, true);
},
remove(member) {
this.changeMember(member, false);
}
}, },
apollo: { apollo: {

View File

@ -146,6 +146,7 @@
font-family: $sans-serif-font-family; font-family: $sans-serif-font-family;
font-weight: $font-weight-regular; font-weight: $font-weight-regular;
color: $color-silver-dark; color: $color-silver-dark;
cursor: pointer;
&--active { &--active {
color: $color-brand; color: $color-brand;