mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into logger_transactionResolver
This commit is contained in:
commit
18242ccfea
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -360,6 +360,25 @@ jobs:
|
|||||||
- name: backend | Lint
|
- name: backend | Lint
|
||||||
run: docker run --rm gradido/backend:test yarn run lint
|
run: docker run --rm gradido/backend:test yarn run lint
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# JOB: LOCALES BACKEND #######################################################
|
||||||
|
##############################################################################
|
||||||
|
locales_backend:
|
||||||
|
name: Locales - Backend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build_test_backend]
|
||||||
|
steps:
|
||||||
|
##########################################################################
|
||||||
|
# CHECKOUT CODE ##########################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
##########################################################################
|
||||||
|
# LOCALES BACKEND #####################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Backend | Locales
|
||||||
|
run: cd backend && yarn && yarn locales
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT DATABASE UP ######################################################
|
# JOB: LINT DATABASE UP ######################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
49
CHANGELOG.md
49
CHANGELOG.md
@ -4,8 +4,57 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
|
|
||||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [1.18.2](https://github.com/gradido/gradido/compare/1.18.1...1.18.2)
|
||||||
|
|
||||||
|
- fix(admin): deny contribution button to left [`#2699`](https://github.com/gradido/gradido/pull/2699)
|
||||||
|
|
||||||
|
#### [1.18.1](https://github.com/gradido/gradido/compare/1.18.0...1.18.1)
|
||||||
|
|
||||||
|
> 10 February 2023
|
||||||
|
|
||||||
|
- chore(release): version 1.18.1 [`#2698`](https://github.com/gradido/gradido/pull/2698)
|
||||||
|
- fix(frontend): fix is last month for empty form date [`#2697`](https://github.com/gradido/gradido/pull/2697)
|
||||||
|
- fix(frontend): community link [`#2696`](https://github.com/gradido/gradido/pull/2696)
|
||||||
|
|
||||||
|
#### [1.18.0](https://github.com/gradido/gradido/compare/1.17.1...1.18.0)
|
||||||
|
|
||||||
|
> 9 February 2023
|
||||||
|
|
||||||
|
- feat(release): version 1.18.0 [`#2690`](https://github.com/gradido/gradido/pull/2690)
|
||||||
|
- refactor(frontend): toast by automatically logged out [`#2681`](https://github.com/gradido/gradido/pull/2681)
|
||||||
|
- refactor(frontend): change text for gdd_per_link.choose-amount [`#2638`](https://github.com/gradido/gradido/pull/2638)
|
||||||
|
- fix(backend): emails for deny and delete contribution [`#2688`](https://github.com/gradido/gradido/pull/2688)
|
||||||
|
- refactor(other): remove config version from `.env.dist` [`#2686`](https://github.com/gradido/gradido/pull/2686)
|
||||||
|
- refactor(backend): use LogError on contributionMessageResolver [`#2663`](https://github.com/gradido/gradido/pull/2663)
|
||||||
|
- refactor(backend): get last transaction by only one function [`#2668`](https://github.com/gradido/gradido/pull/2668)
|
||||||
|
- refactor(other): don't rebuild modul if unit test file has been changed [`#2667`](https://github.com/gradido/gradido/pull/2667)
|
||||||
|
- refactor(backend): use LogError on contributionLinkResolver [`#2662`](https://github.com/gradido/gradido/pull/2662)
|
||||||
|
- refactor(backend): remove event protocol config switch [`#2670`](https://github.com/gradido/gradido/pull/2670)
|
||||||
|
- refactor(backend): event protocol [`#2652`](https://github.com/gradido/gradido/pull/2652)
|
||||||
|
- refactor(frontend): sidebar becomes smaller when critical phase [`#2649`](https://github.com/gradido/gradido/pull/2649)
|
||||||
|
- refactor(backend): use LogError on sendEMailTranslated [`#2656`](https://github.com/gradido/gradido/pull/2656)
|
||||||
|
- refactor(backend): log error class [`#2640`](https://github.com/gradido/gradido/pull/2640)
|
||||||
|
- feat(backend): add filterState parameter to listAllContributions query [`#2619`](https://github.com/gradido/gradido/pull/2619)
|
||||||
|
- refactor(frontend): there is no message when a month is fully created [`#2626`](https://github.com/gradido/gradido/pull/2626)
|
||||||
|
- refactor(frontend): better text alignment on send via link [`#2637`](https://github.com/gradido/gradido/pull/2637)
|
||||||
|
- refactor(frontend): text changed as indicated in the issues [`#2642`](https://github.com/gradido/gradido/pull/2642)
|
||||||
|
- refactor(frontend): when you click on create, you will be directed to the form [`#2645`](https://github.com/gradido/gradido/pull/2645)
|
||||||
|
- feat(backend): federation: separated dht-hub features in new dht-node modul [`#2510`](https://github.com/gradido/gradido/pull/2510)
|
||||||
|
- refactor(backend): refine assembly of error message in user resolver [`#2636`](https://github.com/gradido/gradido/pull/2636)
|
||||||
|
- fix(backend): unit tests creations for 31st day [`#2641`](https://github.com/gradido/gradido/pull/2641)
|
||||||
|
- fix(workflow): properly lint pr - prevent requirement to restart linting [`#2635`](https://github.com/gradido/gradido/pull/2635)
|
||||||
|
- feat(frontend): 'yes'-button shows which dialog is currently open with a different color [`#2629`](https://github.com/gradido/gradido/pull/2629)
|
||||||
|
- feat(backend): federation implement multiple apollo graphql endpoints [`#2459`](https://github.com/gradido/gradido/pull/2459)
|
||||||
|
- refactor(frontend): add legend to all contribution tab, and add tests. [`#2625`](https://github.com/gradido/gradido/pull/2625)
|
||||||
|
- feat(frontend): unit tests community page [`#2587`](https://github.com/gradido/gradido/pull/2587)
|
||||||
|
- feat(backend): deny contributions [`#2461`](https://github.com/gradido/gradido/pull/2461)
|
||||||
|
- refactor(admin): update yarn.lock file of admin. [`#2579`](https://github.com/gradido/gradido/pull/2579)
|
||||||
|
|
||||||
#### [1.17.1](https://github.com/gradido/gradido/compare/1.17.0...1.17.1)
|
#### [1.17.1](https://github.com/gradido/gradido/compare/1.17.0...1.17.1)
|
||||||
|
|
||||||
|
> 20 January 2023
|
||||||
|
|
||||||
|
- chore(release): v1.17.1 [`#2588`](https://github.com/gradido/gradido/pull/2588)
|
||||||
- refactor(frontend): change contribution memo add word-break [`#2583`](https://github.com/gradido/gradido/pull/2583)
|
- refactor(frontend): change contribution memo add word-break [`#2583`](https://github.com/gradido/gradido/pull/2583)
|
||||||
- refactor(admin): add text-break on all table memo fields [`#2584`](https://github.com/gradido/gradido/pull/2584)
|
- refactor(admin): add text-break on all table memo fields [`#2584`](https://github.com/gradido/gradido/pull/2584)
|
||||||
- fix(frontend): throw proper frontend warning errors [`#2586`](https://github.com/gradido/gradido/pull/2586)
|
- fix(frontend): throw proper frontend warning errors [`#2586`](https://github.com/gradido/gradido/pull/2586)
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
CONFIG_VERSION=v1.2022-03-18
|
|
||||||
|
|
||||||
GRAPHQL_URI=http://localhost:4000/graphql
|
GRAPHQL_URI=http://localhost:4000/graphql
|
||||||
WALLET_AUTH_URL=http://localhost/authenticate?token={token}
|
WALLET_AUTH_URL=http://localhost/authenticate?token={token}
|
||||||
WALLET_URL=http://localhost/login
|
WALLET_URL=http://localhost/login
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"description": "Administraion Interface for Gradido",
|
"description": "Administraion Interface for Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Moriz Wahl",
|
"author": "Moriz Wahl",
|
||||||
"version": "1.17.1",
|
"version": "1.18.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -86,5 +86,10 @@
|
|||||||
"> 1%",
|
"> 1%",
|
||||||
"last 2 versions",
|
"last 2 versions",
|
||||||
"not ie <= 10"
|
"not ie <= 10"
|
||||||
|
],
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ignore": [
|
||||||
|
"**/*.spec.js"
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,14 +42,30 @@ describe('ContributionLink', () => {
|
|||||||
expect(wrapper.find('div.contribution-link').exists()).toBe(true)
|
expect(wrapper.find('div.contribution-link').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits toggle::collapse new Contribution', async () => {
|
describe('function editContributionLinkData', () => {
|
||||||
|
beforeEach(() => {
|
||||||
wrapper.vm.editContributionLinkData()
|
wrapper.vm.editContributionLinkData()
|
||||||
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
})
|
||||||
|
it('emits toggle::collapse new Contribution', async () => {
|
||||||
|
await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('function closeContributionForm', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.setData({ visible: true })
|
||||||
|
wrapper.vm.closeContributionForm()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits toggle::collapse close Contribution-Form ', async () => {
|
it('emits toggle::collapse close Contribution-Form ', async () => {
|
||||||
wrapper.vm.closeContributionForm()
|
await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
||||||
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
})
|
||||||
|
it('editContributionLink is false', async () => {
|
||||||
|
await expect(wrapper.vm.editContributionLink).toBe(false)
|
||||||
|
})
|
||||||
|
it('contributionLinkData is empty', async () => {
|
||||||
|
await expect(wrapper.vm.contributionLinkData).toEqual({})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -88,5 +88,16 @@ describe('CreationTransactionList', () => {
|
|||||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('watch currentPage', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.setData({ currentPage: 2 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the string in normal order if reversed property is not true', () => {
|
||||||
|
expect(wrapper.vm.currentPage).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -46,39 +46,31 @@ describe('NavBar', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('Navbar Menu', () => {
|
describe('Navbar Menu', () => {
|
||||||
it('has a link to overview', () => {
|
|
||||||
expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has a link to /user', () => {
|
it('has a link to /user', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe('/user')
|
expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/user')
|
||||||
})
|
|
||||||
|
|
||||||
it('has a link to /creation', () => {
|
|
||||||
expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe('/creation')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /creation-confirm', () => {
|
it('has a link to /creation-confirm', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe(
|
expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe(
|
||||||
'/creation-confirm',
|
'/creation-confirm',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /contribution-links', () => {
|
it('has a link to /contribution-links', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe(
|
expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe(
|
||||||
'/contribution-links',
|
'/contribution-links',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /statistic', () => {
|
it('has a link to /statistic', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('/statistic')
|
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('wallet', () => {
|
describe('wallet', () => {
|
||||||
const assignLocationSpy = jest.fn()
|
const assignLocationSpy = jest.fn()
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.findAll('.nav-item').at(6).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('changes window location to wallet', () => {
|
it.skip('changes window location to wallet', () => {
|
||||||
@ -97,7 +89,7 @@ describe('NavBar', () => {
|
|||||||
window.location = {
|
window.location = {
|
||||||
assign: windowLocationMock,
|
assign: windowLocationMock,
|
||||||
}
|
}
|
||||||
await wrapper.findAll('.nav-item').at(7).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('redirects to /logout', () => {
|
it('redirects to /logout', () => {
|
||||||
|
|||||||
@ -9,9 +9,7 @@
|
|||||||
|
|
||||||
<b-collapse id="nav-collapse" is-nav>
|
<b-collapse id="nav-collapse" is-nav>
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
<b-nav-item to="/">{{ $t('navbar.overview') }}</b-nav-item>
|
|
||||||
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
|
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
|
||||||
<b-nav-item to="/creation">{{ $t('navbar.multi_creation') }}</b-nav-item>
|
|
||||||
<b-nav-item
|
<b-nav-item
|
||||||
v-show="$store.state.openCreations > 0"
|
v-show="$store.state.openCreations > 0"
|
||||||
class="bg-color-creation p-1"
|
class="bg-color-creation p-1"
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="component-select-users-table">
|
|
||||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
|
||||||
<template #cell(bookmark)="row">
|
|
||||||
<div>
|
|
||||||
<b-button
|
|
||||||
v-if="row.item.emailChecked"
|
|
||||||
variant="warning"
|
|
||||||
size="md"
|
|
||||||
@click="$emit('push-item', row.item)"
|
|
||||||
class="mr-2"
|
|
||||||
>
|
|
||||||
<b-icon icon="plus" variant="success"></b-icon>
|
|
||||||
</b-button>
|
|
||||||
<div v-else>{{ $t('e_mail') }}{{ $t('math.exclaim') }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</b-table-lite>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'SelectUsersTable',
|
|
||||||
props: {
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="component-selected-users-table">
|
|
||||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
|
||||||
<template #cell(bookmark)="row">
|
|
||||||
<b-button variant="danger" size="md" @click="$emit('remove-item', row.item)" class="mr-2">
|
|
||||||
<b-icon icon="x" variant="light"></b-icon>
|
|
||||||
</b-button>
|
|
||||||
</template>
|
|
||||||
</b-table-lite>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'SelectedUsersTable',
|
|
||||||
props: {
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -32,7 +32,6 @@
|
|||||||
"creation": "Schöpfung",
|
"creation": "Schöpfung",
|
||||||
"creationList": "Schöpfungsliste",
|
"creationList": "Schöpfungsliste",
|
||||||
"creation_form": {
|
"creation_form": {
|
||||||
"creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.",
|
|
||||||
"creation_for": "Aktives Grundeinkommen für",
|
"creation_for": "Aktives Grundeinkommen für",
|
||||||
"enter_text": "Text eintragen",
|
"enter_text": "Text eintragen",
|
||||||
"form": "Schöpfungsformular",
|
"form": "Schöpfungsformular",
|
||||||
@ -87,7 +86,6 @@
|
|||||||
"lastname": "Nachname",
|
"lastname": "Nachname",
|
||||||
"math": {
|
"math": {
|
||||||
"equals": "=",
|
"equals": "=",
|
||||||
"exclaim": "!",
|
|
||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
@ -95,15 +93,12 @@
|
|||||||
"request": "Die Anfrage wurde gesendet."
|
"request": "Die Anfrage wurde gesendet."
|
||||||
},
|
},
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatische Beiträge",
|
"automaticContributions": "Automatische Beiträge",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"multi_creation": "Mehrfachschöpfung",
|
|
||||||
"my-account": "Mein Konto",
|
"my-account": "Mein Konto",
|
||||||
"open_creation": "Offene Schöpfungen",
|
"open_creation": "Offene Schöpfungen",
|
||||||
"overview": "Übersicht",
|
|
||||||
"statistic": "Statistik",
|
"statistic": "Statistik",
|
||||||
"user_search": "Nutzersuche"
|
"user_search": "Nutzersuche"
|
||||||
},
|
},
|
||||||
@ -132,9 +127,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redeemed": "eingelöst",
|
"redeemed": "eingelöst",
|
||||||
"remove": "Entfernen",
|
|
||||||
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
||||||
"remove_all": "alle Nutzer entfernen",
|
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"statistic": {
|
"statistic": {
|
||||||
"activeUsers": "Aktive Mitglieder",
|
"activeUsers": "Aktive Mitglieder",
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
"creation": "Creation",
|
"creation": "Creation",
|
||||||
"creationList": "Creation list",
|
"creationList": "Creation list",
|
||||||
"creation_form": {
|
"creation_form": {
|
||||||
"creation_failed": "Could not create pending creation for {email}",
|
|
||||||
"creation_for": "Active Basic Income for",
|
"creation_for": "Active Basic Income for",
|
||||||
"enter_text": "Enter text",
|
"enter_text": "Enter text",
|
||||||
"form": "Creation form",
|
"form": "Creation form",
|
||||||
@ -87,7 +86,6 @@
|
|||||||
"lastname": "Lastname",
|
"lastname": "Lastname",
|
||||||
"math": {
|
"math": {
|
||||||
"equals": "=",
|
"equals": "=",
|
||||||
"exclaim": "!",
|
|
||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
@ -95,15 +93,12 @@
|
|||||||
"request": "Request has been sent."
|
"request": "Request has been sent."
|
||||||
},
|
},
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatic Contributions",
|
"automaticContributions": "Automatic Contributions",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"multi_creation": "Multiple creation",
|
|
||||||
"my-account": "My Account",
|
"my-account": "My Account",
|
||||||
"open_creation": "Open creations",
|
"open_creation": "Open creations",
|
||||||
"overview": "Overview",
|
|
||||||
"statistic": "Statistic",
|
"statistic": "Statistic",
|
||||||
"user_search": "User search"
|
"user_search": "User search"
|
||||||
},
|
},
|
||||||
@ -132,9 +127,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redeemed": "redeemed",
|
"redeemed": "redeemed",
|
||||||
"remove": "Remove",
|
|
||||||
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
||||||
"remove_all": "Remove all users",
|
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"statistic": {
|
"statistic": {
|
||||||
"activeUsers": "Active members",
|
"activeUsers": "Active members",
|
||||||
|
|||||||
18
admin/src/locales/index.test.js
Normal file
18
admin/src/locales/index.test.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import locales from './index.js'
|
||||||
|
|
||||||
|
describe('locales', () => {
|
||||||
|
it('should contain 2 locales', () => {
|
||||||
|
expect(locales).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should contain a German locale', () => {
|
||||||
|
expect(locales).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'Deutsch',
|
||||||
|
code: 'de',
|
||||||
|
iso: 'de-DE',
|
||||||
|
enabled: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import ContributionLinks from './ContributionLinks.vue'
|
import ContributionLinks from './ContributionLinks.vue'
|
||||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||||
|
import { toastErrorSpy } from '../../test/testSetup'
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ describe('ContributionLinks', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
describe('apollo returns', () => {
|
||||||
it('calls listContributionLinks', () => {
|
it('calls listContributionLinks', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@ -55,4 +56,22 @@ describe('ContributionLinks', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe.skip('query transaction with error', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
apolloQueryMock.mockRejectedValue({ message: 'OUCH!' })
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toast error', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith(
|
||||||
|
'listContributionLinks has no result, use default data',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,337 +0,0 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import Creation from './Creation.vue'
|
|
||||||
import { toastErrorSpy } from '../../test/testSetup'
|
|
||||||
|
|
||||||
const localVue = global.localVue
|
|
||||||
|
|
||||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
|
||||||
data: {
|
|
||||||
searchUsers: {
|
|
||||||
userCount: 2,
|
|
||||||
userList: [
|
|
||||||
{
|
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
|
||||||
lastName: 'Bloxberg',
|
|
||||||
email: 'bibi@bloxberg.de',
|
|
||||||
creation: [200, 400, 600],
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const storeCommitMock = jest.fn()
|
|
||||||
|
|
||||||
const mocks = {
|
|
||||||
$t: jest.fn((t, options) => (options ? [t, options] : t)),
|
|
||||||
$d: jest.fn((d) => d),
|
|
||||||
$apollo: {
|
|
||||||
query: apolloQueryMock,
|
|
||||||
},
|
|
||||||
$store: {
|
|
||||||
commit: storeCommitMock,
|
|
||||||
state: {
|
|
||||||
userSelectedInMassCreation: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Creation', () => {
|
|
||||||
let wrapper
|
|
||||||
|
|
||||||
const Wrapper = () => {
|
|
||||||
return mount(Creation, { localVue, mocks })
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('mount', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has a DIV element with the class.creation', () => {
|
|
||||||
expect(wrapper.find('div.creation').exists()).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('apollo returns user array', () => {
|
|
||||||
it('calls the searchUser query', () => {
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has two rows in the left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has nwo rows in the right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has correct data in first row ', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi')
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'Bloxberg',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'200 | 400 | 600',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'bibi@bloxberg.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has correct data in second row ', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'Benjamin',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'Blümchen',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'800 | 600 | 400',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('push item', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the correct user in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'bibi@bloxberg.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the correct user in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('updates userSelectedInMassCreation in store', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('remove item', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper
|
|
||||||
.findAll('table')
|
|
||||||
.at(1)
|
|
||||||
.findAll('tbody > tr')
|
|
||||||
.at(0)
|
|
||||||
.find('button')
|
|
||||||
.trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has two items in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the removed user in first row', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has no items in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits empty array as userSelectedInMassCreation', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('remove all bookmarks', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await wrapper.find('button.btn-light').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has no items in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits empty array to userSelectedInMassCreation', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls searchUsers', () => {
|
|
||||||
expect(apolloQueryMock).toBeCalled()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('store has items in userSelectedInMassCreation', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
mocks.$store.state.userSelectedInMassCreation = [
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the stored user in second row', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('failed creations', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper
|
|
||||||
.findComponent({ name: 'CreationFormular' })
|
|
||||||
.vm.$emit('toast-failed-creations', ['bibi@bloxberg.de', 'benjamin@bluemchen.de'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toasts two error messages', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith([
|
|
||||||
'creation_form.creation_failed',
|
|
||||||
{ email: 'bibi@bloxberg.de' },
|
|
||||||
])
|
|
||||||
expect(toastErrorSpy).toBeCalledWith([
|
|
||||||
'creation_form.creation_failed',
|
|
||||||
{ email: 'benjamin@bluemchen.de' },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('watchers', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('search criteria', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.setData({ criteria: 'XX' })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls API when criteria changes', async () => {
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: 'XX',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('reset search criteria', () => {
|
|
||||||
it('calls the API', async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls API when currentPage changes', async () => {
|
|
||||||
await wrapper.setData({ currentPage: 2 })
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 2,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('apollo returns error', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
apolloQueryMock.mockRejectedValue({
|
|
||||||
message: 'Ouch',
|
|
||||||
})
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toasts an error message', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith('Ouch')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,200 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="creation">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="12" lg="6">
|
|
||||||
<label>{{ $t('user_search') }}</label>
|
|
||||||
<b-input-group>
|
|
||||||
<b-form-input
|
|
||||||
type="text"
|
|
||||||
class="test-input-criteria"
|
|
||||||
v-model="criteria"
|
|
||||||
:placeholder="$t('user_search')"
|
|
||||||
></b-form-input>
|
|
||||||
|
|
||||||
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
|
||||||
<b-input-group-text class="pointer">
|
|
||||||
<b-icon icon="x" />
|
|
||||||
</b-input-group-text>
|
|
||||||
</b-input-group-append>
|
|
||||||
</b-input-group>
|
|
||||||
<select-users-table
|
|
||||||
v-if="itemsList.length > 0"
|
|
||||||
:items="itemsList"
|
|
||||||
:fields="Searchfields"
|
|
||||||
@push-item="pushItem"
|
|
||||||
/>
|
|
||||||
<b-pagination
|
|
||||||
pills
|
|
||||||
v-model="currentPage"
|
|
||||||
per-page="perPage"
|
|
||||||
:total-rows="rows"
|
|
||||||
align="center"
|
|
||||||
:hide-ellipsis="true"
|
|
||||||
></b-pagination>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" lg="6" class="shadow p-3 mb-5 rounded bg-info">
|
|
||||||
<div v-show="itemsMassCreation.length > 0">
|
|
||||||
<div class="text-right pr-4 mb-1">
|
|
||||||
<b-button @click="removeAllBookmarks()" variant="light">
|
|
||||||
<b-icon icon="x" scale="2" variant="danger"></b-icon>
|
|
||||||
|
|
||||||
{{ $t('remove_all') }}
|
|
||||||
</b-button>
|
|
||||||
</div>
|
|
||||||
<selected-users-table
|
|
||||||
class="shadow p-3 mb-5 bg-white rounded"
|
|
||||||
:items="itemsMassCreation"
|
|
||||||
:fields="fields"
|
|
||||||
@remove-item="removeItem"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="itemsMassCreation.length === 0">
|
|
||||||
{{ $t('multiple_creation_text') }}
|
|
||||||
</div>
|
|
||||||
<creation-formular
|
|
||||||
v-else
|
|
||||||
type="massCreation"
|
|
||||||
:creation="creation"
|
|
||||||
:items="itemsMassCreation"
|
|
||||||
@remove-all-bookmark="removeAllBookmarks"
|
|
||||||
@toast-failed-creations="toastFailedCreations"
|
|
||||||
/>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import CreationFormular from '../components/CreationFormular.vue'
|
|
||||||
import SelectUsersTable from '../components/Tables/SelectUsersTable.vue'
|
|
||||||
import SelectedUsersTable from '../components/Tables/SelectedUsersTable.vue'
|
|
||||||
import { searchUsers } from '../graphql/searchUsers'
|
|
||||||
import { creationMonths } from '../mixins/creationMonths'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Creation',
|
|
||||||
mixins: [creationMonths],
|
|
||||||
components: {
|
|
||||||
CreationFormular,
|
|
||||||
SelectUsersTable,
|
|
||||||
SelectedUsersTable,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showArrays: false,
|
|
||||||
itemsList: [],
|
|
||||||
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
|
|
||||||
radioSelectedMass: '',
|
|
||||||
criteria: '',
|
|
||||||
rows: 0,
|
|
||||||
currentPage: 1,
|
|
||||||
perPage: 25,
|
|
||||||
now: Date.now(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
await this.getUsers()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getUsers() {
|
|
||||||
this.$apollo
|
|
||||||
.query({
|
|
||||||
query: searchUsers,
|
|
||||||
variables: {
|
|
||||||
searchText: this.criteria,
|
|
||||||
currentPage: this.currentPage,
|
|
||||||
pageSize: this.perPage,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
this.rows = result.data.searchUsers.userCount
|
|
||||||
this.itemsList = result.data.searchUsers.userList.map((user) => {
|
|
||||||
return {
|
|
||||||
...user,
|
|
||||||
showDetails: false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (this.itemsMassCreation.length !== 0) {
|
|
||||||
const selectedIndices = this.itemsMassCreation.map((item) => item.userId)
|
|
||||||
this.itemsList = this.itemsList.filter((item) => !selectedIndices.includes(item.userId))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toastError(error.message)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
pushItem(selectedItem) {
|
|
||||||
this.itemsMassCreation = [
|
|
||||||
this.itemsList.find((item) => selectedItem.userId === item.userId),
|
|
||||||
...this.itemsMassCreation,
|
|
||||||
]
|
|
||||||
this.itemsList = this.itemsList.filter((item) => selectedItem.userId !== item.userId)
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
|
||||||
},
|
|
||||||
removeItem(selectedItem) {
|
|
||||||
this.itemsList = [
|
|
||||||
this.itemsMassCreation.find((item) => selectedItem.userId === item.userId),
|
|
||||||
...this.itemsList,
|
|
||||||
]
|
|
||||||
this.itemsMassCreation = this.itemsMassCreation.filter(
|
|
||||||
(item) => selectedItem.userId !== item.userId,
|
|
||||||
)
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
|
||||||
},
|
|
||||||
removeAllBookmarks() {
|
|
||||||
this.itemsMassCreation = []
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', [])
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
toastFailedCreations(failedCreations) {
|
|
||||||
failedCreations.forEach((email) =>
|
|
||||||
this.toastError(this.$t('creation_form.creation_failed', { email })),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
Searchfields() {
|
|
||||||
return [
|
|
||||||
{ key: 'bookmark', label: 'bookmark' },
|
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
|
||||||
{
|
|
||||||
key: 'creation',
|
|
||||||
label: this.creationLabel,
|
|
||||||
formatter: (value, key, item) => {
|
|
||||||
return value.join(' | ')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
fields() {
|
|
||||||
return [
|
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
|
||||||
{
|
|
||||||
key: 'creation',
|
|
||||||
label: this.creationLabel,
|
|
||||||
formatter: (value, key, item) => {
|
|
||||||
return value.join(' | ')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ key: 'bookmark', label: this.$t('remove') },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentPage() {
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
criteria() {
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -259,7 +259,7 @@ describe('CreationConfirm', () => {
|
|||||||
|
|
||||||
describe('deny creation', () => {
|
describe('deny creation', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.findAll('tr').at(1).findAll('button').at(2).trigger('click')
|
await wrapper.findAll('tr').at(1).findAll('button').at(1).trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('opens the overlay', () => {
|
it('opens the overlay', () => {
|
||||||
|
|||||||
@ -129,6 +129,7 @@ export default {
|
|||||||
fields() {
|
fields() {
|
||||||
return [
|
return [
|
||||||
{ key: 'bookmark', label: this.$t('delete') },
|
{ key: 'bookmark', label: this.$t('delete') },
|
||||||
|
{ key: 'deny', label: this.$t('deny') },
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
{ key: 'email', label: this.$t('e_mail') },
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
{ key: 'firstName', label: this.$t('firstname') },
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
{ key: 'lastName', label: this.$t('lastname') },
|
||||||
@ -149,7 +150,6 @@ export default {
|
|||||||
},
|
},
|
||||||
{ key: 'moderator', label: this.$t('moderator') },
|
{ key: 'moderator', label: this.$t('moderator') },
|
||||||
{ key: 'editCreation', label: this.$t('edit') },
|
{ key: 'editCreation', label: this.$t('edit') },
|
||||||
{ key: 'deny', label: this.$t('deny') },
|
|
||||||
{ key: 'confirm', label: this.$t('save') },
|
{ key: 'confirm', label: this.$t('save') },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -45,7 +45,7 @@ describe('router', () => {
|
|||||||
|
|
||||||
describe('routes', () => {
|
describe('routes', () => {
|
||||||
it('has nine routes defined', () => {
|
it('has nine routes defined', () => {
|
||||||
expect(routes).toHaveLength(9)
|
expect(routes).toHaveLength(8)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has "/overview" as default', async () => {
|
it('has "/overview" as default', async () => {
|
||||||
@ -67,13 +67,6 @@ describe('router', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creation', () => {
|
|
||||||
it('loads the "Creation" component', async () => {
|
|
||||||
const component = await routes.find((r) => r.path === '/creation').component()
|
|
||||||
expect(component.default.name).toBe('Creation')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('creation-confirm', () => {
|
describe('creation-confirm', () => {
|
||||||
it('loads the "CreationConfirm" component', async () => {
|
it('loads the "CreationConfirm" component', async () => {
|
||||||
const component = await routes.find((r) => r.path === '/creation-confirm').component()
|
const component = await routes.find((r) => r.path === '/creation-confirm').component()
|
||||||
|
|||||||
@ -19,10 +19,6 @@ const routes = [
|
|||||||
path: '/user',
|
path: '/user',
|
||||||
component: () => import('@/pages/UserSearch.vue'),
|
component: () => import('@/pages/UserSearch.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/creation',
|
|
||||||
component: () => import('@/pages/Creation.vue'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/creation-confirm',
|
path: '/creation-confirm',
|
||||||
component: () => import('@/pages/CreationConfirm.vue'),
|
component: () => import('@/pages/CreationConfirm.vue'),
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
CONFIG_VERSION=v14.2022-12-22
|
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
PORT=4000
|
PORT=4000
|
||||||
JWT_SECRET=secret123
|
JWT_SECRET=secret123
|
||||||
@ -55,9 +53,6 @@ EMAIL_CODE_REQUEST_TIME=10
|
|||||||
# Webhook
|
# Webhook
|
||||||
WEBHOOK_ELOPAGE_SECRET=secret
|
WEBHOOK_ELOPAGE_SECRET=secret
|
||||||
|
|
||||||
# EventProtocol
|
|
||||||
EVENT_PROTOCOL_DISABLED=false
|
|
||||||
|
|
||||||
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
||||||
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
||||||
# LOG_LEVEL=info
|
# LOG_LEVEL=info
|
||||||
|
|||||||
@ -54,9 +54,6 @@ EMAIL_CODE_REQUEST_TIME=$EMAIL_CODE_REQUEST_TIME
|
|||||||
# Webhook
|
# Webhook
|
||||||
WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
|
WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
|
||||||
|
|
||||||
# EventProtocol
|
|
||||||
EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED
|
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
||||||
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-backend",
|
"name": "gradido-backend",
|
||||||
"version": "1.17.1",
|
"version": "1.18.2",
|
||||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/backend",
|
"repository": "https://github.com/gradido/gradido/backend",
|
||||||
@ -15,7 +15,8 @@
|
|||||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
||||||
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||||
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
||||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts"
|
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
|
||||||
|
"locales": "scripts/sort.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hyperswarm/dht": "^6.2.0",
|
"@hyperswarm/dht": "^6.2.0",
|
||||||
@ -72,5 +73,10 @@
|
|||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"tsconfig-paths": "^3.14.0",
|
"tsconfig-paths": "^3.14.0",
|
||||||
"typescript": "^4.3.4"
|
"typescript": "^4.3.4"
|
||||||
|
},
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ignore": [
|
||||||
|
"**/*.test.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
backend/scripts/sort.sh
Executable file
25
backend/scripts/sort.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ROOT_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
tmp=$(mktemp)
|
||||||
|
exit_code=0
|
||||||
|
|
||||||
|
for locale_file in $ROOT_DIR/src/locales/*.json
|
||||||
|
do
|
||||||
|
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
|
||||||
|
if [ "$*" == "--fix" ]
|
||||||
|
then
|
||||||
|
mv "$tmp" $locale_file
|
||||||
|
else
|
||||||
|
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||||
|
then
|
||||||
|
: # all good
|
||||||
|
else
|
||||||
|
exit_code=$?
|
||||||
|
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $exit_code
|
||||||
13
backend/scripts/sort_filter.jq
Normal file
13
backend/scripts/sort_filter.jq
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
def walk(f):
|
||||||
|
. as $in
|
||||||
|
| if type == "object" then
|
||||||
|
reduce keys_unsorted[] as $key
|
||||||
|
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||||
|
elif type == "array" then map( walk(f) ) | f
|
||||||
|
else f
|
||||||
|
end;
|
||||||
|
|
||||||
|
def keys_sort_by(f):
|
||||||
|
to_entries | sort_by(.key|f ) | from_entries;
|
||||||
|
|
||||||
|
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||||
@ -17,7 +17,7 @@ const constants = {
|
|||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
CONFIG_VERSION: {
|
CONFIG_VERSION: {
|
||||||
DEFAULT: 'DEFAULT',
|
DEFAULT: 'DEFAULT',
|
||||||
EXPECTED: 'v14.2022-12-22',
|
EXPECTED: 'v15.2023-02-07',
|
||||||
CURRENT: '',
|
CURRENT: '',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -99,11 +99,6 @@ const webhook = {
|
|||||||
WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret',
|
WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret',
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventProtocol = {
|
|
||||||
// global switch to enable writing of EventProtocol-Entries
|
|
||||||
EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is needed by graphql-directive-auth
|
// This is needed by graphql-directive-auth
|
||||||
process.env.APP_SECRET = server.JWT_SECRET
|
process.env.APP_SECRET = server.JWT_SECRET
|
||||||
|
|
||||||
@ -139,7 +134,6 @@ const CONFIG = {
|
|||||||
...email,
|
...email,
|
||||||
...loginServer,
|
...loginServer,
|
||||||
...webhook,
|
...webhook,
|
||||||
...eventProtocol,
|
|
||||||
...federation,
|
...federation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
sendAccountMultiRegistrationEmail,
|
sendAccountMultiRegistrationEmail,
|
||||||
sendContributionConfirmedEmail,
|
sendContributionConfirmedEmail,
|
||||||
sendContributionDeniedEmail,
|
sendContributionDeniedEmail,
|
||||||
|
sendContributionDeletedEmail,
|
||||||
sendResetPasswordEmail,
|
sendResetPasswordEmail,
|
||||||
sendTransactionLinkRedeemedEmail,
|
sendTransactionLinkRedeemedEmail,
|
||||||
sendTransactionReceivedEmail,
|
sendTransactionReceivedEmail,
|
||||||
@ -438,6 +439,84 @@ describe('sendEmailVariants', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('sendContributionDeletedEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendContributionDeletedEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'contributionDeleted',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
|
||||||
|
communityURL: CONFIG.COMMUNITY_URL,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Your common good contribution was deleted',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS DELETED'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Your common good contribution was deleted</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Your common good contribution was deleted</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Your public good contribution “My contribution.” was deleted by Bibi Bloxberg.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br>your Gradido team')
|
||||||
|
expect(result.originalMessage.html).toContain('—————')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<div style="position: relative; left: -22px;"><img src="https://gdd.gradido.net/img/brand/green.png" width="200" alt="Gradido-Akademie Logo"></div><br>Gradido-Akademie<br>Institut für Wirtschaftsbionik<br>Pfarrweg 2<br>74653 Künzelsau<br>Deutschland<br><a href="mailto:support@supportmail.com">support@supportmail.com</a><br><a href="http://localhost/">http://localhost/</a>',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('sendResetPasswordEmail', () => {
|
describe('sendResetPasswordEmail', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
result = await sendResetPasswordEmail({
|
result = await sendResetPasswordEmail({
|
||||||
|
|||||||
@ -103,6 +103,32 @@ export const sendContributionConfirmedEmail = (data: {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const sendContributionDeletedEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
contributionMemo: string
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'contributionDeleted',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
contributionMemo: data.contributionMemo,
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL,
|
||||||
|
communityURL: CONFIG.COMMUNITY_URL,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const sendContributionDeniedEmail = (data: {
|
export const sendContributionDeniedEmail = (data: {
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
|
|||||||
16
backend/src/emails/templates/contributionDeleted/html.pug
Normal file
16
backend/src/emails/templates/contributionDeleted/html.pug
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.contributionDeleted.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.contributionDeleted.subject')
|
||||||
|
#container.col
|
||||||
|
include ../hello.pug
|
||||||
|
p= t('emails.contributionDeleted.commonGoodContributionDeleted', { senderFirstName, senderLastName, contributionMemo })
|
||||||
|
p= t('emails.contributionDeleted.toSeeContributionsAndMessages')
|
||||||
|
p
|
||||||
|
= t('emails.general.linkToYourAccount')
|
||||||
|
= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
include ../greatingFormularImprint.pug
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.contributionDeleted.subject')
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { EventProtocol } from '@entity/EventProtocol'
|
|
||||||
import decimal from 'decimal.js-light'
|
import decimal from 'decimal.js-light'
|
||||||
import { EventProtocolType } from './EventProtocolType'
|
import { EventProtocolType } from './EventProtocolType'
|
||||||
|
|
||||||
@ -68,6 +67,7 @@ export class EventTransactionReceiveRedeem extends EventBasicTxX {}
|
|||||||
export class EventContributionCreate extends EventBasicCt {}
|
export class EventContributionCreate extends EventBasicCt {}
|
||||||
export class EventAdminContributionCreate extends EventBasicCt {}
|
export class EventAdminContributionCreate extends EventBasicCt {}
|
||||||
export class EventAdminContributionDelete extends EventBasicCt {}
|
export class EventAdminContributionDelete extends EventBasicCt {}
|
||||||
|
export class EventAdminContributionDeny extends EventBasicCt {}
|
||||||
export class EventAdminContributionUpdate extends EventBasicCt {}
|
export class EventAdminContributionUpdate extends EventBasicCt {}
|
||||||
export class EventUserCreateContributionMessage extends EventBasicCtMsg {}
|
export class EventUserCreateContributionMessage extends EventBasicCtMsg {}
|
||||||
export class EventAdminCreateContributionMessage extends EventBasicCtMsg {}
|
export class EventAdminCreateContributionMessage extends EventBasicCtMsg {}
|
||||||
@ -87,21 +87,6 @@ export class EventDeleteContributionLink extends EventBasicCt {}
|
|||||||
export class EventUpdateContributionLink extends EventBasicCt {}
|
export class EventUpdateContributionLink extends EventBasicCt {}
|
||||||
|
|
||||||
export class Event {
|
export class Event {
|
||||||
constructor()
|
|
||||||
constructor(event?: EventProtocol) {
|
|
||||||
if (event) {
|
|
||||||
this.id = event.id
|
|
||||||
this.type = event.type
|
|
||||||
this.createdAt = event.createdAt
|
|
||||||
this.userId = event.userId
|
|
||||||
this.xUserId = event.xUserId
|
|
||||||
this.xCommunityId = event.xCommunityId
|
|
||||||
this.transactionId = event.transactionId
|
|
||||||
this.contributionId = event.contributionId
|
|
||||||
this.amount = event.amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public setEventBasic(): Event {
|
public setEventBasic(): Event {
|
||||||
this.type = EventProtocolType.BASIC
|
this.type = EventProtocolType.BASIC
|
||||||
this.createdAt = new Date()
|
this.createdAt = new Date()
|
||||||
@ -314,6 +299,13 @@ export class Event {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setEventAdminContributionDeny(ev: EventAdminContributionDeny): Event {
|
||||||
|
this.setByBasicCt(ev.userId, ev.contributionId, ev.amount)
|
||||||
|
this.type = EventProtocolType.ADMIN_CONTRIBUTION_DENY
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
public setEventAdminContributionUpdate(ev: EventAdminContributionUpdate): Event {
|
public setEventAdminContributionUpdate(ev: EventAdminContributionUpdate): Event {
|
||||||
this.setByBasicCt(ev.userId, ev.contributionId, ev.amount)
|
this.setByBasicCt(ev.userId, ev.contributionId, ev.amount)
|
||||||
this.type = EventProtocolType.ADMIN_CONTRIBUTION_UPDATE
|
this.type = EventProtocolType.ADMIN_CONTRIBUTION_UPDATE
|
||||||
|
|||||||
@ -1,41 +1,17 @@
|
|||||||
import { Event } from '@/event/Event'
|
import { Event } from '@/event/Event'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { EventProtocol } from '@entity/EventProtocol'
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
class EventProtocolEmitter {
|
export const writeEvent = async (event: Event): Promise<EventProtocol | null> => {
|
||||||
/* }extends EventEmitter { */
|
logger.info('writeEvent', event)
|
||||||
private events: Event[]
|
|
||||||
|
|
||||||
/*
|
|
||||||
public addEvent(event: Event) {
|
|
||||||
this.events.push(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEvents(): Event[] {
|
|
||||||
return this.events
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public isDisabled() {
|
|
||||||
logger.info(`EventProtocol - isDisabled=${CONFIG.EVENT_PROTOCOL_DISABLED}`)
|
|
||||||
return CONFIG.EVENT_PROTOCOL_DISABLED === true
|
|
||||||
}
|
|
||||||
|
|
||||||
public async writeEvent(event: Event): Promise<void> {
|
|
||||||
if (!eventProtocol.isDisabled()) {
|
|
||||||
logger.info(`writeEvent(${JSON.stringify(event)})`)
|
|
||||||
const dbEvent = new EventProtocol()
|
const dbEvent = new EventProtocol()
|
||||||
dbEvent.type = event.type
|
dbEvent.type = event.type
|
||||||
dbEvent.createdAt = event.createdAt
|
dbEvent.createdAt = event.createdAt
|
||||||
dbEvent.userId = event.userId
|
dbEvent.userId = event.userId
|
||||||
if (event.xUserId) dbEvent.xUserId = event.xUserId
|
dbEvent.xUserId = event.xUserId || null
|
||||||
if (event.xCommunityId) dbEvent.xCommunityId = event.xCommunityId
|
dbEvent.xCommunityId = event.xCommunityId || null
|
||||||
if (event.contributionId) dbEvent.contributionId = event.contributionId
|
dbEvent.contributionId = event.contributionId || null
|
||||||
if (event.transactionId) dbEvent.transactionId = event.transactionId
|
dbEvent.transactionId = event.transactionId || null
|
||||||
if (event.amount) dbEvent.amount = event.amount
|
dbEvent.amount = event.amount || null
|
||||||
await dbEvent.save()
|
return dbEvent.save()
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
export const eventProtocol = new EventProtocolEmitter()
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export enum EventProtocolType {
|
|||||||
CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE',
|
CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE',
|
||||||
ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE',
|
ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE',
|
||||||
ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE',
|
ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE',
|
||||||
|
ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY',
|
||||||
ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE',
|
ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE',
|
||||||
USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE',
|
USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE',
|
||||||
ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
|
ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import { calculateDecay } from '@/util/decay'
|
|||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { GdtResolver } from './GdtResolver'
|
import { GdtResolver } from './GdtResolver'
|
||||||
|
|
||||||
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class BalanceResolver {
|
export class BalanceResolver {
|
||||||
@Authorized([RIGHTS.BALANCE])
|
@Authorized([RIGHTS.BALANCE])
|
||||||
@ -32,7 +34,7 @@ export class BalanceResolver {
|
|||||||
|
|
||||||
const lastTransaction = context.lastTransaction
|
const lastTransaction = context.lastTransaction
|
||||||
? context.lastTransaction
|
? context.lastTransaction
|
||||||
: await dbTransaction.findOne({ userId: user.id }, { order: { id: 'DESC' } })
|
: await getLastTransaction(user.id)
|
||||||
|
|
||||||
logger.debug(`lastTransaction=${lastTransaction}`)
|
logger.debug(`lastTransaction=${lastTransaction}`)
|
||||||
|
|
||||||
|
|||||||
@ -319,27 +319,6 @@ describe('Contribution Links', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an error if name is an empty string', async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await expect(
|
|
||||||
mutate({
|
|
||||||
mutation: createContributionLink,
|
|
||||||
variables: {
|
|
||||||
...variables,
|
|
||||||
name: '',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
errors: [new GraphQLError('The name must be initialized')],
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
|
||||||
expect(logger.error).toBeCalledWith('The name must be initialized')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns an error if name is shorter than 5 characters', async () => {
|
it('returns an error if name is shorter than 5 characters', async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
@ -382,27 +361,6 @@ describe('Contribution Links', () => {
|
|||||||
expect(logger.error).toBeCalledWith('The value of name is too long', 101)
|
expect(logger.error).toBeCalledWith('The value of name is too long', 101)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an error if memo is an empty string', async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await expect(
|
|
||||||
mutate({
|
|
||||||
mutation: createContributionLink,
|
|
||||||
variables: {
|
|
||||||
...variables,
|
|
||||||
memo: '',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
errors: [new GraphQLError('The memo must be initialized')],
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
|
||||||
expect(logger.error).toBeCalledWith('The memo must be initialized')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns an error if memo is shorter than 5 characters', async () => {
|
it('returns an error if memo is shorter than 5 characters', async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
|
|||||||
@ -40,20 +40,12 @@ export class ContributionLinkResolver {
|
|||||||
}: ContributionLinkArgs,
|
}: ContributionLinkArgs,
|
||||||
): Promise<ContributionLink> {
|
): Promise<ContributionLink> {
|
||||||
isStartEndDateValid(validFrom, validTo)
|
isStartEndDateValid(validFrom, validTo)
|
||||||
// TODO: this should be enforced by the schema.
|
|
||||||
if (!name) {
|
|
||||||
throw new LogError('The name must be initialized')
|
|
||||||
}
|
|
||||||
if (name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS) {
|
if (name.length < CONTRIBUTIONLINK_NAME_MIN_CHARS) {
|
||||||
throw new LogError('The value of name is too short', name.length)
|
throw new LogError('The value of name is too short', name.length)
|
||||||
}
|
}
|
||||||
if (name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS) {
|
if (name.length > CONTRIBUTIONLINK_NAME_MAX_CHARS) {
|
||||||
throw new LogError('The value of name is too long', name.length)
|
throw new LogError('The value of name is too long', name.length)
|
||||||
}
|
}
|
||||||
// TODO: this should be enforced by the schema.
|
|
||||||
if (!memo) {
|
|
||||||
throw new LogError('The memo must be initialized')
|
|
||||||
}
|
|
||||||
if (memo.length < MEMO_MIN_CHARS) {
|
if (memo.length < MEMO_MIN_CHARS) {
|
||||||
throw new LogError('The value of memo is too short', memo.length)
|
throw new LogError('The value of memo is too short', memo.length)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,14 +99,18 @@ describe('ContributionMessageResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('ContributionMessage was not sent successfully')],
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||||
|
),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
'ContributionMessage was not sent successfully',
|
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||||
new Error('Contribution not found'),
|
new Error('Contribution not found'),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -135,14 +139,18 @@ describe('ContributionMessageResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('ContributionMessage was not sent successfully')],
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution',
|
||||||
|
),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
'ContributionMessage was not sent successfully',
|
'ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution',
|
||||||
new Error('Admin can not answer on his own contribution'),
|
new Error('Admin can not answer on his own contribution'),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -229,14 +237,18 @@ describe('ContributionMessageResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('ContributionMessage was not sent successfully')],
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||||
|
),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
'ContributionMessage was not sent successfully',
|
'ContributionMessage was not sent successfully: Error: Contribution not found',
|
||||||
new Error('Contribution not found'),
|
new Error('Contribution not found'),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -257,14 +269,18 @@ describe('ContributionMessageResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('ContributionMessage was not sent successfully')],
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user',
|
||||||
|
),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
'ContributionMessage was not sent successfully',
|
'ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user',
|
||||||
new Error('Can not send message to contribution of another user'),
|
new Error('Can not send message to contribution of another user'),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export class ContributionMessageResolver {
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw new LogError('ContributionMessage was not sent successfully', e)
|
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ export class ContributionMessageResolver {
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw new LogError('ContributionMessage was not sent successfully', e)
|
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,11 +22,7 @@ import {
|
|||||||
listContributions,
|
listContributions,
|
||||||
listUnconfirmedContributions,
|
listUnconfirmedContributions,
|
||||||
} from '@/seeds/graphql/queries'
|
} from '@/seeds/graphql/queries'
|
||||||
import {
|
import { sendContributionConfirmedEmail } from '@/emails/sendEmailVariants'
|
||||||
// sendAccountActivationEmail,
|
|
||||||
sendContributionConfirmedEmail,
|
|
||||||
// sendContributionRejectedEmail,
|
|
||||||
} from '@/emails/sendEmailVariants'
|
|
||||||
import {
|
import {
|
||||||
cleanDB,
|
cleanDB,
|
||||||
resetToken,
|
resetToken,
|
||||||
@ -48,7 +44,6 @@ import { logger, i18n as localization } from '@test/testSetup'
|
|||||||
import { UserInputError } from 'apollo-server-express'
|
import { UserInputError } from 'apollo-server-express'
|
||||||
import { ContributionStatus } from '../enum/ContributionStatus'
|
import { ContributionStatus } from '../enum/ContributionStatus'
|
||||||
|
|
||||||
// mock account activation email to avoid console spam
|
|
||||||
// mock account activation email to avoid console spam
|
// mock account activation email to avoid console spam
|
||||||
jest.mock('@/emails/sendEmailVariants', () => {
|
jest.mock('@/emails/sendEmailVariants', () => {
|
||||||
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||||
@ -536,7 +531,6 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: why is this here - this is a different call `adminUpdateContribution` not `updateContribution`
|
|
||||||
describe('admin tries to update a user contribution', () => {
|
describe('admin tries to update a user contribution', () => {
|
||||||
it('throws an error', async () => {
|
it('throws an error', async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
|
|||||||
@ -44,17 +44,21 @@ import {
|
|||||||
EventContributionConfirm,
|
EventContributionConfirm,
|
||||||
EventAdminContributionCreate,
|
EventAdminContributionCreate,
|
||||||
EventAdminContributionDelete,
|
EventAdminContributionDelete,
|
||||||
|
EventAdminContributionDeny,
|
||||||
EventAdminContributionUpdate,
|
EventAdminContributionUpdate,
|
||||||
} from '@/event/Event'
|
} from '@/event/Event'
|
||||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
import { writeEvent } from '@/event/EventProtocolEmitter'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
import {
|
import {
|
||||||
sendContributionConfirmedEmail,
|
sendContributionConfirmedEmail,
|
||||||
|
sendContributionDeletedEmail,
|
||||||
sendContributionDeniedEmail,
|
sendContributionDeniedEmail,
|
||||||
} from '@/emails/sendEmailVariants'
|
} from '@/emails/sendEmailVariants'
|
||||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||||
import LogError from '@/server/LogError'
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class ContributionResolver {
|
export class ContributionResolver {
|
||||||
@Authorized([RIGHTS.CREATE_CONTRIBUTION])
|
@Authorized([RIGHTS.CREATE_CONTRIBUTION])
|
||||||
@ -95,7 +99,7 @@ export class ContributionResolver {
|
|||||||
eventCreateContribution.userId = user.id
|
eventCreateContribution.userId = user.id
|
||||||
eventCreateContribution.amount = amount
|
eventCreateContribution.amount = amount
|
||||||
eventCreateContribution.contributionId = contribution.id
|
eventCreateContribution.contributionId = contribution.id
|
||||||
await eventProtocol.writeEvent(event.setEventContributionCreate(eventCreateContribution))
|
await writeEvent(event.setEventContributionCreate(eventCreateContribution))
|
||||||
|
|
||||||
return new UnconfirmedContribution(contribution, user, creations)
|
return new UnconfirmedContribution(contribution, user, creations)
|
||||||
}
|
}
|
||||||
@ -128,7 +132,7 @@ export class ContributionResolver {
|
|||||||
eventDeleteContribution.userId = user.id
|
eventDeleteContribution.userId = user.id
|
||||||
eventDeleteContribution.contributionId = contribution.id
|
eventDeleteContribution.contributionId = contribution.id
|
||||||
eventDeleteContribution.amount = contribution.amount
|
eventDeleteContribution.amount = contribution.amount
|
||||||
await eventProtocol.writeEvent(event.setEventContributionDelete(eventDeleteContribution))
|
await writeEvent(event.setEventContributionDelete(eventDeleteContribution))
|
||||||
|
|
||||||
const res = await contribution.softRemove()
|
const res = await contribution.softRemove()
|
||||||
return !!res
|
return !!res
|
||||||
@ -281,7 +285,7 @@ export class ContributionResolver {
|
|||||||
eventUpdateContribution.userId = user.id
|
eventUpdateContribution.userId = user.id
|
||||||
eventUpdateContribution.contributionId = contributionId
|
eventUpdateContribution.contributionId = contributionId
|
||||||
eventUpdateContribution.amount = amount
|
eventUpdateContribution.amount = amount
|
||||||
await eventProtocol.writeEvent(event.setEventContributionUpdate(eventUpdateContribution))
|
await writeEvent(event.setEventContributionUpdate(eventUpdateContribution))
|
||||||
|
|
||||||
return new UnconfirmedContribution(contributionToUpdate, user, creations)
|
return new UnconfirmedContribution(contributionToUpdate, user, creations)
|
||||||
}
|
}
|
||||||
@ -343,9 +347,7 @@ export class ContributionResolver {
|
|||||||
eventAdminCreateContribution.userId = moderator.id
|
eventAdminCreateContribution.userId = moderator.id
|
||||||
eventAdminCreateContribution.amount = amount
|
eventAdminCreateContribution.amount = amount
|
||||||
eventAdminCreateContribution.contributionId = contribution.id
|
eventAdminCreateContribution.contributionId = contribution.id
|
||||||
await eventProtocol.writeEvent(
|
await writeEvent(event.setEventAdminContributionCreate(eventAdminCreateContribution))
|
||||||
event.setEventAdminContributionCreate(eventAdminCreateContribution),
|
|
||||||
)
|
|
||||||
|
|
||||||
return getUserCreation(emailContact.userId, clientTimezoneOffset)
|
return getUserCreation(emailContact.userId, clientTimezoneOffset)
|
||||||
}
|
}
|
||||||
@ -445,9 +447,7 @@ export class ContributionResolver {
|
|||||||
eventAdminContributionUpdate.userId = emailContact.user.id
|
eventAdminContributionUpdate.userId = emailContact.user.id
|
||||||
eventAdminContributionUpdate.amount = amount
|
eventAdminContributionUpdate.amount = amount
|
||||||
eventAdminContributionUpdate.contributionId = contributionToUpdate.id
|
eventAdminContributionUpdate.contributionId = contributionToUpdate.id
|
||||||
await eventProtocol.writeEvent(
|
await writeEvent(event.setEventAdminContributionUpdate(eventAdminContributionUpdate))
|
||||||
event.setEventAdminContributionUpdate(eventAdminContributionUpdate),
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -523,10 +523,8 @@ export class ContributionResolver {
|
|||||||
eventAdminContributionDelete.userId = contribution.userId
|
eventAdminContributionDelete.userId = contribution.userId
|
||||||
eventAdminContributionDelete.amount = contribution.amount
|
eventAdminContributionDelete.amount = contribution.amount
|
||||||
eventAdminContributionDelete.contributionId = contribution.id
|
eventAdminContributionDelete.contributionId = contribution.id
|
||||||
await eventProtocol.writeEvent(
|
await writeEvent(event.setEventAdminContributionDelete(eventAdminContributionDelete))
|
||||||
event.setEventAdminContributionDelete(eventAdminContributionDelete),
|
sendContributionDeletedEmail({
|
||||||
)
|
|
||||||
sendContributionDeniedEmail({
|
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
email: user.emailContact.email,
|
email: user.emailContact.email,
|
||||||
@ -582,16 +580,11 @@ export class ContributionResolver {
|
|||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
|
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
|
||||||
try {
|
|
||||||
const lastTransaction = await queryRunner.manager
|
const lastTransaction = await getLastTransaction(contribution.userId)
|
||||||
.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')
|
logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined')
|
||||||
|
|
||||||
|
try {
|
||||||
let newBalance = new Decimal(0)
|
let newBalance = new Decimal(0)
|
||||||
let decay: Decay | null = null
|
let decay: Decay | null = null
|
||||||
if (lastTransaction) {
|
if (lastTransaction) {
|
||||||
@ -647,7 +640,7 @@ export class ContributionResolver {
|
|||||||
eventContributionConfirm.userId = user.id
|
eventContributionConfirm.userId = user.id
|
||||||
eventContributionConfirm.amount = contribution.amount
|
eventContributionConfirm.amount = contribution.amount
|
||||||
eventContributionConfirm.contributionId = contribution.id
|
eventContributionConfirm.contributionId = contribution.id
|
||||||
await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm))
|
await writeEvent(event.setEventContributionConfirm(eventContributionConfirm))
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
@ -737,6 +730,13 @@ export class ContributionResolver {
|
|||||||
contributionToUpdate.deniedAt = new Date()
|
contributionToUpdate.deniedAt = new Date()
|
||||||
const res = await contributionToUpdate.save()
|
const res = await contributionToUpdate.save()
|
||||||
|
|
||||||
|
const event = new Event()
|
||||||
|
const eventAdminContributionDeny = new EventAdminContributionDeny()
|
||||||
|
eventAdminContributionDeny.userId = contributionToUpdate.userId
|
||||||
|
eventAdminContributionDeny.amount = contributionToUpdate.amount
|
||||||
|
eventAdminContributionDeny.contributionId = contributionToUpdate.id
|
||||||
|
await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny))
|
||||||
|
|
||||||
sendContributionDeniedEmail({
|
sendContributionDeniedEmail({
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
|
|||||||
@ -116,6 +116,11 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('redeemTransactionLink', () => {
|
describe('redeemTransactionLink', () => {
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
describe('contributionLink', () => {
|
describe('contributionLink', () => {
|
||||||
describe('input not valid', () => {
|
describe('input not valid', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -482,8 +487,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: there is a test not cleaning up after itself! Fix it!
|
afterAll(async () => {
|
||||||
beforeAll(async () => {
|
|
||||||
await cleanDB()
|
await cleanDB()
|
||||||
resetToken()
|
resetToken()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -34,6 +34,8 @@ import QueryLinkResult from '@union/QueryLinkResult'
|
|||||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||||
import LogError from '@/server/LogError'
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
|
||||||
// TODO: do not export, test it inside the resolver
|
// TODO: do not export, test it inside the resolver
|
||||||
export const transactionLinkCode = (date: Date): string => {
|
export const transactionLinkCode = (date: Date): string => {
|
||||||
const time = date.getTime().toString(16)
|
const time = date.getTime().toString(16)
|
||||||
@ -262,13 +264,7 @@ export class TransactionLinkResolver {
|
|||||||
|
|
||||||
await queryRunner.manager.insert(DbContribution, contribution)
|
await queryRunner.manager.insert(DbContribution, contribution)
|
||||||
|
|
||||||
const lastTransaction = await queryRunner.manager
|
const lastTransaction = await getLastTransaction(user.id)
|
||||||
.createQueryBuilder()
|
|
||||||
.select('transaction')
|
|
||||||
.from(DbTransaction, 'transaction')
|
|
||||||
.where('transaction.userId = :id', { id: user.id })
|
|
||||||
.orderBy('transaction.id', 'DESC')
|
|
||||||
.getOne()
|
|
||||||
let newBalance = new Decimal(0)
|
let newBalance = new Decimal(0)
|
||||||
|
|
||||||
let decay: Decay | null = null
|
let decay: Decay | null = null
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import {
|
|||||||
sendTransactionReceivedEmail,
|
sendTransactionReceivedEmail,
|
||||||
} from '@/emails/sendEmailVariants'
|
} from '@/emails/sendEmailVariants'
|
||||||
import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event'
|
import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event'
|
||||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
import { writeEvent } from '@/event/EventProtocolEmitter'
|
||||||
|
|
||||||
import { BalanceResolver } from './BalanceResolver'
|
import { BalanceResolver } from './BalanceResolver'
|
||||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||||
@ -39,6 +39,8 @@ import { findUserByEmail } from './UserResolver'
|
|||||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||||
import LogError from '@/server/LogError'
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
|
||||||
export const executeTransaction = async (
|
export const executeTransaction = async (
|
||||||
amount: Decimal,
|
amount: Decimal,
|
||||||
memo: string,
|
memo: string,
|
||||||
@ -141,16 +143,14 @@ export const executeTransaction = async (
|
|||||||
eventTransactionSend.xUserId = transactionSend.linkedUserId
|
eventTransactionSend.xUserId = transactionSend.linkedUserId
|
||||||
eventTransactionSend.transactionId = transactionSend.id
|
eventTransactionSend.transactionId = transactionSend.id
|
||||||
eventTransactionSend.amount = transactionSend.amount.mul(-1)
|
eventTransactionSend.amount = transactionSend.amount.mul(-1)
|
||||||
await eventProtocol.writeEvent(new Event().setEventTransactionSend(eventTransactionSend))
|
await writeEvent(new Event().setEventTransactionSend(eventTransactionSend))
|
||||||
|
|
||||||
const eventTransactionReceive = new EventTransactionReceive()
|
const eventTransactionReceive = new EventTransactionReceive()
|
||||||
eventTransactionReceive.userId = transactionReceive.userId
|
eventTransactionReceive.userId = transactionReceive.userId
|
||||||
eventTransactionReceive.xUserId = transactionReceive.linkedUserId
|
eventTransactionReceive.xUserId = transactionReceive.linkedUserId
|
||||||
eventTransactionReceive.transactionId = transactionReceive.id
|
eventTransactionReceive.transactionId = transactionReceive.id
|
||||||
eventTransactionReceive.amount = transactionReceive.amount
|
eventTransactionReceive.amount = transactionReceive.amount
|
||||||
await eventProtocol.writeEvent(
|
await writeEvent(new Event().setEventTransactionReceive(eventTransactionReceive))
|
||||||
new Event().setEventTransactionReceive(eventTransactionReceive),
|
|
||||||
)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw new LogError('Transaction was not successful', e)
|
throw new LogError('Transaction was not successful', e)
|
||||||
@ -204,10 +204,7 @@ export class TransactionResolver {
|
|||||||
logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`)
|
logger.info(`transactionList(user=${user.firstName}.${user.lastName}, ${user.emailId})`)
|
||||||
|
|
||||||
// find current balance
|
// find current balance
|
||||||
const lastTransaction = await dbTransaction.findOne(
|
const lastTransaction = await getLastTransaction(user.id, ['contribution'])
|
||||||
{ userId: user.id },
|
|
||||||
{ order: { id: 'DESC' }, relations: ['contribution'] },
|
|
||||||
)
|
|
||||||
logger.debug(`lastTransaction=${lastTransaction}`)
|
logger.debug(`lastTransaction=${lastTransaction}`)
|
||||||
|
|
||||||
const balanceResolver = new BalanceResolver()
|
const balanceResolver = new BalanceResolver()
|
||||||
|
|||||||
@ -48,7 +48,7 @@ import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddle
|
|||||||
import { klicktippSignIn } from '@/apis/KlicktippController'
|
import { klicktippSignIn } from '@/apis/KlicktippController'
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
import { writeEvent } from '@/event/EventProtocolEmitter'
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
EventLogin,
|
EventLogin,
|
||||||
@ -179,7 +179,7 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
const ev = new EventLogin()
|
const ev = new EventLogin()
|
||||||
ev.userId = user.id
|
ev.userId = user.id
|
||||||
eventProtocol.writeEvent(new Event().setEventLogin(ev))
|
writeEvent(new Event().setEventLogin(ev))
|
||||||
logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`)
|
logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
@ -251,9 +251,7 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail()
|
const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail()
|
||||||
eventSendAccountMultiRegistrationEmail.userId = foundUser.id
|
eventSendAccountMultiRegistrationEmail.userId = foundUser.id
|
||||||
eventProtocol.writeEvent(
|
writeEvent(event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail))
|
||||||
event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail),
|
|
||||||
)
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`,
|
`sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`,
|
||||||
)
|
)
|
||||||
@ -336,7 +334,7 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
||||||
eventSendConfirmEmail.userId = dbUser.id
|
eventSendConfirmEmail.userId = dbUser.id
|
||||||
eventProtocol.writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail))
|
writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail))
|
||||||
|
|
||||||
if (!emailSent) {
|
if (!emailSent) {
|
||||||
logger.debug(`Account confirmation link: ${activationLink}`)
|
logger.debug(`Account confirmation link: ${activationLink}`)
|
||||||
@ -354,10 +352,10 @@ export class UserResolver {
|
|||||||
|
|
||||||
if (redeemCode) {
|
if (redeemCode) {
|
||||||
eventRedeemRegister.userId = dbUser.id
|
eventRedeemRegister.userId = dbUser.id
|
||||||
await eventProtocol.writeEvent(event.setEventRedeemRegister(eventRedeemRegister))
|
await writeEvent(event.setEventRedeemRegister(eventRedeemRegister))
|
||||||
} else {
|
} else {
|
||||||
eventRegister.userId = dbUser.id
|
eventRegister.userId = dbUser.id
|
||||||
await eventProtocol.writeEvent(event.setEventRegister(eventRegister))
|
await writeEvent(event.setEventRegister(eventRegister))
|
||||||
}
|
}
|
||||||
|
|
||||||
return new User(dbUser)
|
return new User(dbUser)
|
||||||
@ -477,7 +475,7 @@ export class UserResolver {
|
|||||||
|
|
||||||
const eventActivateAccount = new EventActivateAccount()
|
const eventActivateAccount = new EventActivateAccount()
|
||||||
eventActivateAccount.userId = user.id
|
eventActivateAccount.userId = user.id
|
||||||
eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount))
|
writeEvent(event.setEventActivateAccount(eventActivateAccount))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw new LogError('Error on writing User and User Contact data', e)
|
throw new LogError('Error on writing User and User Contact data', e)
|
||||||
@ -824,9 +822,7 @@ export class UserResolver {
|
|||||||
const event = new Event()
|
const event = new Event()
|
||||||
const eventSendConfirmationEmail = new EventSendConfirmationEmail()
|
const eventSendConfirmationEmail = new EventSendConfirmationEmail()
|
||||||
eventSendConfirmationEmail.userId = user.id
|
eventSendConfirmationEmail.userId = user.id
|
||||||
await eventProtocol.writeEvent(
|
await writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmationEmail))
|
||||||
event.setEventSendConfirmationEmail(eventSendConfirmationEmail),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
14
backend/src/graphql/resolver/util/getLastTransaction.ts
Normal file
14
backend/src/graphql/resolver/util/getLastTransaction.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
|
|
||||||
|
export const getLastTransaction = async (
|
||||||
|
userId: number,
|
||||||
|
relations?: string[],
|
||||||
|
): Promise<DbTransaction | undefined> => {
|
||||||
|
return DbTransaction.findOne(
|
||||||
|
{ userId },
|
||||||
|
{
|
||||||
|
order: { balanceDate: 'DESC', id: 'DESC' },
|
||||||
|
relations,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
"addedContributionMessage": {
|
|
||||||
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
|
||||||
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
|
||||||
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
|
||||||
},
|
|
||||||
"accountActivation": {
|
"accountActivation": {
|
||||||
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:",
|
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:",
|
||||||
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
||||||
@ -19,12 +14,22 @@
|
|||||||
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
||||||
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
||||||
},
|
},
|
||||||
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
||||||
|
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
||||||
|
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
|
},
|
||||||
"contributionConfirmed": {
|
"contributionConfirmed": {
|
||||||
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
||||||
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
||||||
},
|
},
|
||||||
"contributionRejected": {
|
"contributionDeleted": {
|
||||||
"commonGoodContributionRejected": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.",
|
"commonGoodContributionDeleted": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} gelöscht.",
|
||||||
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde gelöscht",
|
||||||
|
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
|
},
|
||||||
|
"contributionDenied": {
|
||||||
|
"commonGoodContributionDenied": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.",
|
||||||
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde abgelehnt",
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde abgelehnt",
|
||||||
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
"addedContributionMessage": {
|
|
||||||
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
|
||||||
"subject": "Gradido: Message about your common good contribution",
|
|
||||||
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
|
||||||
},
|
|
||||||
"accountActivation": {
|
"accountActivation": {
|
||||||
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:",
|
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:",
|
||||||
"emailRegistered": "Your email address has just been registered with Gradido.",
|
"emailRegistered": "Your email address has just been registered with Gradido.",
|
||||||
@ -19,10 +14,20 @@
|
|||||||
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
||||||
"subject": "Gradido: Try To Register Again With Your Email"
|
"subject": "Gradido: Try To Register Again With Your Email"
|
||||||
},
|
},
|
||||||
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
||||||
|
"subject": "Gradido: Message about your common good contribution",
|
||||||
|
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||||
|
},
|
||||||
"contributionConfirmed": {
|
"contributionConfirmed": {
|
||||||
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
||||||
"subject": "Gradido: Your contribution to the common good was confirmed"
|
"subject": "Gradido: Your contribution to the common good was confirmed"
|
||||||
},
|
},
|
||||||
|
"contributionDeleted": {
|
||||||
|
"commonGoodContributionDeleted": "Your public good contribution “{contributionMemo}” was deleted by {senderFirstName} {senderLastName}.",
|
||||||
|
"subject": "Gradido: Your common good contribution was deleted",
|
||||||
|
"toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||||
|
},
|
||||||
"contributionDenied": {
|
"contributionDenied": {
|
||||||
"commonGoodContributionDenied": "Your public good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.",
|
"commonGoodContributionDenied": "Your public good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.",
|
||||||
"subject": "Gradido: Your common good contribution was rejected",
|
"subject": "Gradido: Your common good contribution was rejected",
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { UserInterface } from './UserInterface'
|
import { UserInterface } from './UserInterface'
|
||||||
|
|
||||||
// TODO: the generated email_contact is not deleted
|
|
||||||
export const stephenHawking: UserInterface = {
|
export const stephenHawking: UserInterface = {
|
||||||
email: 'stephen@hawking.uk',
|
email: 'stephen@hawking.uk',
|
||||||
firstName: 'Stephen',
|
firstName: 'Stephen',
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { calculateDecay } from './decay'
|
import { calculateDecay } from './decay'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Transaction } from '@entity/Transaction'
|
|
||||||
import { Decay } from '@model/Decay'
|
import { Decay } from '@model/Decay'
|
||||||
import { getCustomRepository } from '@dbTools/typeorm'
|
import { getCustomRepository } from '@dbTools/typeorm'
|
||||||
import { TransactionLinkRepository } from '@repository/TransactionLink'
|
import { TransactionLinkRepository } from '@repository/TransactionLink'
|
||||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||||
|
import { getLastTransaction } from '../graphql/resolver/util/getLastTransaction'
|
||||||
|
|
||||||
function isStringBoolean(value: string): boolean {
|
function isStringBoolean(value: string): boolean {
|
||||||
const lowerValue = value.toLowerCase()
|
const lowerValue = value.toLowerCase()
|
||||||
@ -20,7 +20,7 @@ async function calculateBalance(
|
|||||||
time: Date,
|
time: Date,
|
||||||
transactionLink?: dbTransactionLink | null,
|
transactionLink?: dbTransactionLink | null,
|
||||||
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> {
|
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> {
|
||||||
const lastTransaction = await Transaction.findOne({ userId }, { order: { id: 'DESC' } })
|
const lastTransaction = await getLastTransaction(userId)
|
||||||
if (!lastTransaction) return null
|
if (!lastTransaction) return null
|
||||||
|
|
||||||
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
|
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
CONFIG_VERSION=v1.2022-03-18
|
|
||||||
|
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
DB_USER=root
|
DB_USER=root
|
||||||
|
|||||||
@ -16,17 +16,17 @@ export class EventProtocol extends BaseEntity {
|
|||||||
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
||||||
userId: number
|
userId: number
|
||||||
|
|
||||||
@Column({ name: 'x_user_id', unsigned: true, nullable: true })
|
@Column({ name: 'x_user_id', type: 'int', unsigned: true, nullable: true })
|
||||||
xUserId: number
|
xUserId: number | null
|
||||||
|
|
||||||
@Column({ name: 'x_community_id', unsigned: true, nullable: true })
|
@Column({ name: 'x_community_id', type: 'int', unsigned: true, nullable: true })
|
||||||
xCommunityId: number
|
xCommunityId: number | null
|
||||||
|
|
||||||
@Column({ name: 'transaction_id', unsigned: true, nullable: true })
|
@Column({ name: 'transaction_id', type: 'int', unsigned: true, nullable: true })
|
||||||
transactionId: number
|
transactionId: number | null
|
||||||
|
|
||||||
@Column({ name: 'contribution_id', unsigned: true, nullable: true })
|
@Column({ name: 'contribution_id', type: 'int', unsigned: true, nullable: true })
|
||||||
contributionId: number
|
contributionId: number | null
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
type: 'decimal',
|
type: 'decimal',
|
||||||
@ -35,8 +35,8 @@ export class EventProtocol extends BaseEntity {
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
transformer: DecimalTransformer,
|
transformer: DecimalTransformer,
|
||||||
})
|
})
|
||||||
amount: Decimal
|
amount: Decimal | null
|
||||||
|
|
||||||
@Column({ name: 'message_id', unsigned: true, nullable: true })
|
@Column({ name: 'message_id', type: 'int', unsigned: true, nullable: true })
|
||||||
messageId: number
|
messageId: number | null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-database",
|
"name": "gradido-database",
|
||||||
"version": "1.17.1",
|
"version": "1.18.2",
|
||||||
"description": "Gradido Database Tool to execute database migrations",
|
"description": "Gradido Database Tool to execute database migrations",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/database",
|
"repository": "https://github.com/gradido/gradido/database",
|
||||||
|
|||||||
@ -27,7 +27,7 @@ COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
|
|||||||
COMMUNITY_SUPPORT_MAIL=support@supportmail.com
|
COMMUNITY_SUPPORT_MAIL=support@supportmail.com
|
||||||
|
|
||||||
# backend
|
# backend
|
||||||
BACKEND_CONFIG_VERSION=v14.2022-12-22
|
BACKEND_CONFIG_VERSION=v15.2023-02-07
|
||||||
|
|
||||||
JWT_EXPIRES_IN=10m
|
JWT_EXPIRES_IN=10m
|
||||||
GDT_API_URL=https://gdt.gradido.net
|
GDT_API_URL=https://gdt.gradido.net
|
||||||
@ -56,9 +56,6 @@ EMAIL_CODE_REQUEST_TIME=10
|
|||||||
|
|
||||||
WEBHOOK_ELOPAGE_SECRET=secret
|
WEBHOOK_ELOPAGE_SECRET=secret
|
||||||
|
|
||||||
# EventProtocol
|
|
||||||
EVENT_PROTOCOL_DISABLED=false
|
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen
|
# if you set the value of FEDERATION_DHT_TOPIC, the DHT hyperswarm will start to announce and listen
|
||||||
# on an hash created from this topic
|
# on an hash created from this topic
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
CONFIG_VERSION=v1.2023-01-01
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
@ -8,9 +6,6 @@ DB_PASSWORD=
|
|||||||
DB_DATABASE=gradido_community
|
DB_DATABASE=gradido_community
|
||||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log
|
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.dht-node.log
|
||||||
|
|
||||||
# EventProtocol
|
|
||||||
EVENT_PROTOCOL_DISABLED=false
|
|
||||||
|
|
||||||
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
# SET LOG LEVEL AS NEEDED IN YOUR .ENV
|
||||||
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
# POSSIBLE VALUES: all | trace | debug | info | warn | error | fatal
|
||||||
# LOG_LEVEL=info
|
# LOG_LEVEL=info
|
||||||
|
|||||||
@ -8,9 +8,6 @@ DB_PASSWORD=$DB_PASSWORD
|
|||||||
DB_DATABASE=gradido_community
|
DB_DATABASE=gradido_community
|
||||||
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
||||||
|
|
||||||
# EventProtocol
|
|
||||||
EVENT_PROTOCOL_DISABLED=$EVENT_PROTOCOL_DISABLED
|
|
||||||
|
|
||||||
# Federation
|
# Federation
|
||||||
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
FEDERATION_DHT_TOPIC=$FEDERATION_DHT_TOPIC
|
||||||
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
FEDERATION_DHT_SEED=$FEDERATION_DHT_SEED
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const constants = {
|
|||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
CONFIG_VERSION: {
|
CONFIG_VERSION: {
|
||||||
DEFAULT: 'DEFAULT',
|
DEFAULT: 'DEFAULT',
|
||||||
EXPECTED: 'v1.2023-01-01',
|
EXPECTED: 'v2.2023-02-07',
|
||||||
CURRENT: '',
|
CURRENT: '',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -28,11 +28,6 @@ const database = {
|
|||||||
process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.dht-node.log',
|
process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.dht-node.log',
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventProtocol = {
|
|
||||||
// global switch to enable writing of EventProtocol-Entries
|
|
||||||
EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false,
|
|
||||||
}
|
|
||||||
|
|
||||||
const federation = {
|
const federation = {
|
||||||
FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || 'GRADIDO_HUB',
|
FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || 'GRADIDO_HUB',
|
||||||
FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
||||||
@ -55,7 +50,6 @@ const CONFIG = {
|
|||||||
...constants,
|
...constants,
|
||||||
...server,
|
...server,
|
||||||
...database,
|
...database,
|
||||||
...eventProtocol,
|
|
||||||
...federation,
|
...federation,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,10 +52,6 @@ const community = {
|
|||||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// const eventProtocol = {
|
|
||||||
// global switch to enable writing of EventProtocol-Entries
|
|
||||||
// EVENT_PROTOCOL_DISABLED: process.env.EVENT_PROTOCOL_DISABLED === 'true' || false,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// This is needed by graphql-directive-auth
|
// This is needed by graphql-directive-auth
|
||||||
// process.env.APP_SECRET = server.JWT_SECRET
|
// process.env.APP_SECRET = server.JWT_SECRET
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
CONFIG_VERSION=v4.2022-12-20
|
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
DEFAULT_PUBLISHER_ID=2896
|
DEFAULT_PUBLISHER_ID=2896
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bootstrap-vue-gradido-wallet",
|
"name": "bootstrap-vue-gradido-wallet",
|
||||||
"version": "1.17.1",
|
"version": "1.18.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node run/server.js",
|
"start": "node run/server.js",
|
||||||
@ -104,5 +104,10 @@
|
|||||||
],
|
],
|
||||||
"author": "Gradido-Akademie - https://www.gradido.net/",
|
"author": "Gradido-Akademie - https://www.gradido.net/",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur."
|
"description": "Gradido, the Natural Economy of Life, is a way to worldwide prosperity and peace in harmony with nature. - Gradido, die Natürliche Ökonomie des lebens, ist ein Weg zu weltweitem Wohlstand und Frieden in Harmonie mit der Natur.",
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ignore": [
|
||||||
|
"**/*.spec.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,11 +24,6 @@ describe('ContributionForm', () => {
|
|||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => d),
|
||||||
$n: jest.fn((n) => n),
|
$n: jest.fn((n) => n),
|
||||||
$store: {
|
|
||||||
state: {
|
|
||||||
creation: ['1000', '1000', '1000'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
$i18n: {
|
$i18n: {
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
},
|
},
|
||||||
@ -61,7 +56,7 @@ describe('ContributionForm', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('dates', () => {
|
describe('dates and max amounts', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
form: {
|
form: {
|
||||||
@ -73,204 +68,176 @@ describe('ContributionForm', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('actual date', () => {
|
describe('max amount reached for both months', () => {
|
||||||
describe('same month', () => {
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({
|
||||||
|
maxGddLastMonth: 0,
|
||||||
|
maxGddThisMonth: 0,
|
||||||
|
})
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
id: null,
|
||||||
|
date: 'set',
|
||||||
|
memo: '',
|
||||||
|
amount: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows message that no contributions are available', () => {
|
||||||
|
expect(wrapper.find('[data-test="contribtion-message"]').text()).toBe(
|
||||||
|
'contribution.noOpenCreation.allMonth',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('max amount reached for last month, no date selected', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({
|
||||||
|
maxGddLastMonth: 0,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows no message', () => {
|
||||||
|
expect(wrapper.find('[data-test="contribtion-message"]').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('max amount reached for last month, last month selected', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const now = new Date().toISOString()
|
wrapper.setProps({
|
||||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
maxGddLastMonth: 0,
|
||||||
|
isThisMonth: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
|
||||||
it('has true', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe.skip('month before', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper
|
|
||||||
.findComponent({ name: 'BFormDatepicker' })
|
|
||||||
.vm.$emit('input', wrapper.vm.minimalDate)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
|
||||||
it('has false', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe.skip('date in middle of year', () => {
|
|
||||||
describe('same month', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
// jest.useFakeTimers('modern')
|
|
||||||
// jest.setSystemTime(new Date('2020-07-06'))
|
|
||||||
// await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
maximalDate: new Date(2020, 6, 6),
|
form: {
|
||||||
form: { date: new Date(2020, 6, 6) },
|
id: null,
|
||||||
|
date: 'set',
|
||||||
|
memo: '',
|
||||||
|
amount: '',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
it('shows message that no contributions are available for last month', () => {
|
||||||
it('has "2020-06-01T00:00:00.000Z"', () => {
|
expect(wrapper.find('[data-test="contribtion-message"]').text()).toBe(
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z')
|
'contribution.noOpenCreation.lastMonth',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
describe('max amount reached for last month, this month selected', () => {
|
||||||
it('has true', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('month before', () => {
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// jest.useFakeTimers('modern')
|
wrapper.setProps({
|
||||||
// jest.setSystemTime(new Date('2020-07-06'))
|
maxGddLastMonth: 0,
|
||||||
// console.log('middle of year date – now:', wrapper.vm.minimalDate)
|
isThisMonth: true,
|
||||||
// await wrapper
|
})
|
||||||
// .findComponent({ name: 'BFormDatepicker' })
|
|
||||||
// .vm.$emit('input', wrapper.vm.minimalDate)
|
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
maximalDate: new Date(2020, 6, 6),
|
form: {
|
||||||
form: { date: new Date(2020, 5, 6) },
|
id: null,
|
||||||
|
date: 'set',
|
||||||
|
memo: '',
|
||||||
|
amount: '',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
it('shows no message', () => {
|
||||||
it('has "2020-06-01T00:00:00.000Z"', () => {
|
expect(wrapper.find('[data-test="contribtion-message"]').exists()).toBe(false)
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
describe('max amount reached for this month, no date selected', () => {
|
||||||
it('has false', () => {
|
beforeEach(() => {
|
||||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
wrapper.setProps({
|
||||||
})
|
maxGddThisMonth: 0,
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('date in january', () => {
|
it('shows no message', () => {
|
||||||
describe('same month', () => {
|
expect(wrapper.find('[data-test="contribtion-message"]').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('max amount reached for this month, this month selected', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
wrapper.setProps({
|
||||||
|
maxGddThisMonth: 0,
|
||||||
|
isThisMonth: true,
|
||||||
|
})
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
maximalDate: new Date(2020, 0, 6),
|
form: {
|
||||||
form: { date: new Date(2020, 0, 6) },
|
id: null,
|
||||||
|
date: 'set',
|
||||||
|
memo: '',
|
||||||
|
amount: '',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
it('shows message that no contributions are available for last month', () => {
|
||||||
it('has "2019-12-01T00:00:00.000Z"', () => {
|
expect(wrapper.find('[data-test="contribtion-message"]').text()).toBe(
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z')
|
'contribution.noOpenCreation.thisMonth',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
describe('max amount reached for this month, last month selected', () => {
|
||||||
it('has true', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('month before', () => {
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// jest.useFakeTimers('modern')
|
wrapper.setProps({
|
||||||
// jest.setSystemTime(new Date('2020-07-06'))
|
maxGddThisMonth: 0,
|
||||||
// console.log('middle of year date – now:', wrapper.vm.minimalDate)
|
isThisMonth: false,
|
||||||
// await wrapper
|
})
|
||||||
// .findComponent({ name: 'BFormDatepicker' })
|
|
||||||
// .vm.$emit('input', wrapper.vm.minimalDate)
|
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
maximalDate: new Date(2020, 0, 6),
|
form: {
|
||||||
form: { date: new Date(2019, 11, 6) },
|
id: null,
|
||||||
|
date: 'set',
|
||||||
|
memo: '',
|
||||||
|
amount: '',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
it('shows no message', () => {
|
||||||
it('has "2019-12-01T00:00:00.000Z"', () => {
|
expect(wrapper.find('[data-test="contribtion-message"]').exists()).toBe(false)
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
|
||||||
it('has false', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('date with the 31st day of the month', () => {
|
describe('default return message', () => {
|
||||||
describe('same month', () => {
|
it('returns an empty string', () => {
|
||||||
beforeEach(async () => {
|
expect(wrapper.vm.noOpenCreation).toBe('')
|
||||||
await wrapper.setData({
|
|
||||||
maximalDate: new Date('2022-10-31T00:00:00.000Z'),
|
|
||||||
form: { date: new Date('2022-10-31T00:00:00.000Z') },
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
describe('update amount', () => {
|
||||||
it('has "2022-09-01T00:00:00.000Z"', () => {
|
beforeEach(() => {
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2022-09-01T00:00:00.000Z')
|
wrapper.findComponent({ name: 'InputHour' }).vm.$emit('updateAmount', 20)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates form amount', () => {
|
||||||
|
expect(wrapper.vm.form.amount).toBe('400.00')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
describe('watch value', () => {
|
||||||
it('has true', () => {
|
beforeEach(() => {
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
wrapper.setProps({
|
||||||
})
|
value: {
|
||||||
})
|
id: 42,
|
||||||
|
date: 'set',
|
||||||
|
memo: 'Some Memo',
|
||||||
|
amount: '400.00',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('date with the 28th day of the month', () => {
|
it('updates form', () => {
|
||||||
describe('same month', () => {
|
expect(wrapper.vm.form).toEqual({
|
||||||
beforeEach(async () => {
|
id: 42,
|
||||||
await wrapper.setData({
|
date: 'set',
|
||||||
maximalDate: new Date('2023-02-28T00:00:00.000Z'),
|
memo: 'Some Memo',
|
||||||
form: { date: new Date('2023-02-28T00:00:00.000Z') },
|
amount: '400.00',
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
|
||||||
it('has "2023-01-01T00:00:00.000Z"', () => {
|
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2023-01-01T00:00:00.000Z')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
|
||||||
it('has true', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe.skip('date with 29.02.2024 leap year', () => {
|
|
||||||
describe('same month', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.setData({
|
|
||||||
maximalDate: new Date('2024-02-29T00:00:00.000Z'),
|
|
||||||
form: { date: new Date('2024-02-29T00:00:00.000Z') },
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('minimalDate', () => {
|
|
||||||
it('has "2024-01-01T00:00:00.000Z"', () => {
|
|
||||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2024-01-01T00:00:00.000Z')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('isThisMonth', () => {
|
|
||||||
it('has true', () => {
|
|
||||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -477,24 +444,23 @@ describe('ContributionForm', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('on trigger submit', () => {
|
describe('on trigger submit', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.find('form').trigger('submit')
|
await wrapper.find('form').trigger('submit')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits "update-contribution"', () => {
|
it('emits "update-contribution"', () => {
|
||||||
expect(wrapper.emitted('update-contribution')).toEqual(
|
expect(wrapper.emitted('update-contribution')).toEqual([
|
||||||
expect.arrayContaining([
|
[
|
||||||
expect.arrayContaining([
|
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
date: now,
|
date: now,
|
||||||
|
hours: 0,
|
||||||
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
||||||
amount: '200',
|
amount: '200',
|
||||||
},
|
},
|
||||||
]),
|
],
|
||||||
]),
|
])
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -23,10 +23,7 @@
|
|||||||
<template #nav-next-year><span></span></template>
|
<template #nav-next-year><span></span></template>
|
||||||
</b-form-datepicker>
|
</b-form-datepicker>
|
||||||
|
|
||||||
<div
|
<div v-if="showMessage" class="p-3" data-test="contribtion-message">
|
||||||
v-if="(isThisMonth && maxGddThisMonth <= 0) || (!isThisMonth && maxGddLastMonth <= 0)"
|
|
||||||
class="p-3"
|
|
||||||
>
|
|
||||||
{{ noOpenCreation }}
|
{{ noOpenCreation }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
@ -118,8 +115,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateAmount(amount) {
|
updateAmount(hours) {
|
||||||
this.form.amount = (amount * 20).toFixed(2).toString()
|
this.form.amount = (hours * 20).toFixed(2).toString()
|
||||||
},
|
},
|
||||||
submit() {
|
submit() {
|
||||||
this.$emit(this.form.id ? 'update-contribution' : 'set-contribution', { ...this.form })
|
this.$emit(this.form.id ? 'update-contribution' : 'set-contribution', { ...this.form })
|
||||||
@ -135,6 +132,15 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
showMessage() {
|
||||||
|
if (this.maxGddThisMonth <= 0 && this.maxGddLastMonth <= 0) return true
|
||||||
|
if (this.form.date)
|
||||||
|
return (
|
||||||
|
(this.isThisMonth && this.maxGddThisMonth <= 0) ||
|
||||||
|
(!this.isThisMonth && this.maxGddLastMonth <= 0)
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
},
|
||||||
disabled() {
|
disabled() {
|
||||||
return (
|
return (
|
||||||
this.form.date === '' ||
|
this.form.date === '' ||
|
||||||
|
|||||||
@ -116,5 +116,15 @@ describe('ContributionList', () => {
|
|||||||
expect(wrapper.emitted('delete-contribution')).toEqual([[{ id: 2 }]])
|
expect(wrapper.emitted('delete-contribution')).toEqual([[{ id: 2 }]])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('update status', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.findComponent({ name: 'ContributionListItem' }).vm.$emit('update-state', { id: 2 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits update status', () => {
|
||||||
|
expect(wrapper.emitted('update-state')).toEqual([[{ id: 2 }]])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="decayinformation-startblock">
|
<div class="decayinformation-startblock">
|
||||||
|
<div class="my-4">
|
||||||
|
<div class="font-weight-bold pb-2">{{ $t('form.memo') }}</div>
|
||||||
|
<div>{{ memo }}</div>
|
||||||
|
</div>
|
||||||
<div class="mt-3 mb-3 text-center">
|
<div class="mt-3 mb-3 text-center">
|
||||||
<b>{{ $t('decay.before_startblock_transaction') }}</b>
|
<b>{{ $t('decay.before_startblock_transaction') }}</b>
|
||||||
</div>
|
</div>
|
||||||
@ -8,5 +12,11 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'DecayInformation-StartBlock',
|
name: 'DecayInformation-StartBlock',
|
||||||
|
props: {
|
||||||
|
memo: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="decay-information-box">
|
<div class="decay-information-box">
|
||||||
<decay-information-before-startblock v-if="decay.start === null" />
|
<decay-information-before-startblock v-if="decay.start === null" :memo="memo" />
|
||||||
<decay-information-decay-startblock
|
<decay-information-decay-startblock
|
||||||
v-else-if="isStartBlock"
|
v-else-if="isStartBlock"
|
||||||
:amount="amount"
|
:amount="amount"
|
||||||
|
|||||||
@ -179,6 +179,17 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
disabled() {
|
||||||
|
if (
|
||||||
|
this.form.email.length > 5 &&
|
||||||
|
parseInt(this.form.amount) <= parseInt(this.balance) &&
|
||||||
|
this.form.memo.length > 5 &&
|
||||||
|
this.form.memo.length <= 255
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
isBalanceDisabled() {
|
isBalanceDisabled() {
|
||||||
return this.balance <= 0 ? 'disabled' : false
|
return this.balance <= 0 ? 'disabled' : false
|
||||||
},
|
},
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export default {
|
|||||||
}
|
}
|
||||||
if (this.tokenExpiresInSeconds === 0) {
|
if (this.tokenExpiresInSeconds === 0) {
|
||||||
this.$timer.stop('tokenExpires')
|
this.$timer.stop('tokenExpires')
|
||||||
|
this.toastInfoNoHide(this.$t('session.automaticallyLoggedOut'))
|
||||||
this.$emit('logout')
|
this.$emit('logout')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -84,6 +85,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.$timer.stop('tokenExpires')
|
this.$timer.stop('tokenExpires')
|
||||||
|
this.toastInfoNoHide(this.$t('session.automaticallyLoggedOut'))
|
||||||
this.$emit('logout')
|
this.$emit('logout')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@ -173,7 +173,7 @@
|
|||||||
"GDD": "GDD",
|
"GDD": "GDD",
|
||||||
"gddKonto": "GDD Konto",
|
"gddKonto": "GDD Konto",
|
||||||
"gdd_per_link": {
|
"gdd_per_link": {
|
||||||
"choose-amount": "Wähle einen Betrag aus, welchen du per Link versenden möchtest. Du kannst auch noch eine Nachricht eintragen. Beim Klick „Jetzt generieren“ wird ein Link erstellt, den du versenden kannst.",
|
"choose-amount": "Wähle einen Betrag aus, welchen du per Link versenden möchtest, und trage eine Nachricht ein. Die Nachricht ist ein Pflichtfeld.",
|
||||||
"copy-link": "Link kopieren",
|
"copy-link": "Link kopieren",
|
||||||
"copy-link-with-text": "Link und Text kopieren",
|
"copy-link-with-text": "Link und Text kopieren",
|
||||||
"created": "Der Link wurde erstellt!",
|
"created": "Der Link wurde erstellt!",
|
||||||
@ -272,6 +272,7 @@
|
|||||||
"send_gdd": "GDD versenden",
|
"send_gdd": "GDD versenden",
|
||||||
"send_per_link": "GDD versenden per Link",
|
"send_per_link": "GDD versenden per Link",
|
||||||
"session": {
|
"session": {
|
||||||
|
"automaticallyLoggedOut": "Du wurdest automatisch abgemeldet",
|
||||||
"extend": "Angemeldet bleiben",
|
"extend": "Angemeldet bleiben",
|
||||||
"lightText": "Wenn du länger als 10 Minuten keine Aktion getätigt hast, wirst du aus Sicherheitsgründen abgemeldet.",
|
"lightText": "Wenn du länger als 10 Minuten keine Aktion getätigt hast, wirst du aus Sicherheitsgründen abgemeldet.",
|
||||||
"logoutIn": "Abmelden in ",
|
"logoutIn": "Abmelden in ",
|
||||||
|
|||||||
@ -173,7 +173,7 @@
|
|||||||
"GDD": "GDD",
|
"GDD": "GDD",
|
||||||
"gddKonto": "GDD Konto",
|
"gddKonto": "GDD Konto",
|
||||||
"gdd_per_link": {
|
"gdd_per_link": {
|
||||||
"choose-amount": "Select an amount that you would like to send via link. You can also enter a message. Click 'Generate now' to create a link that you can share.",
|
"choose-amount": "Select an amount you want to send via link and enter a message. The message is mandatory.",
|
||||||
"copy-link": "Copy link",
|
"copy-link": "Copy link",
|
||||||
"copy-link-with-text": "Copy link and text",
|
"copy-link-with-text": "Copy link and text",
|
||||||
"created": "Link was created!",
|
"created": "Link was created!",
|
||||||
@ -272,6 +272,7 @@
|
|||||||
"send_gdd": "Send GDD",
|
"send_gdd": "Send GDD",
|
||||||
"send_per_link": "Send GDD via Link",
|
"send_per_link": "Send GDD via Link",
|
||||||
"session": {
|
"session": {
|
||||||
|
"automaticallyLoggedOut": "You have been automatically logged out.",
|
||||||
"extend": "Stay logged in",
|
"extend": "Stay logged in",
|
||||||
"lightText": "If you have not performed any action for more than 10 minutes, you will be logged out for security reasons.",
|
"lightText": "If you have not performed any action for more than 10 minutes, you will be logged out for security reasons.",
|
||||||
"logoutIn": "Log out in ",
|
"logoutIn": "Log out in ",
|
||||||
|
|||||||
@ -18,11 +18,18 @@ export const toasters = {
|
|||||||
variant: 'warning',
|
variant: 'warning',
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
toastInfoNoHide(message) {
|
||||||
|
this.toast(message, {
|
||||||
|
title: this.$t('navigation.info'),
|
||||||
|
variant: 'warning',
|
||||||
|
noAutoHide: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
toast(message, options) {
|
toast(message, options) {
|
||||||
if (message.replace) message = message.replace(/^GraphQL error: /, '')
|
if (message.replace) message = message.replace(/^GraphQL error: /, '')
|
||||||
this.$root.$bvToast.toast(message, {
|
this.$root.$bvToast.toast(message, {
|
||||||
autoHideDelay: 5000,
|
|
||||||
appendToast: true,
|
appendToast: true,
|
||||||
|
autoHideDelay: 5000,
|
||||||
solid: true,
|
solid: true,
|
||||||
toaster: 'b-toaster-top-right',
|
toaster: 'b-toaster-top-right',
|
||||||
headerClass: 'gdd-toaster-title',
|
headerClass: 'gdd-toaster-title',
|
||||||
|
|||||||
@ -70,6 +70,8 @@ describe('Community', () => {
|
|||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
state: 'IN_PROGRESS',
|
state: 'IN_PROGRESS',
|
||||||
messagesCount: 0,
|
messagesCount: 0,
|
||||||
|
deniedAt: null,
|
||||||
|
deniedBy: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1550,
|
id: 1550,
|
||||||
@ -84,6 +86,8 @@ describe('Community', () => {
|
|||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
state: 'CONFIRMED',
|
state: 'CONFIRMED',
|
||||||
messagesCount: 0,
|
messagesCount: 0,
|
||||||
|
deniedAt: null,
|
||||||
|
deniedBy: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
contributionCount: 1,
|
contributionCount: 1,
|
||||||
@ -112,6 +116,10 @@ describe('Community', () => {
|
|||||||
confirmedAt: null,
|
confirmedAt: null,
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
|
deniedAt: null,
|
||||||
|
deniedBy: null,
|
||||||
|
messagesCount: 0,
|
||||||
|
state: 'IN_PROGRESS',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1550,
|
id: 1550,
|
||||||
@ -124,7 +132,10 @@ describe('Community', () => {
|
|||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
contributionDate: '2022-06-15T08:47:06.000Z',
|
contributionDate: '2022-06-15T08:47:06.000Z',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
|
deniedAt: null,
|
||||||
|
deniedBy: null,
|
||||||
messagesCount: 0,
|
messagesCount: 0,
|
||||||
|
state: 'IN_PROGRESS',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1556,
|
id: 1556,
|
||||||
@ -137,6 +148,10 @@ describe('Community', () => {
|
|||||||
confirmedAt: null,
|
confirmedAt: null,
|
||||||
firstName: 'Bob',
|
firstName: 'Bob',
|
||||||
lastName: 'der Baumeister',
|
lastName: 'der Baumeister',
|
||||||
|
deniedAt: null,
|
||||||
|
deniedBy: null,
|
||||||
|
messagesCount: 0,
|
||||||
|
state: 'IN_PROGRESS',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
contributionCount: 3,
|
contributionCount: 3,
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
{{ CONFIG.COMMUNITY_DESCRIPTION }}
|
{{ CONFIG.COMMUNITY_DESCRIPTION }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<router-link :to="CONFIG.COMMUNITY_URL">
|
<b-link :href="CONFIG.COMMUNITY_URL">
|
||||||
{{ CONFIG.COMMUNITY_URL }}
|
{{ CONFIG.COMMUNITY_URL }}
|
||||||
</router-link>
|
</b-link>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="h3">{{ $t('community.openContributionLinks') }}</div>
|
<div class="h3">{{ $t('community.openContributionLinks') }}</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido",
|
"name": "gradido",
|
||||||
"version": "1.17.1",
|
"version": "1.18.2",
|
||||||
"description": "Gradido",
|
"description": "Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "git@github.com:gradido/gradido.git",
|
"repository": "git@github.com:gradido/gradido.git",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user