diff --git a/admin/src/components/RowDetails.vue b/admin/src/components/RowDetails.vue
index 9715ef158..97858c22b 100644
--- a/admin/src/components/RowDetails.vue
+++ b/admin/src/components/RowDetails.vue
@@ -6,7 +6,7 @@
:icon="type === 'PageCreationConfirm' ? 'x' : 'eye-slash-fill'"
aria-label="Help"
>
- {{ $t('hide_details') }} {{ row.item.user.firstName }} {{ row.item.user.lastName }}
+ {{ $t('hide_details') }}
diff --git a/admin/src/components/Tables/OpenCreationsTable.vue b/admin/src/components/Tables/OpenCreationsTable.vue
index d94f85bae..f6bd49343 100644
--- a/admin/src/components/Tables/OpenCreationsTable.vue
+++ b/admin/src/components/Tables/OpenCreationsTable.vue
@@ -30,9 +30,9 @@
{{ row.value }}
-
+
- {{ $t('moderator.memo-modified') }}
+ {{ getMemoComment(row.item) }}
@@ -229,6 +229,27 @@ export default {
this.creationUserData = row.item
}
},
+ isAddCommentToMemo(item) {
+ return item.updatedBy > 0 || item.confirmedBy > 0 || item.deletedBy > 0 || item.deniedBy > 0
+ },
+ getMemoComment(item) {
+ let comment = ''
+ if (item.confirmedBy > 0) {
+ comment = this.$t('contribution.confirmedBy', { name: item.confirmedByUserName })
+ } else if (item.deletedBy > 0) {
+ comment = this.$t('contribution.deletedBy', { name: item.deletedByUserName })
+ } else if (item.deniedBy > 0) {
+ comment = this.$t('contribution.deniedBy', { name: item.deniedByUserName })
+ }
+
+ if (item.updatedBy > 0) {
+ if (comment.length) {
+ comment += ' '
+ }
+ comment += this.$t('moderator.memo-modified', { name: item.updatedByUserName })
+ }
+ return comment
+ },
addClipboardListener() {
document.addEventListener('copy', this.handleCopy)
},
diff --git a/admin/src/graphql/adminListContributions.graphql b/admin/src/graphql/adminListContributions.graphql
index 57c26c10a..a73788c23 100644
--- a/admin/src/graphql/adminListContributions.graphql
+++ b/admin/src/graphql/adminListContributions.graphql
@@ -23,15 +23,20 @@ query adminListContributions(
contributionDate
confirmedAt
confirmedBy
+ confirmedByUserName
updatedAt
- updatedBy
+ updatedBy
+ updatedByUserName
contributionStatus
messagesCount
deniedAt
deniedBy
+ deniedByUserName
deletedAt
deletedBy
+ deletedByUserName
moderatorId
+ moderatorUserName
userId
resubmissionAt
}
diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json
index 58e3efcfc..4e47cf2fa 100644
--- a/admin/src/locales/de.json
+++ b/admin/src/locales/de.json
@@ -16,6 +16,11 @@
"back": "zurück",
"change_user_role": "Nutzerrolle ändern",
"close": "Schließen",
+ "contribution": {
+ "confirmedBy": "Bestätigt von {name}.",
+ "deletedBy": "Gelöscht von {name}.",
+ "deniedBy": "Abgelehnt von {name}."
+ },
"contributionLink": {
"amount": "Betrag",
"changeSaved": "Änderungen gespeichert",
@@ -171,10 +176,11 @@
"notice-tooltip": "Die Notiz ist nur für Moderatoren sichtbar",
"memo": "Text ändern",
"memo-tooltip": "Den Beitragstext bearbeiten",
- "memo-modified": "Text vom Moderator bearbeitet.",
+ "memo-modified": "Text von {name} bearbeitet.",
"message": "Nachricht",
"message-tooltip": "Nachricht an Benutzer schreiben",
- "request": "Diese Nachricht ist nur für die Moderatoren sichtbar!"
+ "request": "Diese Nachricht ist nur für die Moderatoren sichtbar!",
+ "who": "Wer?"
},
"name": "Name",
"navbar": {
diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json
index 4cd9f9860..e9ab404ff 100644
--- a/admin/src/locales/en.json
+++ b/admin/src/locales/en.json
@@ -16,6 +16,11 @@
"back": "back",
"change_user_role": "Change user role",
"close": "Close",
+ "contribution": {
+ "confirmedBy": "Confirmed by {name}.",
+ "deletedBy": "Deleted by {name}.",
+ "deniedBy": "Denied by {name}."
+ },
"contributionLink": {
"amount": "Amount",
"changeSaved": "Changes saved",
@@ -171,10 +176,11 @@
"notice-tooltip": "The note is only visible to moderators",
"memo": "Edit text",
"memo-tooltip": "Edit the text of the contribution",
- "memo-modified": "Text edited by moderator",
+ "memo-modified": "Text edited by {name}",
"message": "Message",
"message-tooltip": "Write message to user",
- "request": "This message is only visible to the moderators!"
+ "request": "This message is only visible to the moderators!",
+ "who": "Who?"
},
"name": "Name",
"navbar": {
diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue
index 74588f9f4..01f38a7c6 100644
--- a/admin/src/pages/CreationConfirm.vue
+++ b/admin/src/pages/CreationConfirm.vue
@@ -159,7 +159,11 @@ const baseFields = {
class: 'no-select',
formatter: formatDateOrDash,
},
- confirmedBy: { key: 'confirmedBy', label: t('moderator.moderator'), class: 'no-select' },
+ confirmedByUserName: {
+ key: 'confirmedByUserName',
+ label: t('moderator.who'),
+ class: 'no-select',
+ },
}
const fields = computed(
@@ -174,7 +178,7 @@ const fields = computed(
baseFields.amount,
baseFields.memo,
baseFields.contributionDate,
- { key: 'moderatorId', label: t('moderator.moderator'), class: 'no-select' },
+ { key: 'moderatorUserName', label: t('moderator.who'), class: 'no-select' },
{ key: 'editCreation', label: t('details') },
{ key: 'confirm', label: t('save') },
],
@@ -187,7 +191,7 @@ const fields = computed(
baseFields.contributionDate,
baseFields.createdAt,
baseFields.confirmedAt,
- baseFields.confirmedBy,
+ baseFields.confirmedByUserName,
{ key: 'chatCreation', label: t('details') },
],
// denied contributions
@@ -203,7 +207,7 @@ const fields = computed(
label: t('contributions.denied'),
formatter: formatDateOrDash,
},
- { key: 'deniedBy', label: t('moderator.moderator') },
+ { key: 'deniedByUserName', label: t('moderator.who') },
{ key: 'chatCreation', label: t('details') },
],
// deleted contributions
@@ -219,7 +223,7 @@ const fields = computed(
label: t('contributions.deleted'),
formatter: formatDateOrDash,
},
- { key: 'deletedBy', label: t('moderator.moderator') },
+ { key: 'deletedByUserName', label: t('moderator.who') },
{ key: 'chatCreation', label: t('details') },
],
// all contributions
@@ -232,7 +236,7 @@ const fields = computed(
baseFields.contributionDate,
baseFields.createdAt,
baseFields.confirmedAt,
- baseFields.confirmedBy,
+ baseFields.confirmedByUserName,
{ key: 'chatCreation', label: t('details') },
],
][tabIndex.value],
diff --git a/backend/package.json b/backend/package.json
index 9420a155a..c950d0de9 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -60,6 +60,7 @@
"core": "*",
"cors": "^2.8.5",
"database": "*",
+ "dataloader": "^2.2.3",
"decimal.js-light": "^2.5.1",
"dotenv": "^10.0.0",
"esbuild": "^0.25.2",
diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts
index 49a3c0be4..be4da99cf 100644
--- a/backend/src/graphql/model/Contribution.ts
+++ b/backend/src/graphql/model/Contribution.ts
@@ -1,6 +1,5 @@
import { Contribution as DbContribution } from 'database'
import { Field, Int, ObjectType } from 'type-graphql'
-
import { UnconfirmedContribution } from './UnconfirmedContribution'
@ObjectType()
@@ -8,6 +7,7 @@ export class Contribution extends UnconfirmedContribution {
constructor(dbContribution: DbContribution) {
super(dbContribution)
this.createdAt = dbContribution.createdAt
+ this.moderatorId = dbContribution.moderatorId
this.confirmedAt = dbContribution.confirmedAt
this.confirmedBy = dbContribution.confirmedBy
this.contributionDate = dbContribution.contributionDate
@@ -24,30 +24,48 @@ export class Contribution extends UnconfirmedContribution {
@Field(() => Date)
createdAt: Date
+ @Field(() => Int, { nullable: true })
+ moderatorId: number | null
+
+ @Field(() => String, { nullable: true })
+ moderatorUserName?: string | null
+
@Field(() => Date, { nullable: true })
confirmedAt: Date | null
@Field(() => Int, { nullable: true })
confirmedBy: number | null
+ @Field(() => String, { nullable: true })
+ confirmedByUserName?: string | null
+
@Field(() => Date, { nullable: true })
deniedAt: Date | null
@Field(() => Int, { nullable: true })
deniedBy: number | null
+ @Field(() => String, { nullable: true })
+ deniedByUserName?: string | null
+
@Field(() => Date, { nullable: true })
deletedAt: Date | null
@Field(() => Int, { nullable: true })
deletedBy: number | null
+ @Field(() => String, { nullable: true })
+ deletedByUserName?: string | null
+
@Field(() => Date, { nullable: true })
updatedAt: Date | null
@Field(() => Int, { nullable: true })
updatedBy: number | null
+ @Field(() => String, { nullable: true })
+ updatedByUserName?: string | null
+
@Field(() => Date)
contributionDate: Date
diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts
index b18af5c39..0882a04bd 100644
--- a/backend/src/graphql/resolver/ContributionResolver.ts
+++ b/backend/src/graphql/resolver/ContributionResolver.ts
@@ -23,9 +23,11 @@ import {
Contribution as DbContribution,
Transaction as DbTransaction,
User as DbUser,
+ findUserNamesByIds,
getLastTransaction,
UserContact,
} from 'database'
+import DataLoader from 'dataloader'
import { Decimal } from 'decimal.js-light'
import { GraphQLResolveInfo } from 'graphql'
import { getLogger } from 'log4js'
@@ -348,6 +350,7 @@ export class ContributionResolver {
): Promise {
// Check if only count was requested (without contributionList)
const fields = Object.keys(extractGraphQLFields(info))
+ // console.log(`fields: ${fields}`)
const countOnly: boolean = fields.includes('contributionCount') && fields.length === 1
// check if related user was requested
const userRequested =
@@ -370,8 +373,32 @@ export class ContributionResolver {
},
countOnly,
)
+ const result = new ContributionListResult(count, dbContributions)
- return new ContributionListResult(count, dbContributions)
+ const dataLoader = new DataLoader(async (userIds: readonly number[]) => {
+ const uniqueUserIds = new Set()
+ userIds.forEach((userId) => uniqueUserIds.add(userId))
+ const users = await findUserNamesByIds(Array.from(uniqueUserIds))
+ return userIds.map((userId) => users.get(userId))
+ })
+ for (const contribution of result.contributionList) {
+ if (contribution.confirmedBy) {
+ contribution.confirmedByUserName = await dataLoader.load(contribution.confirmedBy)
+ }
+ if (contribution.updatedBy) {
+ contribution.updatedByUserName = await dataLoader.load(contribution.updatedBy)
+ }
+ if (contribution.moderatorId) {
+ contribution.moderatorUserName = await dataLoader.load(contribution.moderatorId)
+ }
+ if (contribution.deletedBy) {
+ contribution.deletedByUserName = await dataLoader.load(contribution.deletedBy)
+ }
+ if (contribution.deniedBy) {
+ contribution.deniedByUserName = await dataLoader.load(contribution.deniedBy)
+ }
+ }
+ return result
}
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
diff --git a/bun.lock b/bun.lock
index 9a4a4c2bb..f6dc312a7 100644
--- a/bun.lock
+++ b/bun.lock
@@ -122,6 +122,7 @@
"core": "*",
"cors": "^2.8.5",
"database": "*",
+ "dataloader": "^2.2.3",
"decimal.js-light": "^2.5.1",
"dotenv": "^10.0.0",
"esbuild": "^0.25.2",
@@ -1808,6 +1809,8 @@
"database": ["database@workspace:database"],
+ "dataloader": ["dataloader@2.2.3", "", {}, "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA=="],
+
"date-fns": ["date-fns@2.30.0", "", { "dependencies": { "@babel/runtime": "^7.21.0" } }, "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw=="],
"date-format": ["date-format@4.0.14", "", {}, "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg=="],
diff --git a/database/src/queries/user.ts b/database/src/queries/user.ts
index 9e7d84c27..5aa46a07b 100644
--- a/database/src/queries/user.ts
+++ b/database/src/queries/user.ts
@@ -1,6 +1,6 @@
import { getLogger } from 'log4js'
import { aliasSchema, emailSchema, uuidv4Schema } from 'shared'
-import { Raw } from 'typeorm'
+import { In, Raw } from 'typeorm'
import { User as DbUser, UserContact as DbUserContact } from '../entity'
import { findWithCommunityIdentifier, LOG4JS_QUERIES_CATEGORY_NAME } from './index'
@@ -81,3 +81,19 @@ export async function findForeignUserByUuids(
where: { foreign: true, communityUuid, gradidoID },
})
}
+
+export async function findUserNamesByIds(userIds: number[]): Promise