Merge branch 'master' into log-error

This commit is contained in:
Ulf Gebhardt 2023-02-02 12:16:12 +01:00 committed by GitHub
commit 9b057c5c10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 514 additions and 76 deletions

View File

@ -45,6 +45,7 @@ import { Transaction as DbTransaction } from '@entity/Transaction'
import { User } from '@entity/User'
import { EventProtocolType } from '@/event/EventProtocolType'
import { logger, i18n as localization } from '@test/testSetup'
import { UserInputError } from 'apollo-server-express'
// mock account activation email to avoid console spam
// mock account activation email to avoid console spam
@ -681,7 +682,7 @@ describe('ContributionResolver', () => {
currentPage: 1,
pageSize: 25,
order: 'DESC',
filterConfirmed: false,
statusFilter: null,
},
}),
).resolves.toEqual(
@ -718,7 +719,76 @@ describe('ContributionResolver', () => {
resetToken()
})
it('returns allCreation', async () => {
it('throws an error with "NOT_VALID" in statusFilter', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['NOT_VALID'],
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
new UserInputError(
'Variable "$statusFilter" got invalid value "NOT_VALID" at "statusFilter[0]"; Value "NOT_VALID" does not exist in "ContributionStatus" enum.',
),
],
}),
)
})
it('throws an error with a null in statusFilter', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: [null],
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
new UserInputError(
'Variable "$statusFilter" got invalid value null at "statusFilter[0]"; Expected non-nullable type "ContributionStatus!" not to be null.',
),
],
}),
)
})
it('throws an error with null and "NOT_VALID" in statusFilter', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: [null, 'NOT_VALID'],
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
new UserInputError(
'Variable "$statusFilter" got invalid value null at "statusFilter[0]"; Expected non-nullable type "ContributionStatus!" not to be null.',
),
new UserInputError(
'Variable "$statusFilter" got invalid value "NOT_VALID" at "statusFilter[1]"; Value "NOT_VALID" does not exist in "ContributionStatus" enum.',
),
],
}),
)
})
it('returns all contributions without statusFilter', async () => {
await expect(
query({
query: listAllContributions,
@ -726,7 +796,6 @@ describe('ContributionResolver', () => {
currentPage: 1,
pageSize: 25,
order: 'DESC',
filterConfirmed: false,
},
}),
).resolves.toEqual(
@ -737,11 +806,301 @@ describe('ContributionResolver', () => {
contributionList: expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all contributions for statusFilter = null', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: null,
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 2,
contributionList: expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all contributions for statusFilter = []', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: [],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 2,
contributionList: expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all CONFIRMED contributions', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['CONFIRMED'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 1,
contributionList: expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.not.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all PENDING contributions', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['PENDING'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 1,
contributionList: expect.arrayContaining([
expect.not.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all IN_PROGRESS Creation', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['IN_PROGRESS'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 0,
contributionList: expect.not.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all DENIED Creation', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['DENIED'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 0,
contributionList: expect.not.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all DELETED Creation', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['DELETED'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 0,
contributionList: expect.not.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),
]),
},
},
}),
)
})
it('returns all CONFIRMED and PENDING Creation', async () => {
await expect(
query({
query: listAllContributions,
variables: {
currentPage: 1,
pageSize: 25,
order: 'DESC',
statusFilter: ['CONFIRMED', 'PENDING'],
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
listAllContributions: {
contributionCount: 2,
contributionList: expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
state: 'CONFIRMED',
memo: 'Herzlich Willkommen bei Gradido!',
amount: '1000',
}),
expect.objectContaining({
id: expect.any(Number),
state: 'PENDING',
memo: 'Test env contribution',
amount: '100',
}),

View File

@ -179,12 +179,23 @@ export class ContributionResolver {
async listAllContributions(
@Args()
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
statusFilter?: ContributionStatus[],
): Promise<ContributionListResult> {
const where: {
contributionStatus?: FindOperator<string> | null
} = {}
if (statusFilter && statusFilter.length) {
where.contributionStatus = In(statusFilter)
}
const [dbContributions, count] = await getConnection()
.createQueryBuilder()
.select('c')
.from(DbContribution, 'c')
.innerJoinAndSelect('c.user', 'u')
.where(where)
.orderBy('c.createdAt', order)
.limit(pageSize)
.offset((currentPage - 1) * pageSize)

View File

@ -172,18 +172,23 @@ export const listContributions = gql`
`
export const listAllContributions = `
query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC) {
listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusFilter: [ContributionStatus!]) {
listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order, statusFilter: $statusFilter) {
contributionCount
contributionList {
id
firstName
lastName
amount
memo
createdAt
confirmedAt
confirmedBy
firstName
lastName
amount
memo
createdAt
confirmedAt
confirmedBy
contributionDate
state
messagesCount
deniedAt
deniedBy
}
}
}

View File

@ -3,7 +3,7 @@
<b-form
ref="form"
@submit.prevent="submit"
class="p-3 bg-white appBoxShadow gradido-border-radius"
class="form-style p-3 bg-white appBoxShadow gradido-border-radius"
>
<label>{{ $t('contribution.selectDate') }}</label>
<b-form-datepicker
@ -23,50 +23,70 @@
<template #nav-next-year><span></span></template>
</b-form-datepicker>
<input-textarea
id="contribution-memo"
v-model="form.memo"
:name="$t('form.message')"
:label="$t('contribution.activity')"
:placeholder="$t('contribution.yourActivity')"
:rules="{ required: true, min: 5, max: 255 }"
/>
<input-hour
v-model="form.hours"
:name="$t('form.hours')"
:label="$t('form.hours')"
placeholder="0.25"
:rules="{
required: true,
min: 0.25,
max: validMaxTime,
gddCreationTime: [0.25, validMaxTime],
}"
:validMaxTime="validMaxTime"
@updateAmount="updateAmount"
></input-hour>
<input-amount
id="contribution-amount"
v-model="form.amount"
:name="$t('form.amount')"
:label="$t('form.amount')"
placeholder="20"
:rules="{ required: true, gddSendAmount: [20, validMaxGDD] }"
typ="ContributionForm"
></input-amount>
<div
v-if="(isThisMonth && maxGddThisMonth <= 0) || (!isThisMonth && maxGddLastMonth <= 0)"
class="p-3"
>
{{ noOpenCreation }}
</div>
<div v-else>
<input-textarea
id="contribution-memo"
v-model="form.memo"
:name="$t('form.message')"
:label="$t('contribution.activity')"
:placeholder="$t('contribution.yourActivity')"
:rules="{ required: true, min: 5, max: 255 }"
/>
<input-hour
v-model="form.hours"
:name="$t('form.hours')"
:label="$t('form.hours')"
placeholder="0.25"
:rules="{
required: true,
min: 0.25,
max: validMaxTime,
gddCreationTime: [0.25, validMaxTime],
}"
:validMaxTime="validMaxTime"
@updateAmount="updateAmount"
></input-hour>
<input-amount
id="contribution-amount"
v-model="form.amount"
:name="$t('form.amount')"
:label="$t('form.amount')"
placeholder="20"
:rules="{ required: true, gddSendAmount: [20, validMaxGDD] }"
typ="ContributionForm"
></input-amount>
<b-row class="mt-5">
<b-col>
<b-button type="reset" variant="secondary" @click="reset" data-test="button-cancel">
{{ $t('form.cancel') }}
</b-button>
</b-col>
<b-col class="text-right">
<b-button type="submit" variant="gradido" :disabled="disabled" data-test="button-submit">
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
</b-button>
</b-col>
</b-row>
<b-row class="mt-5">
<b-col cols="12" lg="6">
<b-button
block
type="reset"
variant="secondary"
@click="reset"
data-test="button-cancel"
>
{{ $t('form.cancel') }}
</b-button>
</b-col>
<b-col cols="12" lg="6" class="text-right mt-4 mt-lg-0">
<b-button
block
type="submit"
variant="gradido"
:disabled="disabled"
data-test="button-submit"
>
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
</b-button>
</b-col>
</b-row>
</div>
</b-form>
</div>
</template>
@ -133,6 +153,18 @@ export default {
validMaxTime() {
return Number(this.validMaxGDD / 20)
},
noOpenCreation() {
if (this.maxGddThisMonth <= 0 && this.maxGddLastMonth <= 0) {
return this.$t('contribution.noOpenCreation.allMonth')
}
if (this.isThisMonth && this.maxGddThisMonth <= 0) {
return this.$t('contribution.noOpenCreation.thisMonth')
}
if (!this.isThisMonth && this.maxGddLastMonth <= 0) {
return this.$t('contribution.noOpenCreation.lastMonth')
}
return ''
},
},
watch: {
value() {
@ -142,6 +174,9 @@ export default {
}
</script>
<style>
.form-style {
min-height: 410px;
}
span.errors {
color: red;
}

View File

@ -57,7 +57,16 @@
"yourContribution": "Dein Beitrag zum Gemeinwohl"
},
"lastContribution": "Letzte Beiträge",
"noContributions": {
"allContributions": "Es wurden noch keine Beiträge eingereicht.",
"myContributions": "Du hast noch keine Beiträge eingereicht."
},
"noDateSelected": "Wähle irgendein Datum im Monat",
"noOpenCreation": {
"allMonth": "Für alle beiden Monate ist dein Schöpfungslimit erreicht. Den Nächsten Monat kannst du wieder 1000 GDD Schöpfen.",
"lastMonth": "Für den ausgewählten Monat ist das Schöpfungslimit erreicht.",
"thisMonth": "Für den aktuellen Monat ist das Schöpfungslimit erreicht."
},
"selectDate": "Wann war dein Beitrag?",
"submit": "Einreichen",
"submitted": "Der Beitrag wurde eingereicht.",

View File

@ -57,7 +57,16 @@
"yourContribution": "Your Contributions to the Common Good"
},
"lastContribution": "Last Contributions",
"noContributions": {
"allContributions": "No contributions have been submitted yet.",
"myContributions": "You have not submitted any entries yet."
},
"noDateSelected": "Choose any date in the month",
"noOpenCreation": {
"allMonth": "For all two months your creation limit is reached. The next month you can create 1000 GDD again.",
"lastMonth": "The creation limit is reached for the selected month.",
"thisMonth": "The creation limit has been reached for the current month."
},
"selectDate": "When was your contribution?",
"submit": "Submit",
"submitted": "The contribution was submitted.",

View File

@ -20,28 +20,38 @@
/>
</b-tab>
<b-tab no-body>
<contribution-list
@closeAllOpenCollapse="closeAllOpenCollapse"
:items="items"
@update-list-contributions="updateListContributions"
@update-contribution-form="updateContributionForm"
@delete-contribution="deleteContribution"
@update-state="updateState"
:contributionCount="contributionCount"
:showPagination="true"
:pageSize="pageSize"
/>
<div v-if="items.length === 0">
{{ $t('contribution.noContributions.myContributions') }}
</div>
<div v-else>
<contribution-list
@closeAllOpenCollapse="closeAllOpenCollapse"
:items="items"
@update-list-contributions="updateListContributions"
@update-contribution-form="updateContributionForm"
@delete-contribution="deleteContribution"
@update-state="updateState"
:contributionCount="contributionCount"
:showPagination="true"
:pageSize="pageSize"
/>
</div>
</b-tab>
<b-tab no-body>
<contribution-list
:items="itemsAll"
@update-list-contributions="updateListAllContributions"
@update-contribution-form="updateContributionForm"
:contributionCount="contributionCountAll"
:showPagination="true"
:pageSize="pageSizeAll"
:allContribution="true"
/>
<div v-if="itemsAll.length === 0">
{{ $t('contribution.noContributions.allContributions') }}
</div>
<div v-else>
<contribution-list
:items="itemsAll"
@update-list-contributions="updateListAllContributions"
@update-contribution-form="updateContributionForm"
:contributionCount="contributionCountAll"
:showPagination="true"
:pageSize="pageSizeAll"
:allContribution="true"
/>
</div>
</b-tab>
</b-tabs>
</div>