From 4834c01fe9f32420282a98b14b6301324415e5a3 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 11:57:19 +0200 Subject: [PATCH 01/13] prevent overwriting process message with error message --- backend/src/apis/humhub/ExportUsers.ts | 35 ++++++++++++++++---------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index b3af27f7d..5214fef5d 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -34,6 +34,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise[] = [] - users.forEach((user: User) => promises.push(syncUser(user, humhubUsers))) - const executedActions = await Promise.all(promises) - executedActions.forEach((executedAction: ExecutedHumhubAction) => { - executedHumhubActionsCount[executedAction as number]++ - }) - // using process.stdout.write here so that carriage-return is working analog to c - // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); - process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`) + try { + const [users, totalUsers] = await getUsersPage(page, USER_BULK_SIZE) + dbUserCount += users.length + userCount = users.length + page++ + const promises: Promise[] = [] + users.forEach((user: User) => promises.push(syncUser(user, humhubUsers))) + const executedActions = await Promise.all(promises) + executedActions.forEach((executedAction: ExecutedHumhubAction) => { + executedHumhubActionsCount[executedAction as number]++ + }) + // using process.stdout.write here so that carriage-return is working analog to c + // printf("\rchecked user: %d/%d", dbUserCount, totalUsers); + process.stdout.write(`checked user: ${dbUserCount}/${totalUsers}\r`) + } catch (e) { + process.stdout.write('\n') + throw e + } } while (userCount === USER_BULK_SIZE) + process.stdout.write('\n') await con.destroy() const elapsed = new Date().getTime() - start From 4860cc2670e89e95f09ab20253a8369325854dad Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 13:05:16 +0200 Subject: [PATCH 02/13] use old humhub username on update for finding humhub user to prevent new user if user has changed email in humhub and alias in wallet --- backend/src/apis/humhub/ExportUsers.ts | 4 ++-- backend/src/apis/humhub/__mocks__/syncUser.ts | 4 +++- backend/src/apis/humhub/syncUser.test.ts | 7 ++++--- backend/src/apis/humhub/syncUser.ts | 7 +++---- backend/src/graphql/resolver/UserResolver.ts | 20 ++++++++++++++----- .../graphql/resolver/util/syncHumhub.test.ts | 4 ++-- .../src/graphql/resolver/util/syncHumhub.ts | 13 ++++++------ backend/test/testSetup.ts | 2 ++ 8 files changed, 37 insertions(+), 24 deletions(-) diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index 5214fef5d..4ed6f2928 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -26,7 +26,7 @@ function getUsersPage(page: number, limit: number): Promise<[User[], number]> { /** * @param client - * @returns user map indices with email + * @returns user map indices with username */ async function loadUsersFromHumHub(client: HumHubClient): Promise> { const start = new Date().getTime() @@ -43,7 +43,7 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise { // deleted users have empty emails if (user.account.email) { - humhubUsers.set(user.account.email.trim(), user) + humhubUsers.set(user.account.username, user) } else { skippedUsersCount++ } diff --git a/backend/src/apis/humhub/__mocks__/syncUser.ts b/backend/src/apis/humhub/__mocks__/syncUser.ts index 7e0660da4..1cb2edfbf 100644 --- a/backend/src/apis/humhub/__mocks__/syncUser.ts +++ b/backend/src/apis/humhub/__mocks__/syncUser.ts @@ -2,6 +2,7 @@ import { User } from '@entity/User' import { isHumhubUserIdenticalToDbUser } from '@/apis/humhub/compareHumhubUserDbUser' import { GetUser } from '@/apis/humhub/model/GetUser' +import { PostUser } from '@/apis/humhub/model/PostUser' export enum ExecutedHumhubAction { UPDATE, @@ -26,7 +27,8 @@ export async function syncUser( user: User, humhubUsers: Map, ): Promise { - const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + const postUser = new PostUser(user) + const humhubUser = humhubUsers.get(postUser.account.username) if (humhubUser) { if (!user.humhubAllowed) { return Promise.resolve(ExecutedHumhubAction.DELETE) diff --git a/backend/src/apis/humhub/syncUser.test.ts b/backend/src/apis/humhub/syncUser.test.ts index 20a6b2c33..00c125632 100644 --- a/backend/src/apis/humhub/syncUser.test.ts +++ b/backend/src/apis/humhub/syncUser.test.ts @@ -28,7 +28,8 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is false, should return DELETE action', async () => { const humhubUsers = new Map() - humhubUsers.set(defaultUser.emailContact.email, new GetUser(defaultUser, 1)) + const humhubUser = new GetUser(defaultUser, 1) + humhubUsers.set(humhubUser.account.username, humhubUser) defaultUser.humhubAllowed = false const result = await syncUser(defaultUser, humhubUsers) @@ -39,8 +40,8 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is true and there are changes in user data, should return UPDATE action', async () => { const humhubUsers = new Map() const humhubUser = new GetUser(defaultUser, 1) + humhubUsers.set(humhubUser.account.username, humhubUser) humhubUser.account.username = 'test username' - humhubUsers.set(defaultUser.emailContact.email, humhubUser) defaultUser.humhubAllowed = true const result = await syncUser(defaultUser, humhubUsers) @@ -51,7 +52,7 @@ describe('syncUser function', () => { it('When humhubUser exists and user.humhubAllowed is true and there are no changes in user data, should return SKIP action', async () => { const humhubUsers = new Map() const humhubUser = new GetUser(defaultUser, 1) - humhubUsers.set(defaultUser.emailContact.email, humhubUser) + humhubUsers.set(humhubUser.account.username, humhubUser) defaultUser.humhubAllowed = true const result = await syncUser(defaultUser, humhubUsers) diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index fc6fcc99b..8057626af 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -21,9 +21,8 @@ export enum ExecutedHumhubAction { * | true | true | false | SKIP * | false | false | ignored | SKIP * | false | true | ignored | CREATE - * @param user - * @param humHubClient - * @param humhubUsers + * @param user user entity + * @param humhubUsers user map indices with username * @returns */ export async function syncUser( @@ -31,7 +30,7 @@ export async function syncUser( humhubUsers: Map, ): Promise { const postUser = new PostUser(user) - const humhubUser = humhubUsers.get(user.emailContact.email.trim()) + const humhubUser = humhubUsers.get(postUser.account.username) const humHubClient = HumHubClient.getInstance() if (!humHubClient) { throw new LogError('Error creating humhub client') diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index fea150338..66e6a546b 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -37,6 +37,7 @@ import { UpdateUserInfosArgs } from '@arg/UpdateUserInfosArgs' import { OptInType } from '@enum/OptInType' import { Order } from '@enum/Order' import { PasswordEncryptionType } from '@enum/PasswordEncryptionType' +import { PublishNameType } from '@enum/PublishNameType' import { UserContactType } from '@enum/UserContactType' import { SearchAdminUsersResult } from '@model/AdminUser' // import { Location } from '@model/Location' @@ -54,6 +55,7 @@ import { subscribe } from '@/apis/KlicktippController' import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' +import { PublishNameLogic } from '@/data/PublishName.logic' import { sendAccountActivationEmail, sendAccountMultiRegistrationEmail, @@ -246,12 +248,12 @@ export class UserResolver { try { const result = await humhubUserPromise user.humhubAllowed = result?.result?.account.status === 1 - if (user.humhubAllowed) { + if (user.humhubAllowed && result?.result?.account?.username) { let spaceId = null if (projectBranding) { spaceId = projectBranding.spaceId } - void syncHumhub(null, dbUser, spaceId) + await syncHumhub(null, dbUser, result.result.account.username, spaceId) } } catch (e) { logger.error("couldn't reach out to humhub, disable for now", e) @@ -448,7 +450,11 @@ export class UserResolver { if (projectBranding) { spaceId = projectBranding.spaceId } - void syncHumhub(null, dbUser, spaceId) + try { + await syncHumhub(null, dbUser, dbUser.gradidoID, spaceId) + } catch (e) { + logger.error("createUser: couldn't reach out to humhub, disable for now", e) + } } if (redeemCode) { @@ -659,6 +665,10 @@ export class UserResolver { ) const user = getUser(context) const updateUserInGMS = compareGmsRelevantUserSettings(user, updateUserInfosArgs) + const publishNameLogic = new PublishNameLogic(user) + const oldHumhubUsername = publishNameLogic.getUserIdentifier( + user.humhubPublishName as PublishNameType, + ) // try { if (firstName) { @@ -764,7 +774,7 @@ export class UserResolver { } try { if (CONFIG.HUMHUB_ACTIVE) { - await syncHumhub(updateUserInfosArgs, user) + await syncHumhub(updateUserInfosArgs, user, oldHumhubUsername) } } catch (e) { logger.error('error sync user with humhub', e) @@ -839,7 +849,7 @@ export class UserResolver { } const humhubUserAccount = new HumhubAccount(dbUser) const autoLoginUrlPromise = humhubClient.createAutoLoginUrl(humhubUserAccount.username, project) - const humhubUser = await syncHumhub(null, dbUser) + const humhubUser = await syncHumhub(null, dbUser, humhubUserAccount.username) if (!humhubUser) { throw new LogError("user don't exist (any longer) on humhub and couldn't be created") } diff --git a/backend/src/graphql/resolver/util/syncHumhub.test.ts b/backend/src/graphql/resolver/util/syncHumhub.test.ts index c25eb52a8..d8a61af23 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.test.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.test.ts @@ -32,7 +32,7 @@ describe('syncHumhub', () => { }) it('Should not sync if no relevant changes', async () => { - await syncHumhub(mockUpdateUserInfosArg, new User()) + await syncHumhub(mockUpdateUserInfosArg, new User(), 'username') expect(HumHubClient.getInstance).not.toBeCalled() // language logging from some other place expect(logger.debug).toBeCalledTimes(5) @@ -42,7 +42,7 @@ describe('syncHumhub', () => { it('Should retrieve user from humhub and sync if relevant changes', async () => { mockUpdateUserInfosArg.firstName = 'New' // Relevant changes mockUser.firstName = 'New' - await syncHumhub(mockUpdateUserInfosArg, mockUser) + await syncHumhub(mockUpdateUserInfosArg, mockUser, 'username') expect(logger.debug).toHaveBeenCalledTimes(8) // Four language logging calls, two debug calls in function, one for not syncing expect(logger.info).toHaveBeenLastCalledWith('finished sync user with humhub', { localId: mockUser.id, diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index 90500bbc5..23ba6d0dd 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -2,10 +2,9 @@ import { User } from '@entity/User' import { HumHubClient } from '@/apis/humhub/HumHubClient' import { GetUser } from '@/apis/humhub/model/GetUser' +import { PostUser } from '@/apis/humhub/model/PostUser' import { ExecutedHumhubAction, syncUser } from '@/apis/humhub/syncUser' -import { PublishNameLogic } from '@/data/PublishName.logic' import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs' -import { PublishNameType } from '@/graphql/enum/PublishNameType' import { backendLogger as logger } from '@/server/logger' /** @@ -17,6 +16,7 @@ import { backendLogger as logger } from '@/server/logger' export async function syncHumhub( updateUserInfosArg: UpdateUserInfosArgs | null, user: User, + oldHumhubUsername: string, spaceId?: number | null, ): Promise { // check for humhub relevant changes @@ -38,15 +38,13 @@ export async function syncHumhub( return } logger.debug('retrieve user from humhub') - const userNameLogic = new PublishNameLogic(user) - const username = userNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType) - let humhubUser = await humhubClient.userByUsername(username) + let humhubUser = await humhubClient.userByUsername(oldHumhubUsername) if (!humhubUser) { humhubUser = await humhubClient.userByEmail(user.emailContact.email) } const humhubUsers = new Map() if (humhubUser) { - humhubUsers.set(user.emailContact.email, humhubUser) + humhubUsers.set(humhubUser.account.username, humhubUser) } logger.debug('update user at humhub') const result = await syncUser(user, humhubUsers) @@ -63,7 +61,8 @@ export async function syncHumhub( logger.debug(`user added to space ${spaceId}`) } if (result !== ExecutedHumhubAction.SKIP) { - return await humhubClient.userByUsername(username) + const getUser = new PostUser(user) + return await humhubClient.userByUsername(getUser.account.username) } return humhubUser } diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index 8cf4ff084..74021f0a3 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -6,6 +6,8 @@ import { backendLogger as logger } from '@/server/logger' CONFIG.EMAIL = true CONFIG.EMAIL_TEST_MODUS = false +CONFIG.HUMHUB_ACTIVE = false +CONFIG.GMS_ACTIVE = false jest.setTimeout(1000000) From 456725aeae08c8ff0c843e871d81efaac680df6e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 15:09:37 +0200 Subject: [PATCH 03/13] remove unnecessary \n, better final error reporting --- backend/src/apis/humhub/ExportUsers.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index 4ed6f2928..99c4471e5 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -34,7 +34,6 @@ async function loadUsersFromHumHub(client: HumHubClient): Promise { // eslint-disable-next-line no-console - console.error(e) + console.error(JSON.stringify(e, null, 2)) // eslint-disable-next-line n/no-process-exit process.exit(1) }) From 21be12f3b0857b79883914cfa5efdf187781b9f9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 15:27:30 +0200 Subject: [PATCH 04/13] check firstname length for humhub --- backend/src/apis/humhub/ExportUsers.ts | 3 ++- backend/src/apis/humhub/syncUser.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/backend/src/apis/humhub/ExportUsers.ts b/backend/src/apis/humhub/ExportUsers.ts index 99c4471e5..d4515b3ce 100644 --- a/backend/src/apis/humhub/ExportUsers.ts +++ b/backend/src/apis/humhub/ExportUsers.ts @@ -88,7 +88,7 @@ async function main() { const humhubUsers = await loadUsersFromHumHub(humHubClient) let dbUserCount = 0 - const executedHumhubActionsCount = [0, 0, 0, 0] + const executedHumhubActionsCount = [0, 0, 0, 0, 0] do { try { @@ -121,6 +121,7 @@ async function main() { updatedCount: executedHumhubActionsCount[ExecutedHumhubAction.UPDATE], skippedCount: executedHumhubActionsCount[ExecutedHumhubAction.SKIP], deletedCount: executedHumhubActionsCount[ExecutedHumhubAction.DELETE], + validationErrorCount: executedHumhubActionsCount[ExecutedHumhubAction.VALIDATION_ERROR], }) } diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index 8057626af..8b7963b07 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -1,6 +1,7 @@ import { User } from '@entity/User' import { LogError } from '@/server/LogError' +import { backendLogger as logger } from '@/server/logger' import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' import { HumHubClient } from './HumHubClient' @@ -12,7 +13,18 @@ export enum ExecutedHumhubAction { CREATE, SKIP, DELETE, + VALIDATION_ERROR, } + +// todo: replace with full validation (schema) +function isValid(postUser: PostUser, userId: number): boolean { + if (postUser.profile.firstname.length > 20) { + logger.error('firstname too long for humhub, for user with id:', userId) + return false + } + return true +} + /** * Trigger action according to conditions * | User exist on humhub | export to humhub allowed | changes in user data | ACTION @@ -30,6 +42,9 @@ export async function syncUser( humhubUsers: Map, ): Promise { const postUser = new PostUser(user) + if (!isValid(postUser, user.id)) { + return ExecutedHumhubAction.VALIDATION_ERROR + } const humhubUser = humhubUsers.get(postUser.account.username) const humHubClient = HumHubClient.getInstance() if (!humHubClient) { From c16786cd208afaeb42a2205da65471ce9a74d3e7 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 16:22:43 +0200 Subject: [PATCH 05/13] slice first_name and last_name after 20 character for humhub --- .../humhub/compareHumhubUserDbUser.test.ts | 18 ++++++++++++++++++ backend/src/apis/humhub/syncUser.ts | 4 ++++ backend/src/data/PublishName.logic.ts | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts index cc636d17a..a3d6c7044 100644 --- a/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts +++ b/backend/src/apis/humhub/compareHumhubUserDbUser.test.ts @@ -1,4 +1,7 @@ /* eslint-disable prettier/prettier */ +// eslint-disable-next-line import/no-unassigned-import +import 'reflect-metadata' +import { PublishNameType } from '@/graphql/enum/PublishNameType' import { communityDbUser } from '@/util/communityUser' import { isHumhubUserIdenticalToDbUser } from './compareHumhubUserDbUser' @@ -13,6 +16,7 @@ describe('isHumhubUserIdenticalToDbUser', () => { defaultUser.alias = 'alias' defaultUser.emailContact.email = 'email@gmail.com' defaultUser.language = 'en' + defaultUser.gradidoID = 'gradidoID' }) it('Should return true because humhubUser was created from entity user', () => { @@ -21,6 +25,20 @@ describe('isHumhubUserIdenticalToDbUser', () => { expect(result).toBe(true) }) + it('Should return false, because last name differ because of publish name type', () => { + const humhubUser = new GetUser(defaultUser, 1) + defaultUser.humhubPublishName = PublishNameType.PUBLISH_NAME_FIRST + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(false) + }) + + it('Should return true, even if alias is empty', () => { + defaultUser.alias = '' + const humhubUser = new GetUser(defaultUser, 1) + const result = isHumhubUserIdenticalToDbUser(humhubUser, defaultUser) + expect(result).toBe(true) + }) + it('Should return false because first name differ', () => { const humhubUser = new GetUser(defaultUser, 1) humhubUser.profile.firstname = 'changed first name' diff --git a/backend/src/apis/humhub/syncUser.ts b/backend/src/apis/humhub/syncUser.ts index 8b7963b07..e5b793ee3 100644 --- a/backend/src/apis/humhub/syncUser.ts +++ b/backend/src/apis/humhub/syncUser.ts @@ -22,6 +22,10 @@ function isValid(postUser: PostUser, userId: number): boolean { logger.error('firstname too long for humhub, for user with id:', userId) return false } + if (postUser.profile.lastname.length > 20) { + logger.error('lastname too long for humhub, for user with id:', userId) + return false + } return true } diff --git a/backend/src/data/PublishName.logic.ts b/backend/src/data/PublishName.logic.ts index a78627d49..b70a29bb3 100644 --- a/backend/src/data/PublishName.logic.ts +++ b/backend/src/data/PublishName.logic.ts @@ -35,12 +35,16 @@ export class PublishNameLogic { * @returns user.firstName for PUBLISH_NAME_FIRST, PUBLISH_NAME_FIRST_INITIAL or PUBLISH_NAME_FULL */ public getFirstName(publishNameType: PublishNameType): string { + let firstName = '' + if (this.user && typeof this.user.firstName === 'string') { + firstName = this.user.firstName + } return [ PublishNameType.PUBLISH_NAME_FIRST, PublishNameType.PUBLISH_NAME_FIRST_INITIAL, PublishNameType.PUBLISH_NAME_FULL, ].includes(publishNameType) - ? this.user.firstName + ? firstName.slice(0, 20) : '' } @@ -51,10 +55,14 @@ export class PublishNameLogic { * first initial from user.lastName for PUBLISH_NAME_FIRST_INITIAL */ public getLastName(publishNameType: PublishNameType): string { + let lastName = '' + if (this.user && typeof this.user.lastName === 'string') { + lastName = this.user.lastName + } return publishNameType === PublishNameType.PUBLISH_NAME_FULL - ? this.user.lastName - : publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL - ? this.user.lastName.charAt(0) + ? lastName.slice(0, 20) + : publishNameType === PublishNameType.PUBLISH_NAME_FIRST_INITIAL && lastName.length > 0 + ? lastName.charAt(0) : '' } From adf9e04c4f2e4571fadef51d1e78e13454c4e9b9 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 24 Apr 2025 16:42:42 +0200 Subject: [PATCH 06/13] request max 25 admins and mods on frontend information page --- frontend/src/graphql/queries.js | 4 ++-- frontend/src/pages/InfoStatistic.vue | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 65f7e9418..edd034344 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -256,8 +256,8 @@ export const communityStatistics = gql` ` export const searchAdminUsers = gql` - query { - searchAdminUsers { + query ($pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) { + searchAdminUsers(pageSize: $pageSize, currentPage: $currentPage, order: $order) { userCount userList { firstName diff --git a/frontend/src/pages/InfoStatistic.vue b/frontend/src/pages/InfoStatistic.vue index d5cebf2b1..b63a09423 100644 --- a/frontend/src/pages/InfoStatistic.vue +++ b/frontend/src/pages/InfoStatistic.vue @@ -49,7 +49,11 @@ const moderators = computed(() => itemsAdminUser.value.filter((item) => item.rol const { onResult: onContributionLinksResult, onError: onContributionLinksError } = useQuery(listContributionLinks) -const { onResult: onAdminUsersResult, onError: onAdminUsersError } = useQuery(searchAdminUsers) +const { onResult: onAdminUsersResult, onError: onAdminUsersError } = useQuery(searchAdminUsers, { + pageSize: 25, + currentPage: 1, + order: 'ASC', +}) onContributionLinksResult(({ data }) => { if (data) { From 184a7266e8a44b71f89f75a5e4b3cf0d76cabe64 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 27 Apr 2025 10:41:08 +0200 Subject: [PATCH 07/13] fix warning --- frontend/src/components/Contributions/ContributionList.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/Contributions/ContributionList.vue b/frontend/src/components/Contributions/ContributionList.vue index 889bc8fd2..e7fbd750d 100644 --- a/frontend/src/components/Contributions/ContributionList.vue +++ b/frontend/src/components/Contributions/ContributionList.vue @@ -67,6 +67,7 @@ const props = defineProps({ }) const emit = defineEmits([ + 'close-all-open-collapse', 'update-list-contributions', 'update-contribution-form', 'delete-contribution', From 3478b3b75863b36a929d6392c7776cb6fb439d99 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 27 Apr 2025 10:41:52 +0200 Subject: [PATCH 08/13] fix error with updating wrong contribution list --- frontend/src/pages/Community.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index 6d7ab8ba0..74cac3210 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -29,7 +29,7 @@ :show-pagination="true" :page-size="pageSize" @close-all-open-collapse="closeAllOpenCollapse" - @update-list-contributions="handleUpdateListAllContributions" + @update-list-contributions="handleUpdateListContributions" @update-contribution-form="handleUpdateContributionForm" @delete-contribution="handleDeleteContribution" @update-status="updateStatus" From f67cee1fe395919e1e1563bbf793a68a42177af2 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 27 Apr 2025 10:42:08 +0200 Subject: [PATCH 09/13] fix bug with not updating contribution message --- .../Contributions/ContributionListItem.vue | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionListItem.vue b/frontend/src/components/Contributions/ContributionListItem.vue index a08af2e4c..2b0e2b0a1 100644 --- a/frontend/src/components/Contributions/ContributionListItem.vue +++ b/frontend/src/components/Contributions/ContributionListItem.vue @@ -246,7 +246,7 @@ function deleteContribution(item) { } } -const { onResult, onError, load } = useLazyQuery(listContributionMessages, { +const { onResult, onError, load, refetch } = useLazyQuery(listContributionMessages, { contributionId: props.contributionId, }) @@ -254,9 +254,15 @@ function getListContributionMessages(closeCollapse = true) { if (closeCollapse) { emit('close-all-open-collapse') } - load(listContributionMessages, { + const variables = { contributionId: props.contributionId, - }) + } + // load works only once and return false on second call + if (!load(listContributionMessages, variables)) { + // update list data every time getListContributionMessages is called + // because it could be added new messages + refetch(variables) + } } onResult((resultValue) => { From 6142ee4ca27c65e646696c6d604d420029f01215 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 27 Apr 2025 10:48:09 +0200 Subject: [PATCH 10/13] fix test --- .../src/components/Contributions/ContributionListItem.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/Contributions/ContributionListItem.spec.js b/frontend/src/components/Contributions/ContributionListItem.spec.js index eb5fb1247..300076060 100644 --- a/frontend/src/components/Contributions/ContributionListItem.spec.js +++ b/frontend/src/components/Contributions/ContributionListItem.spec.js @@ -14,6 +14,7 @@ vi.mock('@vue/apollo-composable', () => ({ onResult: vi.fn(), onError: vi.fn(), load: vi.fn(), + refetch: vi.fn(), })), useMutation: vi.fn(() => ({ mutate: vi.fn(), From 612d8b736b5a9d0918f1bd951b0a81fbf25b35ae Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Sun, 27 Apr 2025 15:49:25 +0200 Subject: [PATCH 11/13] show skeleton only as long as transactionslist is loaded --- .../src/components/GddTransactionList.vue | 2 +- frontend/src/graphql/transactions.graphql | 56 ++++++++++ frontend/src/layouts/DashboardLayout.vue | 104 +++++++----------- frontend/src/pages/Community.vue | 4 - frontend/src/pages/InfoStatistic.vue | 8 -- frontend/src/pages/Send.vue | 3 - 6 files changed, 98 insertions(+), 79 deletions(-) create mode 100644 frontend/src/graphql/transactions.graphql diff --git a/frontend/src/components/GddTransactionList.vue b/frontend/src/components/GddTransactionList.vue index 8396f8d52..b4d188374 100644 --- a/frontend/src/components/GddTransactionList.vue +++ b/frontend/src/components/GddTransactionList.vue @@ -101,7 +101,7 @@ export default { this.updateTransactions() }, timestamp: { - immediate: true, + immediate: false, handler: 'updateTransactions', }, }, diff --git a/frontend/src/graphql/transactions.graphql b/frontend/src/graphql/transactions.graphql new file mode 100644 index 000000000..eff0abbb5 --- /dev/null +++ b/frontend/src/graphql/transactions.graphql @@ -0,0 +1,56 @@ +fragment balanceFields on Balance { + balance + balanceGDT + count + linkCount +} + +fragment transactionFields on Transaction { + id + typeId + amount + balance + previousBalance + balanceDate + memo + linkedUser { + firstName + lastName + communityUuid + communityName + gradidoID + alias + } + decay { + decay + start + end + duration + } + linkId +} + +query transactionsQuery($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) { + transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + balance { + ...balanceFields + } + transactions { + ...transactionFields + } + } +} + +query transactionsUserCountQuery($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) { + transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + balance { + ...balanceFields + } + transactions { + ...transactionFields + } + } + communityStatistics { + totalUsers + } +} \ No newline at end of file diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index 95bd858f9..6ac0bcb51 100755 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -187,11 +187,10 @@