From 924db68d0638172d3dbc280e1ca7dd321d9abed6 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 7 Jul 2022 15:52:55 +0200 Subject: [PATCH 001/122] setup community statistics --- backend/src/auth/RIGHTS.ts | 1 + .../src/graphql/model/CommunityStatistics.ts | 26 +++++++++++++++++++ .../graphql/resolver/StatisticsResolver.ts | 26 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 backend/src/graphql/model/CommunityStatistics.ts create mode 100644 backend/src/graphql/resolver/StatisticsResolver.ts diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index c10fc96de..cbf650ec1 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -44,4 +44,5 @@ export enum RIGHTS { LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', + COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS', } diff --git a/backend/src/graphql/model/CommunityStatistics.ts b/backend/src/graphql/model/CommunityStatistics.ts new file mode 100644 index 000000000..61354115c --- /dev/null +++ b/backend/src/graphql/model/CommunityStatistics.ts @@ -0,0 +1,26 @@ +import { ObjectType, Field } from 'type-graphql' +import Decimal from 'decimal.js-light' + +@ObjectType() +export class CommunityStatistics { + @Field(() => Number) + totalUsers: number + + @Field(() => Number) + activeUsers: number + + @Field(() => Number) + deletedUsers: number + + @Field(() => Decimal) + totalGradidoCreated: Decimal + + @Field(() => Decimal) + totalGradidoDecayed: Decimal + + @Field(() => Decimal) + totalGradidoAvailable: Decimal + + @Field(() => Decimal) + totalGradidoUnbookedDecayed: Decimal +} diff --git a/backend/src/graphql/resolver/StatisticsResolver.ts b/backend/src/graphql/resolver/StatisticsResolver.ts new file mode 100644 index 000000000..a90d42f75 --- /dev/null +++ b/backend/src/graphql/resolver/StatisticsResolver.ts @@ -0,0 +1,26 @@ +import { Resolver, Query, Arg, Args, Authorized, Ctx, Int } from 'type-graphql' +import { RIGHTS } from '@/auth/RIGHTS' +import { CommunityStatistics } from '@model/CommunityStatistics' +import { User as DbUser } from '@entity/User' +import { getConnection } from '@dbTools/typeorm' +import Decimal from 'decimal.js-light' + +@Resolver() +export class StatisticsResolver { + @Authorized([RIGHTS.COMMUNITY_STATISTICS]) + @Query(() => CommunityStatistics) + async communityStatistics(): Promise { + const totalUsers = await DbUser.find({ withDeleted: true }) + console.log(totalUsers.length) + + return { + totalUsers: 12, + activeUsers: 6, + deletedUsers: 1, + totalGradidoCreated: new Decimal(3000), + totalGradidoDecayed: new Decimal(200), + totalGradidoAvailable: new Decimal(2800), + totalGradidoUnbookedDecayed: new Decimal(200), + } + } +} From df227607ad539ad0bc209eebed917d0a7b356bed Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 7 Jul 2022 17:27:50 +0200 Subject: [PATCH 002/122] get basic statistics from database --- .../graphql/resolver/StatisticsResolver.ts | 71 ++++++++++++++++--- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/backend/src/graphql/resolver/StatisticsResolver.ts b/backend/src/graphql/resolver/StatisticsResolver.ts index a90d42f75..4c1500839 100644 --- a/backend/src/graphql/resolver/StatisticsResolver.ts +++ b/backend/src/graphql/resolver/StatisticsResolver.ts @@ -1,26 +1,77 @@ -import { Resolver, Query, Arg, Args, Authorized, Ctx, Int } from 'type-graphql' +import { Resolver, Query, Authorized } from 'type-graphql' import { RIGHTS } from '@/auth/RIGHTS' import { CommunityStatistics } from '@model/CommunityStatistics' import { User as DbUser } from '@entity/User' +import { Transaction as DbTransaction } from '@entity/Transaction' import { getConnection } from '@dbTools/typeorm' import Decimal from 'decimal.js-light' +import { calculateDecay } from '@/util/decay' @Resolver() export class StatisticsResolver { @Authorized([RIGHTS.COMMUNITY_STATISTICS]) @Query(() => CommunityStatistics) async communityStatistics(): Promise { - const totalUsers = await DbUser.find({ withDeleted: true }) - console.log(totalUsers.length) + const allUsers = await DbUser.find({ withDeleted: true }) + + let totalUsers = 0 + let activeUsers = 0 + let deletedUsers = 0 + + let totalGradidoAvailable: Decimal = new Decimal(0) + let totalGradidoUnbookedDecayed: Decimal = new Decimal(0) + + const receivedCallDate = new Date() + + for (let i = 0; i < allUsers.length; i++) { + if (allUsers[i].deletedAt) { + deletedUsers++ + } else { + totalUsers++ + const lastTransaction = await DbTransaction.findOne({ + where: { userId: allUsers[i].id }, + order: { balanceDate: 'DESC' }, + }) + if (lastTransaction) { + activeUsers++ + const decay = calculateDecay( + lastTransaction.balance, + lastTransaction.balanceDate, + receivedCallDate, + ) + if (decay) { + totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString()) + totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString()) + } + } + } + } + + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + + const { totalGradidoCreated } = await queryRunner.manager + .createQueryBuilder() + .select('SUM(transaction.amount) AS totalGradidoCreated') + .from(DbTransaction, 'transaction') + .where('transaction.typeId = 1') + .getRawOne() + + const { totalGradidoDecayed } = await queryRunner.manager + .createQueryBuilder() + .select('SUM(transaction.decay) AS totalGradidoDecayed') + .from(DbTransaction, 'transaction') + .where('transaction.decay IS NOT NULL') + .getRawOne() return { - totalUsers: 12, - activeUsers: 6, - deletedUsers: 1, - totalGradidoCreated: new Decimal(3000), - totalGradidoDecayed: new Decimal(200), - totalGradidoAvailable: new Decimal(2800), - totalGradidoUnbookedDecayed: new Decimal(200), + totalUsers, + activeUsers, + deletedUsers, + totalGradidoCreated, + totalGradidoDecayed, + totalGradidoAvailable, + totalGradidoUnbookedDecayed, } } } From baa5084f5d6365485f1af3a109e5dcd771155eed Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 7 Jul 2022 17:29:09 +0200 Subject: [PATCH 003/122] add query for communty statistics --- admin/src/graphql/communityStatistics.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 admin/src/graphql/communityStatistics.js diff --git a/admin/src/graphql/communityStatistics.js b/admin/src/graphql/communityStatistics.js new file mode 100644 index 000000000..868bfd02a --- /dev/null +++ b/admin/src/graphql/communityStatistics.js @@ -0,0 +1,15 @@ +import gql from 'graphql-tag' + +export const communityStatistics = gql` + query { + communityStatistics { + totalUsers + activeUsers + deletedUsers + totalGradidoCreated + totalGradidoDecayed + totalGradidoAvailable + totalGradidoUnbookedDecayed + } + } +` From b590a82b04cc3d4197e2bf060538c54634a6add6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 26 Jul 2022 16:20:29 +0200 Subject: [PATCH 004/122] Change reset button to cancel and dev comments --- .../src/components/Contributions/ContributionForm.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 647cd0067..97d012516 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -68,8 +68,8 @@ - - {{ $t('form.reset') }} + + {{ $t('form.cancel') }} @@ -136,6 +136,7 @@ export default { return false }, lastMonthObject() { + // Wolle: refine logic and melt with 'thisMonthObject' // new Date().getMonth === 1 If the current month is January, then one year must be gone back in the previous month const obj = { monthAndYear: this.$d(new Date(this.minimalDate), 'monthAndYear'), @@ -144,6 +145,7 @@ export default { return this.$t('contribution.formText.openAmountForMonth', obj) }, thisMonthObject() { + // Wolle: refine logic and melt with 'lastMonthObject' const obj = { monthAndYear: this.$d(new Date(), 'monthAndYear'), creation: this.maxGddThisMonth, @@ -151,15 +153,18 @@ export default { return this.$t('contribution.formText.openAmountForMonth', obj) }, isThisMonth() { + // Wolle: Jahr testen return new Date(this.form.date).getMonth() === new Date().getMonth() }, maxGddLastMonth() { + // Wolle: refine logic and melt with 'maxGddThisMonth' // When edited, the amount is added back on top of the amount return this.form.id && !this.isThisMonth ? parseInt(this.$store.state.creation[1]) + parseInt(this.updateAmount) : this.$store.state.creation[1] }, maxGddThisMonth() { + // Wolle: refine logic and melt with 'maxGddLastMonth' // When edited, the amount is added back on top of the amount return this.form.id && this.isThisMonth ? parseInt(this.$store.state.creation[2]) + parseInt(this.updateAmount) From 7eb6fc6c5165c5b88a8fbfe02061555da947e691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 26 Jul 2022 16:20:49 +0200 Subject: [PATCH 005/122] Refactor and enhance tests, try --- .../Contributions/ContributionForm.spec.js | 131 +++++++++++++++++- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.spec.js b/frontend/src/components/Contributions/ContributionForm.spec.js index 5b05957bb..00a3d9578 100644 --- a/frontend/src/components/Contributions/ContributionForm.spec.js +++ b/frontend/src/components/Contributions/ContributionForm.spec.js @@ -1,4 +1,5 @@ import { mount } from '@vue/test-utils' +import flushPromises from 'flush-promises' import ContributionForm from './ContributionForm.vue' const localVue = global.localVue @@ -6,7 +7,7 @@ const localVue = global.localVue describe('ContributionForm', () => { let wrapper - const propsData = { + let propsData = { value: { id: null, date: '', @@ -42,8 +43,132 @@ describe('ContributionForm', () => { expect(wrapper.find('div.contribution-form').exists()).toBe(true) }) - it('is submit button disable of true', () => { - expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled') + describe('empty form data', () => { + describe('has button', () => { + it('reset enabled', () => { + expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + }) + + it('submit disabled', () => { + expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled') + }) + }) + }) + + describe('set contrubtion', () => { + describe('fill in form data', () => { + const now = new Date().toISOString() + + beforeEach(async () => { + await wrapper.setData({ + form: { + id: null, + date: now, + memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + amount: '200', + }, + }) + }) + + describe('has button', () => { + it('reset enabled', () => { + expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + }) + + it('submit enabled', () => { + expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy() + }) + }) + + describe.skip('on trigger submit', () => { + beforeEach(async () => { + // await wrapper.find('.test-submit').trigger('click') + await wrapper.find('button[type="submit"]').trigger('click') + }) + + it('emits "set-contribution"', () => { + expect(wrapper.emitted('set-contribution')).toEqual({ + id: null, + date: now, + memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + amount: '200', + }) + }) + }) + }) + }) + + describe('update contrubtion', () => { + describe('fill in form data and "id"', () => { + const now = new Date().toISOString() + + beforeEach(async () => { + // Wolle: await wrapper.setData({ + // form: { + // id: 2, + // date: now, + // memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + // amount: '200', + // }, + // }) + // await wrapper.setData({ + // form: { + // id: 2, + // }, + // }) + propsData = { + value: { + id: 2, + date: '', + memo: '', + amount: '', + }, + } + wrapper = Wrapper() + // await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now) + await wrapper + .find('#contribution-memo') + .find('textarea') + .setValue('Mein Beitrag zur Gemeinschaft für diesen Monat ...') + await wrapper.find('#contribution-amount').find('input').setValue('200') + await flushPromises() + // Wolle: + await wrapper.vm.$nextTick() + }) + + describe('has button', () => { + it('reset enabled', () => { + expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy() + }) + + it('submit enabled', () => { + expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy() + }) + }) + + describe.only('on trigger submit', () => { + beforeEach(async () => { + // await wrapper.find('.test-submit').trigger('click') + // await wrapper.find('button[type="submit"]').trigger('click') + await wrapper.find('form').trigger('submit') + }) + + it('emits "update-contribution"', () => { + expect(wrapper.emitted('update-contribution')).toEqual( + expect.arrayContaining([ + expect.arrayContaining([ + { + id: 2, + date: now, + memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...', + amount: '200', + }, + ]), + ]), + ) + }) + }) + }) }) }) }) From 14360fb8afba8ba129a31870cf2867e953648da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 26 Jul 2022 16:26:10 +0200 Subject: [PATCH 006/122] Cleanup a little --- frontend/src/components/Contributions/ContributionForm.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 97d012516..da5b2fbee 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -94,7 +94,6 @@ export default { maxlength: 255, maximalDate: new Date(), form: this.value, // includes 'id' - // Wolle: id: this.value.id, } }, methods: { @@ -108,7 +107,6 @@ export default { }, reset() { this.$refs.form.reset() - // Wolle: this.id = null this.form.id = null this.form.date = '' this.form.memo = '' From 80eaf1c8ea326a9204c8d2e0de90286f9fc2cb5b Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 26 Jul 2022 18:34:40 +0200 Subject: [PATCH 007/122] add fucntionality to copy link and text after creating a transaction link --- frontend/src/components/ClipboardCopy.vue | 20 +++++++++++++++++++ .../GddSend/TransactionResultLink.vue | 14 ++++++++----- frontend/src/pages/Send.vue | 9 ++++++++- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index 7a6cf0ec1..78af3c772 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -3,6 +3,9 @@ + + {{ $t('gdd_per_link.copy-with-text') }} + {{ $t('gdd_per_link.copy') }} @@ -22,6 +25,8 @@ export default { name: 'ClipboardCopy', props: { link: { type: String, required: true }, + amount: { type: Number, required: true }, + memo: { type: String, required: true }, }, data() { return { @@ -40,6 +45,21 @@ export default { this.toastError(this.$t('gdd_per_link.not-copied')) }) }, + copyLinkWithText() { + navigator.clipboard + .writeText( + `${this.link} +${this.$store.state.firstName} ${this.$t('transaction-link.send_you')} ${this.amount} Gradido. +"${this.memo}"`, + ) + .then(() => { + this.toastSuccess(this.$t('gdd_per_link.link-and-text-copied')) + }) + .catch(() => { + this.canCopyLink = false + this.toastError(this.$t('gdd_per_link.not-copied')) + }) + }, }, } diff --git a/frontend/src/components/GddSend/TransactionResultLink.vue b/frontend/src/components/GddSend/TransactionResultLink.vue index 04445acfe..81d18644d 100644 --- a/frontend/src/components/GddSend/TransactionResultLink.vue +++ b/frontend/src/components/GddSend/TransactionResultLink.vue @@ -3,7 +3,12 @@
{{ $t('gdd_per_link.created') }}
- +
@@ -27,10 +32,9 @@ export default { FigureQrCode, }, props: { - link: { - type: String, - required: true, - }, + link: { type: String, required: true }, + amount: { type: Number, required: true }, + memo: { type: String, required: true }, }, data() { return { diff --git a/frontend/src/pages/Send.vue b/frontend/src/pages/Send.vue index cd5f8f572..85ac53a52 100644 --- a/frontend/src/pages/Send.vue +++ b/frontend/src/pages/Send.vue @@ -41,7 +41,12 @@ >
@@ -145,6 +150,8 @@ export default { .then((result) => { this.$emit('set-tunneled-email', null) this.link = result.data.createTransactionLink.link + this.amount = this.transactionData.amount + this.memo = this.transactionData.memo this.transactionData = { ...EMPTY_TRANSACTION_DATA } this.currentTransactionStep = TRANSACTION_STEPS.transactionResultLink this.updateTransactions({}) From 21c9bc22a710febd6d5d61aa5f43a11933981da5 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 10:13:00 +0200 Subject: [PATCH 008/122] adapt existing unit tests for copying a created transaction link --- frontend/src/pages/Send.spec.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Send.spec.js b/frontend/src/pages/Send.spec.js index 47a30ff65..79ba65133 100644 --- a/frontend/src/pages/Send.spec.js +++ b/frontend/src/pages/Send.spec.js @@ -20,6 +20,9 @@ describe('Send', () => { balance: 123.45, GdtBalance: 1234.56, pending: true, + amount: '15', + link: 'http://localhost/redeem/0123456789', + memo: 'Quis auctor elit sed vulputate mi sit amet mauris commodo quis imperdiet.', } const mocks = { @@ -28,6 +31,7 @@ describe('Send', () => { $store: { state: { email: 'sender@example.org', + firstName: 'Testy', }, }, $apollo: { @@ -228,21 +232,26 @@ describe('Send', () => { navigator.clipboard = navigatorClipboard }) - describe('copy with success', () => { + describe('copy link with success', () => { beforeEach(async () => { navigatorClipboardMock.mockResolvedValue() - await wrapper.findAll('button').at(0).trigger('click') + await wrapper.findAll('button').at(1).trigger('click') }) + it('should call clipboard.writeText', () => { + expect(navigator.clipboard.writeText).toHaveBeenCalledWith( + 'http://localhost/redeem/0123456789', + ) + }) it('toasts success message', () => { expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.link-copied') }) }) - describe('copy with error', () => { + describe('copy link with error', () => { beforeEach(async () => { navigatorClipboardMock.mockRejectedValue() - await wrapper.findAll('button').at(0).trigger('click') + await wrapper.findAll('button').at(1).trigger('click') }) it('toasts error message', () => { @@ -253,7 +262,7 @@ describe('Send', () => { describe('close button click', () => { beforeEach(async () => { - await wrapper.findAll('button').at(2).trigger('click') + await wrapper.findAll('button').at(3).trigger('click') }) it('Shows the TransactionForm', () => { From d1cc3115ba642ae9a20ec6c2af92995f4f050438 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 11:07:58 +0200 Subject: [PATCH 009/122] add unit tests for copying a created transaction link with username, amount and memo text --- frontend/src/pages/Send.spec.js | 43 ++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/Send.spec.js b/frontend/src/pages/Send.spec.js index 79ba65133..be63027b1 100644 --- a/frontend/src/pages/Send.spec.js +++ b/frontend/src/pages/Send.spec.js @@ -20,9 +20,6 @@ describe('Send', () => { balance: 123.45, GdtBalance: 1234.56, pending: true, - amount: '15', - link: 'http://localhost/redeem/0123456789', - memo: 'Quis auctor elit sed vulputate mi sit amet mauris commodo quis imperdiet.', } const mocks = { @@ -260,6 +257,46 @@ describe('Send', () => { }) }) + describe('copy link and text with success', () => { + const navigatorClipboard = navigator.clipboard + beforeAll(() => { + delete navigator.clipboard + navigator.clipboard = { writeText: navigatorClipboardMock } + }) + afterAll(() => { + navigator.clipboard = navigatorClipboard + }) + + describe('copy link and text with success', () => { + beforeEach(async () => { + navigatorClipboardMock.mockResolvedValue() + await wrapper.findAll('button').at(0).trigger('click') + }) + + it('should call clipboard.writeText', () => { + expect(navigator.clipboard.writeText).toHaveBeenCalledWith( + 'http://localhost/redeem/0123456789\n' + + 'Testy transaction-link.send_you 56.78 Gradido.\n' + + '"Make the best of the link!"', + ) + }) + it('toasts success message', () => { + expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.link-and-text-copied') + }) + }) + + describe('copy link and text with error', () => { + beforeEach(async () => { + navigatorClipboardMock.mockRejectedValue() + await wrapper.findAll('button').at(0).trigger('click') + }) + + it('toasts error message', () => { + expect(toastErrorSpy).toBeCalledWith('gdd_per_link.not-copied') + }) + }) + }) + describe('close button click', () => { beforeEach(async () => { await wrapper.findAll('button').at(3).trigger('click') From 3c539a6edf0a2136470c12c530965462aaaee339 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 11:21:47 +0200 Subject: [PATCH 010/122] make the wording more precise wherever a link can be copied --- frontend/src/components/ClipboardCopy.vue | 4 ++-- frontend/src/components/TransactionLinks/TransactionLink.vue | 4 ++-- frontend/src/locales/de.json | 3 ++- frontend/src/locales/en.json | 4 +++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index 78af3c772..efddf8ab3 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -4,10 +4,10 @@ - {{ $t('gdd_per_link.copy-with-text') }} + {{ $t('gdd_per_link.copy-link-with-text') }} - {{ $t('gdd_per_link.copy') }} + {{ $t('gdd_per_link.copy-link') }} diff --git a/frontend/src/components/TransactionLinks/TransactionLink.vue b/frontend/src/components/TransactionLinks/TransactionLink.vue index 5618c8696..fe5e44658 100644 --- a/frontend/src/components/TransactionLinks/TransactionLink.vue +++ b/frontend/src/components/TransactionLinks/TransactionLink.vue @@ -20,7 +20,7 @@ - {{ $t('gdd_per_link.copy') }} + {{ $t('gdd_per_link.copy-link') }} - {{ $t('gdd_per_link.copy-with-text') }} + {{ $t('gdd_per_link.copy-link-with-text') }} Date: Wed, 27 Jul 2022 12:17:04 +0200 Subject: [PATCH 011/122] fix linting and locales --- frontend/src/locales/de.json | 1 - frontend/src/locales/en.json | 2 -- 2 files changed, 3 deletions(-) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index aebbffab2..91e602990 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -150,7 +150,6 @@ "GDD": "GDD", "gdd_per_link": { "choose-amount": "Wähle einen Betrag aus, welchen du per Link versenden möchtest. Du kannst auch noch eine Nachricht eintragen. Beim Klick „Jetzt generieren“ wird ein Link erstellt, den du versenden kannst.", - "copy": "kopieren", "copy-link": "Link kopieren", "copy-link-with-text": "Link und Text kopieren", "created": "Der Link wurde erstellt!", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 4ace4e10b..fcd604fea 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -12,7 +12,6 @@ "hasAccount": "You already have an account?", "hereLogin": "Log in here", "learnMore": "Learn more …", - "oneDignity": "We gift to each other and give thanks with Gradido.", "oneDonation": "You are a gift for the community. 1000 thanks because you are with us.", "oneGratitude": "For each other, for all people, for nature." @@ -151,7 +150,6 @@ "GDD": "GDD", "gdd_per_link": { "choose-amount": "Select an amount that you would like to send via link. You can also enter a message. Click 'Generate now' to create a link that you can share.", - "copy": "copy", "copy-link": "Copy link", "copy-link-with-text": "Copy link and text", "created": "Link was created!", From dff0853bc7c55813b769e4f63431dc5d011240aa Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 14:38:53 +0200 Subject: [PATCH 012/122] remove member area from menu, if user has no elopage account --- frontend/src/components/Menu/Sidebar.vue | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/components/Menu/Sidebar.vue b/frontend/src/components/Menu/Sidebar.vue index b54eb541e..a8850ed18 100644 --- a/frontend/src/components/Menu/Sidebar.vue +++ b/frontend/src/components/Menu/Sidebar.vue @@ -27,12 +27,9 @@
- + {{ $t('navigation.members_area') }} - - {{ $t('math.exclaim') }} - From 2c07ec98ab77f3c04adcdf80e3c42d9bc132a633 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 21:06:49 +0200 Subject: [PATCH 013/122] remove the member area entry from the footer and adjust the unit tests accordingly --- frontend/src/components/ContentFooter.spec.js | 24 ++++--------------- frontend/src/components/ContentFooter.vue | 6 ----- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/frontend/src/components/ContentFooter.spec.js b/frontend/src/components/ContentFooter.spec.js index f67560e44..c578d3fac 100644 --- a/frontend/src/components/ContentFooter.spec.js +++ b/frontend/src/components/ContentFooter.spec.js @@ -97,24 +97,14 @@ describe('ContentFooter', () => { ) }) - it('has a link to the members area', () => { - expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('navigation.members_area') - }) - - it('links to the elopage', () => { - expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( - 'https://elopage.com/s/gradido/sign_in?locale=en', - ) - }) - it('links to the whitepaper', () => { - expect(wrapper.findAll('a.nav-link').at(3).attributes('href')).toEqual( + expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( 'https://docs.google.com/document/d/1kcX1guOi6tDgnFHD9tf7fB_MneKTx-0nHJxzdN8ygNs/edit?usp=sharing', ) }) it('links to the support', () => { - expect(wrapper.findAll('a.nav-link').at(4).attributes('href')).toEqual( + expect(wrapper.findAll('a.nav-link').at(3).attributes('href')).toEqual( 'https://gradido.net/en/contact/', ) }) @@ -142,20 +132,14 @@ describe('ContentFooter', () => { ) }) - it('links to the German elopage when locale is de', () => { - expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( - 'https://elopage.com/s/gradido/sign_in?locale=de', - ) - }) - it('links to the German whitepaper when locale is de', () => { - expect(wrapper.findAll('a.nav-link').at(3).attributes('href')).toEqual( + expect(wrapper.findAll('a.nav-link').at(2).attributes('href')).toEqual( 'https://docs.google.com/document/d/1jZp-DiiMPI9ZPNXmjsvOQ1BtnfDFfx8BX7CDmA8KKjY/edit?usp=sharing', ) }) it('links to the German support-page when locale is de', () => { - expect(wrapper.findAll('a.nav-link').at(4).attributes('href')).toEqual( + expect(wrapper.findAll('a.nav-link').at(3).attributes('href')).toEqual( 'https://gradido.net/de/contact/', ) }) diff --git a/frontend/src/components/ContentFooter.vue b/frontend/src/components/ContentFooter.vue index bdcb5b1a9..c563cc23d 100755 --- a/frontend/src/components/ContentFooter.vue +++ b/frontend/src/components/ContentFooter.vue @@ -34,12 +34,6 @@ {{ $t('footer.privacy_policy') }} - - {{ $t('navigation.members_area') }} - { expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.profile') }) - + }) + describe('navigation Navbar (user has an elopage account)', () => { it('has a link to the members area', () => { expect(wrapper.findAll('.nav-item').at(7).text()).toContain('navigation.members_area') expect(wrapper.findAll('.nav-item').at(7).find('a').attributes('href')).toBe( @@ -81,6 +82,18 @@ describe('Navbar', () => { expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.logout') }) }) + describe('navigation Navbar (user has no elopage account)', () => { + beforeAll(() => { + mocks.$store.state.hasElopage = false + wrapper = Wrapper() + }) + it('has first nav-item "navigation.admin_area" in navbar', () => { + expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.admin_area') + }) + it('has first nav-item "navigation.logout" in navbar', () => { + expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.logout') + }) + }) }) describe('check watch visible true', () => { beforeEach(async () => { diff --git a/frontend/src/components/Menu/Navbar.vue b/frontend/src/components/Menu/Navbar.vue index 2f26f381e..d8fc942fe 100644 --- a/frontend/src/components/Menu/Navbar.vue +++ b/frontend/src/components/Menu/Navbar.vue @@ -57,12 +57,9 @@ {{ $t('navigation.profile') }}
- + {{ $t('navigation.members_area') }} - - {{ $t('math.exclaim') }} - From 7fdba919ca93655d06844c96f9578ee324c2efac Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 22:16:17 +0200 Subject: [PATCH 015/122] fix removal of exclamation mark in navbar --- frontend/src/components/Menu/Navbar.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/components/Menu/Navbar.vue b/frontend/src/components/Menu/Navbar.vue index d8fc942fe..1c49a1aa3 100644 --- a/frontend/src/components/Menu/Navbar.vue +++ b/frontend/src/components/Menu/Navbar.vue @@ -60,6 +60,9 @@ {{ $t('navigation.members_area') }} + + {{ $t('math.exclaim') }} + From 67453c8a27ec60ee677e724c6a671aef09d76246 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 27 Jul 2022 22:16:53 +0200 Subject: [PATCH 016/122] remove the member area entry from sidebar menu and adjust the unit tests accordingly --- frontend/src/components/Menu/Sidebar.spec.js | 28 +++++++++++++++----- frontend/src/components/Menu/Sidebar.vue | 10 ++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/Menu/Sidebar.spec.js b/frontend/src/components/Menu/Sidebar.spec.js index f6051c733..dd9511562 100644 --- a/frontend/src/components/Menu/Sidebar.spec.js +++ b/frontend/src/components/Menu/Sidebar.spec.js @@ -31,11 +31,7 @@ describe('Sidebar', () => { expect(wrapper.find('div#component-sidebar').exists()).toBeTruthy() }) - describe('navigation Navbar', () => { - it('has seven b-nav-item in the navbar', () => { - expect(wrapper.findAll('.nav-item')).toHaveLength(8) - }) - + describe('navigation Navbar (general elements)', () => { it('has first nav-item "navigation.overview" in navbar', () => { expect(wrapper.findAll('.nav-item').at(0).text()).toEqual('navigation.overview') }) @@ -55,7 +51,12 @@ describe('Sidebar', () => { it('has first nav-item "navigation.profile" in navbar', () => { expect(wrapper.findAll('.nav-item').at(4).text()).toEqual('navigation.profile') }) - + }) + // ---- + describe('navigation Navbar (user has an elopage account)', () => { + it('has seven b-nav-item in the navbar', () => { + expect(wrapper.findAll('.nav-item')).toHaveLength(8) + }) it('has a link to the members area', () => { expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.members_area') expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('#') @@ -69,5 +70,20 @@ describe('Sidebar', () => { expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.logout') }) }) + describe('navigation Navbar (user has no elopage account)', () => { + beforeAll(() => { + mocks.$store.state.hasElopage = false + wrapper = Wrapper() + }) + it('has six b-nav-item in the navbar', () => { + expect(wrapper.findAll('.nav-item')).toHaveLength(7) + }) + it('has first nav-item "navigation.admin_area" in navbar', () => { + expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.admin_area') + }) + it('has first nav-item "navigation.logout" in navbar', () => { + expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.logout') + }) + }) }) }) diff --git a/frontend/src/components/Menu/Sidebar.vue b/frontend/src/components/Menu/Sidebar.vue index a8850ed18..0cdab31c3 100644 --- a/frontend/src/components/Menu/Sidebar.vue +++ b/frontend/src/components/Menu/Sidebar.vue @@ -27,9 +27,17 @@

- + {{ $t('navigation.members_area') }} + + {{ $t('math.exclaim') }} + From bb120b8b82f9c25f5caa39cdd81064c1a36e4dc9 Mon Sep 17 00:00:00 2001 From: mahula Date: Thu, 28 Jul 2022 08:39:31 +0200 Subject: [PATCH 017/122] improve unit test structure for better readability --- frontend/src/components/Menu/Navbar.spec.js | 11 +++++++++++ frontend/src/components/Menu/Sidebar.spec.js | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Menu/Navbar.spec.js b/frontend/src/components/Menu/Navbar.spec.js index ef39fdc0e..109190e39 100644 --- a/frontend/src/components/Menu/Navbar.spec.js +++ b/frontend/src/components/Menu/Navbar.spec.js @@ -43,9 +43,11 @@ describe('Navbar', () => { it('has .navbar-brand in the navbar', () => { expect(wrapper.find('.navbar-brand').exists()).toBeTruthy() }) + it('has b-navbar-toggle in the navbar', () => { expect(wrapper.find('.navbar-toggler').exists()).toBeTruthy() }) + it('has ten b-nav-item in the navbar', () => { expect(wrapper.findAll('.nav-item')).toHaveLength(10) }) @@ -57,16 +59,20 @@ describe('Navbar', () => { it('has first nav-item "navigation.overview" in navbar', () => { expect(wrapper.findAll('.nav-item').at(3).text()).toEqual('navigation.overview') }) + it('has first nav-item "navigation.send" in navbar', () => { expect(wrapper.findAll('.nav-item').at(4).text()).toEqual('navigation.send') }) + it('has first nav-item "navigation.transactions" in navbar', () => { expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.transactions') }) + it('has first nav-item "navigation.profile" in navbar', () => { expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.profile') }) }) + describe('navigation Navbar (user has an elopage account)', () => { it('has a link to the members area', () => { expect(wrapper.findAll('.nav-item').at(7).text()).toContain('navigation.members_area') @@ -78,23 +84,28 @@ describe('Navbar', () => { it('has first nav-item "navigation.admin_area" in navbar', () => { expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.admin_area') }) + it('has first nav-item "navigation.logout" in navbar', () => { expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.logout') }) }) + describe('navigation Navbar (user has no elopage account)', () => { beforeAll(() => { mocks.$store.state.hasElopage = false wrapper = Wrapper() }) + it('has first nav-item "navigation.admin_area" in navbar', () => { expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.admin_area') }) + it('has first nav-item "navigation.logout" in navbar', () => { expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.logout') }) }) }) + describe('check watch visible true', () => { beforeEach(async () => { await wrapper.setProps({ visible: true }) diff --git a/frontend/src/components/Menu/Sidebar.spec.js b/frontend/src/components/Menu/Sidebar.spec.js index dd9511562..7be68363b 100644 --- a/frontend/src/components/Menu/Sidebar.spec.js +++ b/frontend/src/components/Menu/Sidebar.spec.js @@ -27,6 +27,7 @@ describe('Sidebar', () => { beforeEach(() => { wrapper = Wrapper() }) + it('renders the component', () => { expect(wrapper.find('div#component-sidebar').exists()).toBeTruthy() }) @@ -52,11 +53,12 @@ describe('Sidebar', () => { expect(wrapper.findAll('.nav-item').at(4).text()).toEqual('navigation.profile') }) }) - // ---- + describe('navigation Navbar (user has an elopage account)', () => { it('has seven b-nav-item in the navbar', () => { expect(wrapper.findAll('.nav-item')).toHaveLength(8) }) + it('has a link to the members area', () => { expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.members_area') expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('#') @@ -70,17 +72,21 @@ describe('Sidebar', () => { expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.logout') }) }) + describe('navigation Navbar (user has no elopage account)', () => { beforeAll(() => { mocks.$store.state.hasElopage = false wrapper = Wrapper() }) + it('has six b-nav-item in the navbar', () => { expect(wrapper.findAll('.nav-item')).toHaveLength(7) }) + it('has first nav-item "navigation.admin_area" in navbar', () => { expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.admin_area') }) + it('has first nav-item "navigation.logout" in navbar', () => { expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.logout') }) From a41385141902b7611c7fbde289d68cc3d2fe4c6b Mon Sep 17 00:00:00 2001 From: mahula Date: Thu, 28 Jul 2022 08:50:26 +0200 Subject: [PATCH 018/122] specify the wording of the unit tests regarding the number of nav items --- frontend/src/components/Menu/Sidebar.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Menu/Sidebar.spec.js b/frontend/src/components/Menu/Sidebar.spec.js index 7be68363b..1593a79a8 100644 --- a/frontend/src/components/Menu/Sidebar.spec.js +++ b/frontend/src/components/Menu/Sidebar.spec.js @@ -55,7 +55,7 @@ describe('Sidebar', () => { }) describe('navigation Navbar (user has an elopage account)', () => { - it('has seven b-nav-item in the navbar', () => { + it('has eight b-nav-item in the navbar', () => { expect(wrapper.findAll('.nav-item')).toHaveLength(8) }) @@ -79,7 +79,7 @@ describe('Sidebar', () => { wrapper = Wrapper() }) - it('has six b-nav-item in the navbar', () => { + it('has seven b-nav-item in the navbar', () => { expect(wrapper.findAll('.nav-item')).toHaveLength(7) }) From 6f081c4cc941ea6a242d17b2986d8cd98f6513da Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Jul 2022 11:12:17 +0200 Subject: [PATCH 019/122] fix: Use Inner Join for Contribution and User --- .../graphql/resolver/ContributionResolver.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index a22715fb4..3307252e4 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -3,7 +3,7 @@ import { Context, getUser } from '@/server/context' import { backendLogger as logger } from '@/server/logger' import { Contribution as dbContribution } from '@entity/Contribution' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' -import { FindOperator, IsNull } from '@dbTools/typeorm' +import { FindOperator, IsNull, getConnection } from '@dbTools/typeorm' import ContributionArgs from '@arg/ContributionArgs' import Paginated from '@arg/Paginated' import { Order } from '@enum/Order' @@ -106,14 +106,15 @@ export class ContributionResolver { @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, ): Promise { - const [dbContributions, count] = await dbContribution.findAndCount({ - relations: ['user'], - order: { - createdAt: order, - }, - skip: (currentPage - 1) * pageSize, - take: pageSize, - }) + const [dbContributions, count] = await getConnection() + .createQueryBuilder() + .select('c') + .from(dbContribution, 'c') + .innerJoinAndSelect('c.user', 'u') + .orderBy('c.createdAt', order) + .limit(pageSize) + .offset((currentPage - 1) * pageSize) + .getManyAndCount() return new ContributionListResult( count, dbContributions.map( From 2d80c7029eae75fbda6c925cc8b0e5b54ef41e17 Mon Sep 17 00:00:00 2001 From: mahula Date: Thu, 28 Jul 2022 13:06:42 +0200 Subject: [PATCH 020/122] including validation date infos to link copying after transaction link creation - adapt transaction link creation mutation to get the validation date - add validation date and text to link copy - utilize mixins to avoid code doubling --- frontend/src/components/ClipboardCopy.vue | 42 ++----------------- .../GddSend/TransactionResultLink.vue | 4 +- frontend/src/graphql/mutations.js | 3 ++ frontend/src/pages/Send.vue | 13 ++++-- 4 files changed, 19 insertions(+), 43 deletions(-) diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue index efddf8ab3..66e47a264 100644 --- a/frontend/src/components/ClipboardCopy.vue +++ b/frontend/src/components/ClipboardCopy.vue @@ -6,7 +6,7 @@ {{ $t('gdd_per_link.copy-link-with-text') }} - + {{ $t('gdd_per_link.copy-link') }} @@ -21,46 +21,10 @@