diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3c486cb20..4cc28bf20 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -527,7 +527,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 76 + min_coverage: 78 token: ${{ github.token }} ########################################################################## diff --git a/admin/yarn.lock b/admin/yarn.lock index 7507f2559..5d8275b1a 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -4141,9 +4141,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001271: - version "1.0.30001354" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz" - integrity sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg== + version "1.0.30001442" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz" + integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow== capture-exit@^2.0.0: version "2.0.0" diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 4779e5b21..d010b4ab0 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0058-add_communities_table', + DB_VERSION: '0059-add_hide_amount_to_users', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info @@ -70,11 +70,13 @@ const email = { EMAIL: process.env.EMAIL === 'true' || false, EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' || false, EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER || 'stage1@gradido.net', - EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email', + EMAIL_USERNAME: process.env.EMAIL_USERNAME || '', EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net', - EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx', - EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com', - EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587', + EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || '', + EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'mailserver', + EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '1025', + // eslint-disable-next-line no-unneeded-ternary + EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true, EMAIL_LINK_VERIFICATION: process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}', EMAIL_LINK_SETPASSWORD: diff --git a/backend/src/emails/sendEmailTranslated.ts b/backend/src/emails/sendEmailTranslated.ts index 69008c00e..5652b3424 100644 --- a/backend/src/emails/sendEmailTranslated.ts +++ b/backend/src/emails/sendEmailTranslated.ts @@ -41,7 +41,7 @@ export const sendEmailTranslated = async (params: { host: CONFIG.EMAIL_SMTP_URL, port: Number(CONFIG.EMAIL_SMTP_PORT), secure: false, // true for 465, false for other ports - requireTLS: true, + requireTLS: CONFIG.EMAIL_TLS, auth: { user: CONFIG.EMAIL_USERNAME, pass: CONFIG.EMAIL_PASSWORD, diff --git a/backend/src/graphql/arg/UpdateUserInfosArgs.ts b/backend/src/graphql/arg/UpdateUserInfosArgs.ts index 81c07a329..b45539487 100644 --- a/backend/src/graphql/arg/UpdateUserInfosArgs.ts +++ b/backend/src/graphql/arg/UpdateUserInfosArgs.ts @@ -19,4 +19,10 @@ export default class UpdateUserInfosArgs { @Field({ nullable: true }) passwordNew?: string + + @Field({ nullable: true }) + hideAmountGDD?: boolean + + @Field({ nullable: true }) + hideAmountGDT?: boolean } diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index cc52ff1f1..6e353f6a7 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -27,6 +27,8 @@ export class User { this.klickTipp = null this.hasElopage = null this.creation = creation + this.hideAmountGDD = user.hideAmountGDD + this.hideAmountGDT = user.hideAmountGDT } @Field(() => Number) @@ -72,6 +74,12 @@ export class User { @Field(() => String) language: string + @Field(() => Boolean) + hideAmountGDD: boolean + + @Field(() => Boolean) + hideAmountGDT: boolean + // This is not the users publisherId, but the one of the users who recommend him @Field(() => Number, { nullable: true }) publisherId: number | null diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index a0016e8f2..26f9cd656 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -32,7 +32,7 @@ export class BalanceResolver { const lastTransaction = context.lastTransaction ? context.lastTransaction - : await dbTransaction.findOne({ userId: user.id }, { order: { balanceDate: 'DESC' } }) + : await dbTransaction.findOne({ userId: user.id }, { order: { id: 'DESC' } }) logger.debug(`lastTransaction=${lastTransaction}`) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index cf2d55d94..9a7fb76f2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -1146,13 +1146,21 @@ describe('ContributionResolver', () => { const now = new Date() beforeAll(async () => { - creation = await creationFactory(testEnv, { - email: 'peter@lustig.de', - amount: 400, - memo: 'Herzlich Willkommen bei Gradido!', - creationDate: contributionDateFormatter( - new Date(now.getFullYear(), now.getMonth() - 1, 1), - ), + await mutate({ + mutation: adminCreateContribution, + variables: { + email: 'peter@lustig.de', + amount: 400, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: contributionDateFormatter( + new Date(now.getFullYear(), now.getMonth() - 1, 1), + ), + }, + }) + creation = await Contribution.findOneOrFail({ + where: { + memo: 'Herzlich Willkommen bei Gradido!', + }, }) }) @@ -1879,6 +1887,10 @@ describe('ContributionResolver', () => { new Date(now.getFullYear(), now.getMonth() - 2, 1), ), }) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) }) it('returns true', async () => { @@ -1935,6 +1947,23 @@ describe('ContributionResolver', () => { }), ) }) + + describe('confirm same contribution again', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: confirmContribution, + variables: { + id: creation ? creation.id : -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Contribution already confirmd.')], + }), + ) + }) + }) }) describe('confirm two creations one after the other quickly', () => { @@ -1959,6 +1988,10 @@ describe('ContributionResolver', () => { new Date(now.getFullYear(), now.getMonth() - 2, 1), ), }) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) }) it('throws no error for the second confirmation', async () => { diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 2587aab61..3794546e2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -553,108 +553,116 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const clientTimezoneOffset = getClientTimezoneOffset(context) - const contribution = await DbContribution.findOne(id) - if (!contribution) { - logger.error(`Contribution not found for given id: ${id}`) - throw new Error('Contribution not found to given id.') - } - const moderatorUser = getUser(context) - if (moderatorUser.id === contribution.userId) { - logger.error('Moderator can not confirm own contribution') - throw new Error('Moderator can not confirm own contribution') - } - const user = await DbUser.findOneOrFail( - { id: contribution.userId }, - { withDeleted: true, relations: ['emailContact'] }, - ) - if (user.deletedAt) { - logger.error('This user was deleted. Cannot confirm a contribution.') - throw new Error('This user was deleted. Cannot confirm a contribution.') - } - const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false) - validateContribution( - creations, - contribution.amount, - contribution.contributionDate, - clientTimezoneOffset, - ) - // acquire lock const releaseLock = await TRANSACTIONS_LOCK.acquire() - const receivedCallDate = new Date() - const queryRunner = getConnection().createQueryRunner() - await queryRunner.connect() - await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED') try { - const lastTransaction = await queryRunner.manager - .createQueryBuilder() - .select('transaction') - .from(DbTransaction, 'transaction') - .where('transaction.userId = :id', { id: contribution.userId }) - .orderBy('transaction.id', 'DESC') - .getOne() - logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined') - - let newBalance = new Decimal(0) - let decay: Decay | null = null - if (lastTransaction) { - decay = calculateDecay( - lastTransaction.balance, - lastTransaction.balanceDate, - receivedCallDate, - ) - newBalance = decay.balance + const clientTimezoneOffset = getClientTimezoneOffset(context) + const contribution = await DbContribution.findOne(id) + if (!contribution) { + logger.error(`Contribution not found for given id: ${id}`) + throw new Error('Contribution not found to given id.') } - newBalance = newBalance.add(contribution.amount.toString()) + if (contribution.confirmedAt) { + logger.error(`Contribution already confirmd: ${id}`) + throw new Error('Contribution already confirmd.') + } + const moderatorUser = getUser(context) + if (moderatorUser.id === contribution.userId) { + logger.error('Moderator can not confirm own contribution') + throw new Error('Moderator can not confirm own contribution') + } + const user = await DbUser.findOneOrFail( + { id: contribution.userId }, + { withDeleted: true, relations: ['emailContact'] }, + ) + if (user.deletedAt) { + logger.error('This user was deleted. Cannot confirm a contribution.') + throw new Error('This user was deleted. Cannot confirm a contribution.') + } + const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false) + validateContribution( + creations, + contribution.amount, + contribution.contributionDate, + clientTimezoneOffset, + ) - const transaction = new DbTransaction() - transaction.typeId = TransactionTypeId.CREATION - transaction.memo = contribution.memo - transaction.userId = contribution.userId - transaction.previous = lastTransaction ? lastTransaction.id : null - transaction.amount = contribution.amount - transaction.creationDate = contribution.contributionDate - transaction.balance = newBalance - transaction.balanceDate = receivedCallDate - transaction.decay = decay ? decay.decay : new Decimal(0) - transaction.decayStart = decay ? decay.start : null - await queryRunner.manager.insert(DbTransaction, transaction) + const receivedCallDate = new Date() + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED') + try { + const lastTransaction = await queryRunner.manager + .createQueryBuilder() + .select('transaction') + .from(DbTransaction, 'transaction') + .where('transaction.userId = :id', { id: contribution.userId }) + .orderBy('transaction.id', 'DESC') + .getOne() + logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined') - contribution.confirmedAt = receivedCallDate - contribution.confirmedBy = moderatorUser.id - contribution.transactionId = transaction.id - contribution.contributionStatus = ContributionStatus.CONFIRMED - await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution) + let newBalance = new Decimal(0) + let decay: Decay | null = null + if (lastTransaction) { + decay = calculateDecay( + lastTransaction.balance, + lastTransaction.balanceDate, + receivedCallDate, + ) + newBalance = decay.balance + } + newBalance = newBalance.add(contribution.amount.toString()) - await queryRunner.commitTransaction() - logger.info('creation commited successfuly.') - sendContributionConfirmedEmail({ - firstName: user.firstName, - lastName: user.lastName, - email: user.emailContact.email, - language: user.language, - senderFirstName: moderatorUser.firstName, - senderLastName: moderatorUser.lastName, - contributionMemo: contribution.memo, - contributionAmount: contribution.amount, - }) - } catch (e) { - await queryRunner.rollbackTransaction() - logger.error('Creation was not successful', e) - throw new Error('Creation was not successful.') + const transaction = new DbTransaction() + transaction.typeId = TransactionTypeId.CREATION + transaction.memo = contribution.memo + transaction.userId = contribution.userId + transaction.previous = lastTransaction ? lastTransaction.id : null + transaction.amount = contribution.amount + transaction.creationDate = contribution.contributionDate + transaction.balance = newBalance + transaction.balanceDate = receivedCallDate + transaction.decay = decay ? decay.decay : new Decimal(0) + transaction.decayStart = decay ? decay.start : null + await queryRunner.manager.insert(DbTransaction, transaction) + + contribution.confirmedAt = receivedCallDate + contribution.confirmedBy = moderatorUser.id + contribution.transactionId = transaction.id + contribution.contributionStatus = ContributionStatus.CONFIRMED + await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution) + + await queryRunner.commitTransaction() + logger.info('creation commited successfuly.') + sendContributionConfirmedEmail({ + firstName: user.firstName, + lastName: user.lastName, + email: user.emailContact.email, + language: user.language, + senderFirstName: moderatorUser.firstName, + senderLastName: moderatorUser.lastName, + contributionMemo: contribution.memo, + contributionAmount: contribution.amount, + }) + } catch (e) { + await queryRunner.rollbackTransaction() + logger.error('Creation was not successful', e) + throw new Error('Creation was not successful.') + } finally { + await queryRunner.release() + } + + const event = new Event() + const eventContributionConfirm = new EventContributionConfirm() + eventContributionConfirm.userId = user.id + eventContributionConfirm.amount = contribution.amount + eventContributionConfirm.contributionId = contribution.id + await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) } finally { - await queryRunner.release() releaseLock() } - const event = new Event() - const eventContributionConfirm = new EventContributionConfirm() - eventContributionConfirm.userId = user.id - eventContributionConfirm.amount = contribution.amount - eventContributionConfirm.contributionId = contribution.id - await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) return true } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 0ac5b382e..33914583e 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -211,7 +211,7 @@ export class TransactionResolver { // find current balance const lastTransaction = await dbTransaction.findOne( { userId: user.id }, - { order: { balanceDate: 'DESC' }, relations: ['contribution'] }, + { order: { id: 'DESC' }, relations: ['contribution'] }, ) logger.debug(`lastTransaction=${lastTransaction}`) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b70dd1522..073bad04d 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -63,6 +63,7 @@ jest.mock('@/emails/sendEmailVariants', () => { }) /* + jest.mock('@/apis/KlicktippController', () => { return { __esModule: true, @@ -132,6 +133,8 @@ describe('UserResolver', () => { { id: expect.any(Number), gradidoID: expect.any(String), + hideAmountGDD: expect.any(Boolean), + hideAmountGDT: expect.any(Boolean), alias: null, emailContact: expect.any(UserContact), // 'peter@lustig.de', emailId: expect.any(Number), diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4b4744b67..ed1bf3f47 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -567,7 +567,15 @@ export class UserResolver { @Mutation(() => Boolean) async updateUserInfos( @Args() - { firstName, lastName, language, password, passwordNew }: UpdateUserInfosArgs, + { + firstName, + lastName, + language, + password, + passwordNew, + hideAmountGDD, + hideAmountGDT, + }: UpdateUserInfosArgs, @Ctx() context: Context, ): Promise { logger.info(`updateUserInfos(${firstName}, ${lastName}, ${language}, ***, ***)...`) @@ -609,6 +617,15 @@ export class UserResolver { userEntity.password = encryptPassword(userEntity, passwordNew) } + // Save hideAmountGDD value + if (hideAmountGDD !== undefined) { + userEntity.hideAmountGDD = hideAmountGDD + } + // Save hideAmountGDT value + if (hideAmountGDT !== undefined) { + userEntity.hideAmountGDT = hideAmountGDT + } + const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 36e481519..09bf981bb 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -1,8 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { backendLogger as logger } from '@/server/logger' -import { login, adminCreateContribution, confirmContribution } from '@/seeds/graphql/mutations' +import { login, createContribution, confirmContribution } from '@/seeds/graphql/mutations' import { CreationInterface } from '@/seeds/creation/CreationInterface' import { ApolloServerTestClient } from 'apollo-server-testing' import { Transaction } from '@entity/Transaction' @@ -19,43 +18,27 @@ export const creationFactory = async ( creation: CreationInterface, ): Promise => { const { mutate } = client - logger.trace('creationFactory...') - await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) - logger.trace('creationFactory... after login') - // TODO it would be nice to have this mutation return the id - await mutate({ mutation: adminCreateContribution, variables: { ...creation } }) - logger.trace('creationFactory... after adminCreateContribution') + await mutate({ mutation: login, variables: { email: creation.email, password: 'Aa12345_' } }) - const user = await findUserByEmail(creation.email) // userContact.user + const { + data: { createContribution: contribution }, + } = await mutate({ mutation: createContribution, variables: { ...creation } }) - const pendingCreation = await Contribution.findOneOrFail({ - where: { userId: user.id, amount: creation.amount }, - order: { createdAt: 'DESC' }, - }) - logger.trace( - 'creationFactory... after Contribution.findOneOrFail pendingCreation=', - pendingCreation, - ) if (creation.confirmed) { - logger.trace('creationFactory... creation.confirmed=', creation.confirmed) - await mutate({ mutation: confirmContribution, variables: { id: pendingCreation.id } }) - logger.trace('creationFactory... after confirmContribution') - const confirmedCreation = await Contribution.findOneOrFail({ id: pendingCreation.id }) - logger.trace( - 'creationFactory... after Contribution.findOneOrFail confirmedCreation=', - confirmedCreation, - ) + const user = await findUserByEmail(creation.email) // userContact.user + + await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) + await mutate({ mutation: confirmContribution, variables: { id: contribution.id } }) + const confirmedContribution = await Contribution.findOneOrFail({ id: contribution.id }) if (creation.moveCreationDate) { - logger.trace('creationFactory... creation.moveCreationDate=', creation.moveCreationDate) const transaction = await Transaction.findOneOrFail({ where: { userId: user.id, creationDate: new Date(creation.creationDate) }, order: { balanceDate: 'DESC' }, }) - logger.trace('creationFactory... after Transaction.findOneOrFail transaction=', transaction) if (transaction.decay.equals(0) && transaction.creationDate) { - confirmedCreation.contributionDate = new Date( + confirmedContribution.contributionDate = new Date( nMonthsBefore(transaction.creationDate, creation.moveCreationDate), ) transaction.creationDate = new Date( @@ -64,17 +47,11 @@ export const creationFactory = async ( transaction.balanceDate = new Date( nMonthsBefore(transaction.balanceDate, creation.moveCreationDate), ) - logger.trace('creationFactory... before transaction.save transaction=', transaction) await transaction.save() - logger.trace( - 'creationFactory... before confirmedCreation.save confirmedCreation=', - confirmedCreation, - ) - await confirmedCreation.save() + await confirmedContribution.save() } } } else { - logger.trace('creationFactory... pendingCreation=', pendingCreation) - return pendingCreation + return contribution } } diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index e5dc90a0e..2b4ed6656 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -31,6 +31,8 @@ export const updateUserInfos = gql` $password: String $passwordNew: String $locale: String + $hideAmountGDD: Boolean + $hideAmountGDT: Boolean ) { updateUserInfos( firstName: $firstName @@ -38,6 +40,8 @@ export const updateUserInfos = gql` password: $password passwordNew: $passwordNew language: $locale + hideAmountGDD: $hideAmountGDD + hideAmountGDT: $hideAmountGDT ) } ` diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index 98279db15..4d04be1b8 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -18,6 +18,8 @@ const communityDbUser: dbUser = { lastName: 'Akademie', deletedAt: null, password: BigInt(0), + hideAmountGDD: false, + hideAmountGDT: false, // emailHash: Buffer.from(''), createdAt: new Date(), // emailChecked: false, diff --git a/backend/yarn.lock b/backend/yarn.lock index 82bcd6b1f..264cbd409 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1913,9 +1913,9 @@ camelcase@^6.2.0: integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== caniuse-lite@^1.0.30001264: - version "1.0.30001418" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz" - integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg== + version "1.0.30001442" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz" + integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow== chacha20-universal@^1.0.4: version "1.0.4" diff --git a/database/entity/0057-clear_old_password_junk/UserContact.ts b/database/entity/0057-clear_old_password_junk/UserContact.ts index c101fba4c..dd06d3bbc 100644 --- a/database/entity/0057-clear_old_password_junk/UserContact.ts +++ b/database/entity/0057-clear_old_password_junk/UserContact.ts @@ -6,7 +6,7 @@ import { DeleteDateColumn, OneToOne, } from 'typeorm' -import { User } from './User' +import { User } from '../User' @Entity('user_contacts', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class UserContact extends BaseEntity { diff --git a/database/entity/0059-add_hide_amount_to_users/User.ts b/database/entity/0059-add_hide_amount_to_users/User.ts new file mode 100644 index 000000000..ba9e5fc49 --- /dev/null +++ b/database/entity/0059-add_hide_amount_to_users/User.ts @@ -0,0 +1,118 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, + OneToOne, +} from 'typeorm' +import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' +import { UserContact } from '../UserContact' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ + name: 'gradido_id', + length: 36, + nullable: false, + collation: 'utf8mb4_unicode_ci', + }) + gradidoID: string + + @Column({ + name: 'alias', + length: 20, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + alias: string + + @OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user) + @JoinColumn({ name: 'email_id' }) + emailContact: UserContact + + @Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null }) + emailId: number | null + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @DeleteDateColumn({ name: 'deleted_at', nullable: true }) + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ + name: 'password_encryption_type', + type: 'int', + unsigned: true, + nullable: false, + default: 0, + }) + passwordEncryptionType: number + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ type: 'bool', default: false }) + hideAmountGDD: boolean + + @Column({ type: 'bool', default: false }) + hideAmountGDT: boolean + + @Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null }) + isAdmin: Date | null + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ + name: 'contribution_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + contributionLinkId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] + + @OneToMany(() => UserContact, (userContact: UserContact) => userContact.user) + @JoinColumn({ name: 'user_id' }) + userContacts?: UserContact[] +} diff --git a/database/entity/User.ts b/database/entity/User.ts index 5cffc688e..fbb65204c 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0057-clear_old_password_junk/User' +export { User } from './0059-add_hide_amount_to_users/User' diff --git a/database/migrations/0059-add_hide_amount_to_users.ts b/database/migrations/0059-add_hide_amount_to_users.ts new file mode 100644 index 000000000..d06e00559 --- /dev/null +++ b/database/migrations/0059-add_hide_amount_to_users.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE users ADD COLUMN hideAmountGDD bool DEFAULT false;') + await queryFn('ALTER TABLE users ADD COLUMN hideAmountGDT bool DEFAULT false;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE users DROP COLUMN hideAmountGDD;') + await queryFn('ALTER TABLE users DROP COLUMN hideAmountGDT;') +} diff --git a/docker-compose.override.yml b/docker-compose.override.yml index fe2f68a8d..c69c68b50 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -112,6 +112,17 @@ services: volumes: - /sessions + ######################################################## + # MAILSERVER TO FAKE SMTP ############################## + ######################################################## + mailserver: + image: maildev/maildev + ports: + - 1080:1080 + - 1025:1025 + networks: + - external-net + volumes: frontend_node_modules: admin_node_modules: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 790bd468d..84647ef03 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -81,6 +81,17 @@ services: nginx: image: gradido/nginx:test + ######################################################## + # MAILSERVER TO FAKE SMTP ############################## + ######################################################## + mailserver: + image: maildev/maildev + ports: + - 1080:1080 + - 1025:1025 + networks: + - external-net + networks: external-net: internal-net: diff --git a/frontend/src/components/GddSend/TransactionConfirmationLink.vue b/frontend/src/components/GddSend/TransactionConfirmationLink.vue index 0fcb94459..91fea3486 100644 --- a/frontend/src/components/GddSend/TransactionConfirmationLink.vue +++ b/frontend/src/components/GddSend/TransactionConfirmationLink.vue @@ -37,7 +37,7 @@ - {{ $t('back') }} + {{ $t('back') }} - {{ $t('back') }} + {{ $t('back') }}
- + {{ $t('form.close') }}
diff --git a/frontend/src/components/GddSend/TransactionResultSendSuccess.vue b/frontend/src/components/GddSend/TransactionResultSendSuccess.vue index 36ce3f4e6..b57196db4 100644 --- a/frontend/src/components/GddSend/TransactionResultSendSuccess.vue +++ b/frontend/src/components/GddSend/TransactionResultSendSuccess.vue @@ -6,7 +6,7 @@ {{ $t('form.send_transaction_success') }}
- {{ $t('form.close') }} + {{ $t('form.close') }}
diff --git a/frontend/src/components/Inputs/InputEmail.vue b/frontend/src/components/Inputs/InputEmail.vue index 1532e1edd..3a7ed4f4e 100644 --- a/frontend/src/components/Inputs/InputEmail.vue +++ b/frontend/src/components/Inputs/InputEmail.vue @@ -40,11 +40,11 @@ export default { } }, }, - name: { type: String, default: 'Email' }, - label: { type: String, default: 'Email' }, - placeholder: { type: String, default: 'Email' }, - value: { required: true, type: String, default: '' }, - disabled: { required: false, type: Boolean, default: false }, + name: { type: String, required: true }, + label: { type: String, required: true }, + placeholder: { type: String, required: true }, + value: { type: String, required: true }, + disabled: { type: Boolean, required: false, default: false }, }, data() { return { diff --git a/frontend/src/components/Menu/Navbar.vue b/frontend/src/components/Menu/Navbar.vue index 7efb0d2ef..309e3ffd5 100644 --- a/frontend/src/components/Menu/Navbar.vue +++ b/frontend/src/components/Menu/Navbar.vue @@ -12,14 +12,20 @@
- - + +
- +
+
diff --git a/frontend/src/components/Template/ContentHeader/GddAmount.spec.js b/frontend/src/components/Template/ContentHeader/GddAmount.spec.js index fc7d65cc4..26967507b 100644 --- a/frontend/src/components/Template/ContentHeader/GddAmount.spec.js +++ b/frontend/src/components/Template/ContentHeader/GddAmount.spec.js @@ -1,20 +1,31 @@ import { mount } from '@vue/test-utils' import GddAmount from './GddAmount' +import { updateUserInfos } from '@/graphql/mutations' +import flushPromises from 'flush-promises' + +import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup' const localVue = global.localVue +const mockAPICall = jest.fn() +const storeCommitMock = jest.fn() + const state = { - language: 'en', + hideAmountGDD: false, } const mocks = { $store: { state, + commit: storeCommitMock, }, $i18n: { locale: 'en', }, $t: jest.fn((t) => t), + $apollo: { + mutate: mockAPICall, + }, } const propsData = { @@ -38,5 +49,89 @@ describe('GddAmount', () => { it('renders the component gdd-amount', () => { expect(wrapper.find('div.gdd-amount').exists()).toBe(true) }) + + describe('API throws exception', () => { + beforeEach(async () => { + mockAPICall.mockRejectedValue({ + message: 'Ouch', + }) + jest.clearAllMocks() + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Ouch') + }) + }) + + describe('API call successful', () => { + beforeEach(async () => { + mockAPICall.mockResolvedValue({ + data: { + updateUserInfos: { + validValues: 1, + }, + }, + }) + jest.clearAllMocks() + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('calls the API', () => { + expect(mockAPICall).toBeCalledWith( + expect.objectContaining({ + mutation: updateUserInfos, + variables: { + hideAmountGDD: true, + }, + }), + ) + }) + + it('commits hideAmountGDD to store', () => { + expect(storeCommitMock).toBeCalledWith('hideAmountGDD', true) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('settings.showAmountGDD') + }) + }) + }) + + describe('second call to API', () => { + beforeEach(async () => { + mockAPICall.mockResolvedValue({ + data: { + updateUserInfos: { + validValues: 1, + }, + }, + }) + jest.clearAllMocks() + wrapper.vm.$store.state.hideAmountGDD = true + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('calls the API', () => { + expect(mockAPICall).toBeCalledWith( + expect.objectContaining({ + mutation: updateUserInfos, + variables: { + hideAmountGDD: false, + }, + }), + ) + }) + + it('commits hideAmountGDD to store', () => { + expect(storeCommitMock).toBeCalledWith('hideAmountGDD', false) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('settings.hideAmountGDD') + }) }) }) diff --git a/frontend/src/components/Template/ContentHeader/GddAmount.vue b/frontend/src/components/Template/ContentHeader/GddAmount.vue index f8d04e883..1c774f3d7 100644 --- a/frontend/src/components/Template/ContentHeader/GddAmount.vue +++ b/frontend/src/components/Template/ContentHeader/GddAmount.vue @@ -39,7 +39,7 @@ @@ -47,6 +47,8 @@
diff --git a/frontend/src/components/Template/ContentHeader/GdtAmount.spec.js b/frontend/src/components/Template/ContentHeader/GdtAmount.spec.js index 30ce85817..58744cbfe 100644 --- a/frontend/src/components/Template/ContentHeader/GdtAmount.spec.js +++ b/frontend/src/components/Template/ContentHeader/GdtAmount.spec.js @@ -1,19 +1,30 @@ import { mount } from '@vue/test-utils' import GdtAmount from './GdtAmount' +import { updateUserInfos } from '@/graphql/mutations' +import flushPromises from 'flush-promises' + +import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup' const localVue = global.localVue +const mockAPICall = jest.fn() +const storeCommitMock = jest.fn() + const state = { - language: 'en', + hideAmountGDT: false, } const mocks = { $store: { state, + commit: storeCommitMock, }, $i18n: { locale: 'en', }, + $apollo: { + mutate: mockAPICall, + }, $t: jest.fn((t) => t), $n: jest.fn((n) => n), } @@ -39,5 +50,89 @@ describe('GdtAmount', () => { it('renders the component gdt-amount', () => { expect(wrapper.find('div.gdt-amount').exists()).toBe(true) }) + + describe('API throws exception', () => { + beforeEach(async () => { + mockAPICall.mockRejectedValue({ + message: 'Ouch', + }) + jest.clearAllMocks() + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Ouch') + }) + }) + + describe('API call successful', () => { + beforeEach(async () => { + mockAPICall.mockResolvedValue({ + data: { + updateUserInfos: { + validValues: 1, + }, + }, + }) + jest.clearAllMocks() + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('calls the API', () => { + expect(mockAPICall).toBeCalledWith( + expect.objectContaining({ + mutation: updateUserInfos, + variables: { + hideAmountGDT: true, + }, + }), + ) + }) + + it('commits hideAmountGDT to store', () => { + expect(storeCommitMock).toBeCalledWith('hideAmountGDT', true) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('settings.showAmountGDT') + }) + }) + }) + + describe('second call to API', () => { + beforeEach(async () => { + mockAPICall.mockResolvedValue({ + data: { + updateUserInfos: { + validValues: 1, + }, + }, + }) + jest.clearAllMocks() + wrapper.vm.$store.state.hideAmountGDT = true + await wrapper.find('div.border-left svg').trigger('click') + await flushPromises() + }) + + it('calls the API', () => { + expect(mockAPICall).toBeCalledWith( + expect.objectContaining({ + mutation: updateUserInfos, + variables: { + hideAmountGDT: false, + }, + }), + ) + }) + + it('commits hideAmountGDT to store', () => { + expect(storeCommitMock).toBeCalledWith('hideAmountGDT', false) + }) + + it('toasts a success message', () => { + expect(toastSuccessSpy).toBeCalledWith('settings.hideAmountGDT') + }) }) }) diff --git a/frontend/src/components/Template/ContentHeader/GdtAmount.vue b/frontend/src/components/Template/ContentHeader/GdtAmount.vue index 6a2312e07..9216ff232 100644 --- a/frontend/src/components/Template/ContentHeader/GdtAmount.vue +++ b/frontend/src/components/Template/ContentHeader/GdtAmount.vue @@ -34,7 +34,7 @@
@@ -42,6 +42,8 @@ diff --git a/frontend/src/components/Template/RightSide/LastTransactions.vue b/frontend/src/components/Template/RightSide/LastTransactions.vue index 419bf334d..1cfb02061 100644 --- a/frontend/src/components/Template/RightSide/LastTransactions.vue +++ b/frontend/src/components/Template/RightSide/LastTransactions.vue @@ -24,6 +24,7 @@ :size="72" :color="'#fff'" :username="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`" + :initials="`${transaction.linkedUser.firstName[0]} ${transaction.linkedUser.lastName[0]}`" >
diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 3156c2861..95d7b0c39 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -31,6 +31,8 @@ export const updateUserInfos = gql` $password: String $passwordNew: String $locale: String + $hideAmountGDD: Boolean + $hideAmountGDT: Boolean ) { updateUserInfos( firstName: $firstName @@ -38,6 +40,8 @@ export const updateUserInfos = gql` password: $password passwordNew: $passwordNew language: $locale + hideAmountGDD: $hideAmountGDD + hideAmountGDT: $hideAmountGDT ) } ` @@ -151,6 +155,8 @@ export const login = gql` publisherId isAdmin creation + hideAmountGDD + hideAmountGDT } } ` diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index fd810da76..db6a68bf4 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -14,6 +14,8 @@ export const verifyLogin = gql` publisherId isAdmin creation + hideAmountGDD + hideAmountGDT } } ` diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 52da80cc6..2eabd6ab5 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -269,6 +269,8 @@ "warningText": "Bist du noch da?" }, "settings": { + "hideAmountGDD": "Dein GDD Betrag ist versteckt.", + "hideAmountGDT": "Dein GDT Betrag ist versteckt.", "language": { "changeLanguage": "Sprache ändern", "de": "Deutsch", @@ -301,7 +303,9 @@ "text": "Speichere nun dein neues Passwort, mit dem du dich zukünftig in deinem Gradido-Konto anmelden kannst." }, "subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen." - } + }, + "showAmountGDD": "Dein GDD Betrag ist sichtbar.", + "showAmountGDT": "Dein GDT Betrag ist sichtbar." }, "signin": "Anmelden", "signup": "Registrieren", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 7837743e0..f90eef660 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -269,6 +269,8 @@ "warningText": "Are you still there?" }, "settings": { + "hideAmountGDD": "Your GDD amount is hidden.", + "hideAmountGDT": "Your GDT amount is hidden.", "language": { "changeLanguage": "Change language", "de": "Deutsch", @@ -301,7 +303,9 @@ "text": "Now save your new password, which you can use to log in to your Gradido account in the future." }, "subtitle": "If you have forgotten your password, you can reset it here." - } + }, + "showAmountGDD": "Your GDD amount is visible.", + "showAmountGDT": "Your GDT amount is visible." }, "signin": "Sign in", "signup": "Sign up", diff --git a/frontend/src/pages/ForgotPassword.spec.js b/frontend/src/pages/ForgotPassword.spec.js index af7931793..f4b74e555 100644 --- a/frontend/src/pages/ForgotPassword.spec.js +++ b/frontend/src/pages/ForgotPassword.spec.js @@ -58,11 +58,11 @@ describe('ForgotPassword', () => { }) it('has the label "Email"', () => { - expect(form.find('label').text()).toEqual('Email') + expect(form.find('label').text()).toEqual('form.email') }) it('has the placeholder "Email"', () => { - expect(form.find('input').attributes('placeholder')).toEqual('Email') + expect(form.find('input').attributes('placeholder')).toEqual('form.email') }) it('has a submit button', () => { diff --git a/frontend/src/pages/ForgotPassword.vue b/frontend/src/pages/ForgotPassword.vue index 095920046..77c2ac926 100644 --- a/frontend/src/pages/ForgotPassword.vue +++ b/frontend/src/pages/ForgotPassword.vue @@ -6,7 +6,12 @@ - +
{{ $t('settings.password.send_now') }} diff --git a/frontend/src/pages/Login.spec.js b/frontend/src/pages/Login.spec.js index 90e98cd44..14bf77aa6 100644 --- a/frontend/src/pages/Login.spec.js +++ b/frontend/src/pages/Login.spec.js @@ -76,7 +76,7 @@ describe('Login', () => { }) it('has an Email input field', () => { - expect(wrapper.find('input[placeholder="Email"]').exists()).toBe(true) + expect(wrapper.find('div[data-test="input-email"]').find('input').exists()).toBe(true) }) it('has an Password input field', () => { @@ -110,7 +110,10 @@ describe('Login', () => { describe('valid data', () => { beforeEach(async () => { jest.clearAllMocks() - await wrapper.find('input[placeholder="Email"]').setValue('user@example.org') + await wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('user@example.org') await wrapper.find('input[placeholder="form.password"]').setValue('1234') await flushPromises() apolloMutateMock.mockResolvedValue({ @@ -159,7 +162,10 @@ describe('Login', () => { code: 'some-code', } wrapper = Wrapper() - await wrapper.find('input[placeholder="Email"]').setValue('user@example.org') + await wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('user@example.org') await wrapper.find('input[placeholder="form.password"]').setValue('1234') await flushPromises() await wrapper.find('form').trigger('submit') @@ -180,7 +186,10 @@ describe('Login', () => { }) wrapper = Wrapper() jest.clearAllMocks() - await wrapper.find('input[placeholder="Email"]').setValue('user@example.org') + await wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('user@example.org') await wrapper.find('input[placeholder="form.password"]').setValue('1234') await flushPromises() await wrapper.find('form').trigger('submit') diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue index 6d2dff5fa..bd07af3ef 100755 --- a/frontend/src/pages/Login.vue +++ b/frontend/src/pages/Login.vue @@ -5,7 +5,14 @@ - + + + { }) it('has email input fields', () => { - expect(wrapper.find('#Email-input-field').exists()).toBe(true) + expect(wrapper.find('div[data-test="input-email"]').find('input').exists()).toBe(true) }) it('has 1 checkbox input fields', () => { @@ -107,7 +107,10 @@ describe('Register', () => { wrapper.find('#registerLastname').setValue('Mustermann') }) it('has disabled submit button when missing input checked box', () => { - wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net') + wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('max.mustermann@gradido.net') expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled') }) @@ -121,7 +124,10 @@ describe('Register', () => { beforeEach(() => { wrapper.find('#registerFirstname').setValue('Max') wrapper.find('#registerLastname').setValue('Mustermann') - wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net') + wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('max.mustermann@gradido.net') wrapper.find('#registerCheckbox').setChecked() }) @@ -211,7 +217,10 @@ describe('Register', () => { wrapper = Wrapper() wrapper.find('#registerFirstname').setValue('Max') wrapper.find('#registerLastname').setValue('Mustermann') - wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net') + wrapper + .find('div[data-test="input-email"]') + .find('input') + .setValue('max.mustermann@gradido.net') wrapper.find('#registerCheckbox').setChecked() await wrapper.find('form').trigger('submit') await flushPromises() diff --git a/frontend/src/pages/Register.vue b/frontend/src/pages/Register.vue index 9ecb196db..eb68ee7c5 100755 --- a/frontend/src/pages/Register.vue +++ b/frontend/src/pages/Register.vue @@ -59,7 +59,14 @@ - + + +
@@ -169,8 +169,9 @@ export default { } this.loading = false }, - onReset() { + onBack() { this.currentTransactionStep = TRANSACTION_STEPS.transactionForm + this.$mount() }, updateTransactions(pagination) { this.$emit('update-transactions', pagination) diff --git a/frontend/src/pages/Transactions.vue b/frontend/src/pages/Transactions.vue index 4be91b92b..40d883886 100644 --- a/frontend/src/pages/Transactions.vue +++ b/frontend/src/pages/Transactions.vue @@ -85,7 +85,7 @@ export default { } }, watch: { - gdt() { + currentPage() { if (this.gdt) { this.updateGdt() } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ff3a7ff1a..f374ac7bd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4578,9 +4578,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001286: - version "1.0.30001439" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz" - integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== + version "1.0.30001442" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz" + integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow== capture-exit@^2.0.0: version "2.0.0"