From 5b66edb387c01afc03c032705882af6c697dab64 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 13:49:25 +0200 Subject: [PATCH 1/8] add group count query --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/groups.js | 33 +++++++++++++++++++ backend/src/schema/types/type/Group.gql | 6 ++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 3d698810e..728a248fb 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -312,6 +312,7 @@ export default shield( currentUser: allow, Group: isAuthenticated, GroupMembers: isAllowedSeeingGroupMembers, + GroupCount: isAuthenticated, Post: allow, profilePagePosts: allow, Comment: allow, diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index 5e22bd743..caed48bd0 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -81,6 +81,39 @@ export default { session.close() } }, + GroupCount: async (_object, params, context, _resolveInfo) => { + const { isMember } = params + const { + user: { id: userId }, + } = context + const session = context.driver.session() + const readTxResultPromise = session.readTransaction(async (txc) => { + let cypher + if (isMember) { + cypher = `MATCH (user:User)-[membership:MEMBER_OF]->(group:Group) + WHERE user.id = $userId + AND membership.role IN ['usual', 'admin', 'owner'] + RETURN toString(count(group)) AS count` + } else { + cypher = `MATCH (group:Group) + OPTIONAL MATCH (user:User)-[membership:MEMBER_OF]->(group) + WHERE user.id = $userId + WITH group, membership + WHERE group.groupType IN ['public', 'closed'] + OR membership.role IN ['usual', 'admin', 'owner'] + RETURN toString(count(group)) AS count` + } + const transactionResponse = await txc.run(cypher, { userId }) + return transactionResponse.records.map((record) => record.get('count')) + }) + try { + return parseInt(await readTxResultPromise) + } catch (error) { + throw new Error(error) + } finally { + session.close() + } + }, }, Mutation: { CreateGroup: async (_parent, params, context, _resolveInfo) => { diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql index c4890fdce..f7cbe0aa4 100644 --- a/backend/src/schema/types/type/Group.gql +++ b/backend/src/schema/types/type/Group.gql @@ -65,8 +65,8 @@ type Query { isMember: Boolean # if 'undefined' or 'null' then get all groups id: ID slug: String - # first: Int # not implemented yet - # offset: Int # not implemented yet + first: Int # not implemented yet + offset: Int # not implemented yet # orderBy: [_GroupOrdering] # not implemented yet # filter: _GroupFilter # not implemented yet ): [Group] @@ -79,6 +79,8 @@ type Query { # filter: _UserFilter # not implemented yet ): [User] + GroupCount(isMember: Boolean): Int + # AvailableGroupTypes: [GroupType]! # AvailableGroupActionRadii: [GroupActionRadius]! From 7f22b5fb6261107201b0d9e6ce3dd92783a6eb02 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 14:06:02 +0200 Subject: [PATCH 2/8] add tab to group page to have all groups and my groups --- webapp/graphql/groups.js | 12 +++++- webapp/locales/de.json | 1 + webapp/locales/en.json | 1 + webapp/pages/my-groups.vue | 76 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js index 5ee5869ce..e3510c74d 100644 --- a/webapp/graphql/groups.js +++ b/webapp/graphql/groups.js @@ -145,8 +145,8 @@ export const changeGroupMemberRoleMutation = () => { export const groupQuery = (i18n) => { const lang = i18n ? i18n.locale().toUpperCase() : 'EN' return gql` - query ($isMember: Boolean, $id: ID, $slug: String) { - Group(isMember: $isMember, id: $id, slug: $slug) { + query ($isMember: Boolean, $id: ID, $slug: String, $first: Int, $offset: Int) { + Group(isMember: $isMember, id: $id, slug: $slug, first: $first, offset: $offset) { id name slug @@ -190,3 +190,11 @@ export const groupMembersQuery = () => { } ` } + +export const groupCountQuery = () => { + return gql` + query ($isMember: Boolean) { + GroupCount(isMember: $isMember) + } + ` +} diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 67c88e4f4..e93584e2f 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -407,6 +407,7 @@ "addMemberToGroup": "Zur Gruppe hinzufügen", "addUser": "Benutzer hinzufügen", "addUserPlaceholder": "eindeutiger Benutzername > @slug-from-user", + "allGroups": "Alle Gruppen", "categories": "Thema ::: Themen", "changeMemberRole": "Die Rolle wurde auf „{role}“ geändert!", "contentMenu": { diff --git a/webapp/locales/en.json b/webapp/locales/en.json index ba1d65881..be360bcdd 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -407,6 +407,7 @@ "addMemberToGroup": "Add to group", "addUser": "Add User", "addUserPlaceholder": "unique username > @slug-from-user", + "allGroups": "All Groups", "categories": "Topic ::: Topics", "changeMemberRole": "The role has been changed to “{role}”!", "contentMenu": { diff --git a/webapp/pages/my-groups.vue b/webapp/pages/my-groups.vue index 7302b92ad..6b13648fc 100644 --- a/webapp/pages/my-groups.vue +++ b/webapp/pages/my-groups.vue @@ -1,7 +1,7 @@ @@ -31,6 +57,7 @@ import GroupList from '~/components/Group/GroupList' import { groupQuery, groupCountQuery } from '~/graphql/groups.js' import TabNavigation from '~/components/_new/generic/TabNavigation/TabNavigation' +import PaginationButtons from '~/components/_new/generic/PaginationButtons/PaginationButtons' const tabToFilterMapping = (tab) => { return { @@ -44,6 +71,7 @@ export default { components: { GroupList, TabNavigation, + PaginationButtons, }, data() { return { @@ -51,7 +79,7 @@ export default { groupFilter: { isMember: true }, tabActive: 'myGroups', pageSize: 5, - currentPage: 1, + activePage: 0, myGroupsCount: 0, allGroupsCount: 0, } @@ -60,16 +88,37 @@ export default { handleTab(tab) { if (this.tabActive !== tab) { this.tabActive = tab + this.activePage = 0 this.groupFilter = tabToFilterMapping(tab) this.$apollo.queries.Group.refetch() } }, + previousResults() { + this.activePage-- + this.$apollo.queries.Group.refetch() + }, + nextResults() { + this.activePage++ + this.$apollo.queries.Group.refetch() + }, }, computed: { + activeTab() { + return this.tabOptions.find((tab) => tab.type === this.tabActive) + }, + showPagination() { + return this.activeTab.count > this.pageSize + }, + hasNext() { + return (this.activePage + 1) * this.pageSize < this.activeTab.count + }, + hasPrevious() { + return this.activePage > 0 + }, pagination() { return { first: this.pageSize, - offset: (this.currentPage - 1) * this.pageSize, + offset: this.activePage * this.pageSize, } }, myGroups() { From bbf625d525f6e9ef27b2675c878c4f724cd0aba6 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 15:15:08 +0200 Subject: [PATCH 6/8] remove comments --- backend/src/schema/types/type/Group.gql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql index f7cbe0aa4..ce90fad1d 100644 --- a/backend/src/schema/types/type/Group.gql +++ b/backend/src/schema/types/type/Group.gql @@ -65,8 +65,8 @@ type Query { isMember: Boolean # if 'undefined' or 'null' then get all groups id: ID slug: String - first: Int # not implemented yet - offset: Int # not implemented yet + first: Int + offset: Int # orderBy: [_GroupOrdering] # not implemented yet # filter: _GroupFilter # not implemented yet ): [Group] From b18348523780365e9d47e932dba33af4a5f14f06 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 15:17:36 +0200 Subject: [PATCH 7/8] fix locales --- webapp/locales/de.json | 2 +- webapp/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index cc2acd05e..39fd2de9f 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -427,7 +427,6 @@ "general": "Allgemein", "goal": "Ziel der Gruppe", "groupCreated": "Die Gruppe wurde angelegt!", - "Groups": "Gruppen", "in": "in", "joinLeaveButton": { "iAmMember": "Bin Mitglied", @@ -449,6 +448,7 @@ "membersCount": "Mitglied ::: Mitglieder", "membersListTitle": "Gruppenmitglieder", "membersListTitleNotAllowedSeeingGroupMembers": "Gruppenmitglieder unsichtbar", + "myGroups": "Meine Gruppen", "name": "Gruppenname", "radius": "Radius", "removeMember": "Mitglied aus der Gruppe entfernen?", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index a0ed18c2e..18b579df4 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -427,7 +427,6 @@ "general": "General", "goal": "Goal of group", "groupCreated": "The group was created!", - "Groups": "Groups", "in": "in", "joinLeaveButton": { "iAmMember": "I'm a member", @@ -449,6 +448,7 @@ "membersCount": "Member ::: Members", "membersListTitle": "Group Members", "membersListTitleNotAllowedSeeingGroupMembers": "Group Members invisible", + "myGroups": "My Groups", "name": "Group name", "radius": "Radius", "removeMember": "Remove member", From 1cc663ed3444b45826d3e510fe9cdd8b5628686f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Oct 2022 16:05:44 +0200 Subject: [PATCH 8/8] add spec for groups --- webapp/pages/groups.spec.js | 35 +++++++++++++++++++++++++++++++++++ webapp/pages/groups.vue | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 webapp/pages/groups.spec.js diff --git a/webapp/pages/groups.spec.js b/webapp/pages/groups.spec.js new file mode 100644 index 000000000..0fbacd6b3 --- /dev/null +++ b/webapp/pages/groups.spec.js @@ -0,0 +1,35 @@ +import { config, mount } from '@vue/test-utils' +import groups from './groups.vue' + +const localVue = global.localVue + +config.stubs['nuxt-link'] = '' +config.stubs['client-only'] = '' + +describe('groups', () => { + let wrapper + let mocks + + beforeEach(() => { + mocks = { + $t: jest.fn(), + } + }) + + describe('mount', () => { + const Wrapper = () => { + return mount(groups, { + mocks, + localVue, + }) + } + + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders', () => { + expect(wrapper.is('div')).toBe(true) + }) + }) +}) diff --git a/webapp/pages/groups.vue b/webapp/pages/groups.vue index 87c9bb9a1..386905c5b 100644 --- a/webapp/pages/groups.vue +++ b/webapp/pages/groups.vue @@ -67,7 +67,7 @@ const tabToFilterMapping = (tab) => { } export default { - name: 'MyGroups', + name: 'Groups', components: { GroupList, TabNavigation,