mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge remote-tracking branch 'origin/master' into
1798-feature-gradidoid-1-adapt-and-migrate-database-schema
This commit is contained in:
commit
94f15f7475
41
CHANGELOG.md
41
CHANGELOG.md
@ -4,8 +4,49 @@ 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.12.0](https://github.com/gradido/gradido/compare/1.11.0...1.12.0)
|
||||||
|
|
||||||
|
- if message empty else disabled button [`#2189`](https://github.com/gradido/gradido/pull/2189)
|
||||||
|
- messages show if Confirmed [`#2185`](https://github.com/gradido/gradido/pull/2185)
|
||||||
|
- text in messages smaller [`#2186`](https://github.com/gradido/gradido/pull/2186)
|
||||||
|
- feat: 🍰 Klicktipp retrieve not registered email [`#2181`](https://github.com/gradido/gradido/pull/2181)
|
||||||
|
- fix: 🍰 isModerator on messages to switch the messages side in the messages overview [`#2182`](https://github.com/gradido/gradido/pull/2182)
|
||||||
|
- Refactor locales for Nederlands [`#2174`](https://github.com/gradido/gradido/pull/2174)
|
||||||
|
- Add is moderator to contribution message [`#2180`](https://github.com/gradido/gradido/pull/2180)
|
||||||
|
- feat: 🍰 Moderator Cannot Answer Himself [`#2178`](https://github.com/gradido/gradido/pull/2178)
|
||||||
|
- refactor: Improve Statistics Query [`#2170`](https://github.com/gradido/gradido/pull/2170)
|
||||||
|
- fix: Remove Statistics from Wallet [`#2171`](https://github.com/gradido/gradido/pull/2171)
|
||||||
|
- feat: 🍰 Contribution Messages In Frontend [`#2164`](https://github.com/gradido/gradido/pull/2164)
|
||||||
|
- feat: 🚀 CRUD For Contribution Messages [`#2149`](https://github.com/gradido/gradido/pull/2149)
|
||||||
|
- fix: 🍰 Decay Calculation In Community Statistics [`#2167`](https://github.com/gradido/gradido/pull/2167)
|
||||||
|
- chore: 🍰 Remove Fetch Policy Network Only From Statistics [`#2159`](https://github.com/gradido/gradido/pull/2159)
|
||||||
|
- feat: 🍰 Remove Some Statistics Data From Frontend [`#2153`](https://github.com/gradido/gradido/pull/2153)
|
||||||
|
- feat: 🍰 Add Toogle Collaps On Language Name [`#2156`](https://github.com/gradido/gradido/pull/2156)
|
||||||
|
- 2145 corrections style for frontend [`#2147`](https://github.com/gradido/gradido/pull/2147)
|
||||||
|
- 2072 feature usecase contribution messaging [`#2073`](https://github.com/gradido/gradido/pull/2073)
|
||||||
|
- 2151 add hint to redeem link [`#2158`](https://github.com/gradido/gradido/pull/2158)
|
||||||
|
- 🍰 Create `contribution messages` table [`#2137`](https://github.com/gradido/gradido/pull/2137)
|
||||||
|
- feat: 🍰 Add The Languages French And Dutch [`#2138`](https://github.com/gradido/gradido/pull/2138)
|
||||||
|
- 1973 list open contribution links in the wallet [`#1975`](https://github.com/gradido/gradido/pull/1975)
|
||||||
|
- feat: 🍰 Admin Interface Displays Statistics [`#2124`](https://github.com/gradido/gradido/pull/2124)
|
||||||
|
- feat: Statistics Resolver [`#2041`](https://github.com/gradido/gradido/pull/2041)
|
||||||
|
- 2116 retrieve admin and moderators [`#2127`](https://github.com/gradido/gradido/pull/2127)
|
||||||
|
- 2125 feature gradido id: new column gradidoid in users table [`#2126`](https://github.com/gradido/gradido/pull/2126)
|
||||||
|
- 2119 new menu item gdt [`#2120`](https://github.com/gradido/gradido/pull/2120)
|
||||||
|
- feat: Migrate Contributions Table [`#2136`](https://github.com/gradido/gradido/pull/2136)
|
||||||
|
- chore: 🍰 Refactor Contribution Form Logic And Write Tests [`#2092`](https://github.com/gradido/gradido/pull/2092)
|
||||||
|
- fix: 🍰 Add `emailChecked` Before Changing `optIn` State & Log Error On klicktipp Middleware [`#2107`](https://github.com/gradido/gradido/pull/2107)
|
||||||
|
- Add RIGHTS.LIST_CONTRIBUTION_LINKS to ROLE_USER [`#2123`](https://github.com/gradido/gradido/pull/2123)
|
||||||
|
- 2121 translate locales to spanish [`#2122`](https://github.com/gradido/gradido/pull/2122)
|
||||||
|
- add formatter on input amount replace point and comma [`#2115`](https://github.com/gradido/gradido/pull/2115)
|
||||||
|
- remove required from form.memo [`#2114`](https://github.com/gradido/gradido/pull/2114)
|
||||||
|
- Fix pagination ellipsis [`#2104`](https://github.com/gradido/gradido/pull/2104)
|
||||||
|
|
||||||
#### [1.11.0](https://github.com/gradido/gradido/compare/1.10.1...1.11.0)
|
#### [1.11.0](https://github.com/gradido/gradido/compare/1.10.1...1.11.0)
|
||||||
|
|
||||||
|
> 28 July 2022
|
||||||
|
|
||||||
|
- release: Version 1.11.0 [`#2103`](https://github.com/gradido/gradido/pull/2103)
|
||||||
- Fix navbar community item [`#2102`](https://github.com/gradido/gradido/pull/2102)
|
- Fix navbar community item [`#2102`](https://github.com/gradido/gradido/pull/2102)
|
||||||
- Add validation date info to copied text after transaction link creation [`#2101`](https://github.com/gradido/gradido/pull/2101)
|
- Add validation date info to copied text after transaction link creation [`#2101`](https://github.com/gradido/gradido/pull/2101)
|
||||||
- Remove member area from menu (desktop and mobile), when user has no elopage account [`#2099`](https://github.com/gradido/gradido/pull/2099)
|
- Remove member area from menu (desktop and mobile), when user has no elopage account [`#2099`](https://github.com/gradido/gradido/pull/2099)
|
||||||
|
|||||||
@ -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.11.0",
|
"version": "1.12.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -0,0 +1,111 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesFormular from './ContributionMessagesFormular.vue'
|
||||||
|
import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const apolloMutateMock = jest.fn().mockResolvedValue()
|
||||||
|
|
||||||
|
describe('ContributionMessagesFormular', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$apollo: {
|
||||||
|
mutate: apolloMutateMock,
|
||||||
|
},
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesFormular, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-formular', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-formular').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on trigger reset', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.find('form').trigger('reset')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('form has empty text', () => {
|
||||||
|
expect(wrapper.vm.form).toEqual({
|
||||||
|
text: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on trigger submit', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emitted "get-list-contribution-messages" with data', async () => {
|
||||||
|
expect(wrapper.emitted('get-list-contribution-messages')).toEqual(
|
||||||
|
expect.arrayContaining([expect.arrayContaining([42])]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emitted "update-state" with data', async () => {
|
||||||
|
expect(wrapper.emitted('update-state')).toEqual(
|
||||||
|
expect.arrayContaining([expect.arrayContaining([42])]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send contribution message with error', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })
|
||||||
|
wrapper = Wrapper()
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an error message', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send contribution message with success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
wrapper = Wrapper()
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an success message', () => {
|
||||||
|
expect(toastSuccessSpy).toBeCalledWith('message.request')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-formular">
|
||||||
|
<div class="mt-5">
|
||||||
|
<b-form @submit.prevent="onSubmit" @reset.prevent="onReset">
|
||||||
|
<b-form-textarea
|
||||||
|
id="textarea"
|
||||||
|
v-model="form.text"
|
||||||
|
:placeholder="$t('contributionLink.memo')"
|
||||||
|
rows="3"
|
||||||
|
max-rows="6"
|
||||||
|
></b-form-textarea>
|
||||||
|
<b-row class="mt-4 mb-6">
|
||||||
|
<b-col>
|
||||||
|
<b-button type="reset" variant="danger">{{ $t('form.cancel') }}</b-button>
|
||||||
|
</b-col>
|
||||||
|
<b-col class="text-right">
|
||||||
|
<b-button type="submit" variant="primary" :disabled="disabled">
|
||||||
|
{{ $t('form.submit') }}
|
||||||
|
</b-button>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesFormular',
|
||||||
|
props: {
|
||||||
|
contributionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
text: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit(event) {
|
||||||
|
this.$apollo
|
||||||
|
.mutate({
|
||||||
|
mutation: adminCreateContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: this.contributionId,
|
||||||
|
message: this.form.text,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.$emit('get-list-contribution-messages', this.contributionId)
|
||||||
|
this.$emit('update-state', this.contributionId)
|
||||||
|
this.form.text = ''
|
||||||
|
this.toastSuccess(this.$t('message.request'))
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.toastError(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onReset(event) {
|
||||||
|
this.form.text = ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
disabled() {
|
||||||
|
if (this.form.text !== '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesList from './ContributionMessagesList.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const apolloQueryMock = jest.fn().mockResolvedValue()
|
||||||
|
|
||||||
|
describe('ContributionMessagesList', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
$apollo: {
|
||||||
|
query: apolloQueryMock,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesList, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends query to Apollo when created', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
contributionId: propsData.contributionId,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-list', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-list').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a Component ContributionMessagesFormular', () => {
|
||||||
|
expect(wrapper.findComponent({ name: 'ContributionMessagesFormular' }).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-list">
|
||||||
|
<b-container>
|
||||||
|
{{ messages.lenght }}
|
||||||
|
<div v-for="message in messages" v-bind:key="message.id">
|
||||||
|
<contribution-messages-list-item :message="message" />
|
||||||
|
</div>
|
||||||
|
</b-container>
|
||||||
|
|
||||||
|
<contribution-messages-formular
|
||||||
|
:contributionId="contributionId"
|
||||||
|
@get-list-contribution-messages="getListContributionMessages"
|
||||||
|
@update-state="updateState"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import ContributionMessagesListItem from './slots/ContributionMessagesListItem.vue'
|
||||||
|
import ContributionMessagesFormular from '../ContributionMessages/ContributionMessagesFormular.vue'
|
||||||
|
import { listContributionMessages } from '../../graphql/listContributionMessages.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesList',
|
||||||
|
components: {
|
||||||
|
ContributionMessagesListItem,
|
||||||
|
ContributionMessagesFormular,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contributionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
messages: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getListContributionMessages(id) {
|
||||||
|
this.$apollo
|
||||||
|
.query({
|
||||||
|
query: listContributionMessages,
|
||||||
|
variables: {
|
||||||
|
contributionId: id,
|
||||||
|
},
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.messages = result.data.listContributionMessages.messages
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.toastError(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.$emit('update-state', id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getListContributionMessages(this.contributionId)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.temp-message {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesListItem from './ContributionMessagesListItem.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('ContributionMessagesListItem', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
$store: {
|
||||||
|
state: {
|
||||||
|
moderator: {
|
||||||
|
id: 107,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
state: 'PENDING0',
|
||||||
|
message: {
|
||||||
|
id: 111,
|
||||||
|
message: 'asd asda sda sda',
|
||||||
|
createdAt: '2022-08-29T12:23:27.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
userId: 107,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesListItem, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-list-item', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('props.message.default', () => {
|
||||||
|
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-list-item">
|
||||||
|
<is-moderator v-if="message.isModerator" :message="message"></is-moderator>
|
||||||
|
<is-not-moderator v-else :message="message"></is-not-moderator>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import IsModerator from '@/components/ContributionMessages/slots/IsModerator.vue'
|
||||||
|
import IsNotModerator from '@/components/ContributionMessages/slots/IsNotModerator.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesListItem',
|
||||||
|
components: {
|
||||||
|
IsModerator,
|
||||||
|
IsNotModerator,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import IsModerator from './IsModerator.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('IsModerator', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
message: {
|
||||||
|
id: 111,
|
||||||
|
message: 'asd asda sda sda',
|
||||||
|
createdAt: '2022-08-29T12:23:27.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
userId: 107,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(IsModerator, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .slot-is-moderator', () => {
|
||||||
|
expect(wrapper.find('div.slot-is-moderator').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('props.message.default', () => {
|
||||||
|
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="slot-is-moderator">
|
||||||
|
<div class="text-right">
|
||||||
|
<b-avatar square :text="initialLetters" variant="warning"></b-avatar>
|
||||||
|
<span class="ml-2 mr-2">{{ message.userFirstName }} {{ message.userLastName }}</span>
|
||||||
|
<span class="ml-2">{{ $d(new Date(message.createdAt), 'short') }}</span>
|
||||||
|
<small class="ml-4 text-success">{{ $t('moderator') }}</small>
|
||||||
|
<div class="mt-2">{{ message.message }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
initialLetters() {
|
||||||
|
return `${this.message.userFirstName[0]} ${this.message.userLastName[0]}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.slot-is-moderator {
|
||||||
|
clear: both;
|
||||||
|
float: right;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import IsNotModerator from './IsNotModerator.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('IsNotModerator', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
message: {
|
||||||
|
id: 113,
|
||||||
|
message: 'asda sdad ad asdasd ',
|
||||||
|
createdAt: '2022-08-29T12:25:34.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Bibi',
|
||||||
|
userLastName: 'Bloxberg',
|
||||||
|
userId: 108,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(IsNotModerator, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .slot-is-not-moderator', () => {
|
||||||
|
expect(wrapper.find('div.slot-is-not-moderator').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('props.message.default', () => {
|
||||||
|
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="slot-is-not-moderator">
|
||||||
|
<div>
|
||||||
|
<b-avatar :text="initialLetters" variant="info"></b-avatar>
|
||||||
|
<span class="ml-2 mr-2">{{ message.userFirstName }} {{ message.userLastName }}</span>
|
||||||
|
<span class="ml-2">{{ $d(new Date(message.createdAt), 'short') }}</span>
|
||||||
|
<div class="mt-2">{{ message.message }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
initialLetters() {
|
||||||
|
return `${this.message.userFirstName[0]} ${this.message.userLastName[0]}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.slot-is-not-moderator {
|
||||||
|
clear: both;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -12,20 +12,42 @@
|
|||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(editCreation)="row">
|
<template #cell(editCreation)="row">
|
||||||
<b-button
|
<div v-if="$store.state.moderator.id !== row.item.userId">
|
||||||
v-if="row.item.moderator"
|
<b-button
|
||||||
variant="info"
|
v-if="row.item.moderator"
|
||||||
size="md"
|
variant="info"
|
||||||
@click="rowToggleDetails(row, 0)"
|
size="md"
|
||||||
class="mr-2"
|
@click="rowToggleDetails(row, 0)"
|
||||||
>
|
class="mr-2"
|
||||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
>
|
||||||
</b-button>
|
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||||
|
</b-button>
|
||||||
|
<b-button v-else @click="rowToggleDetails(row, 0)">
|
||||||
|
<b-icon icon="chat-dots"></b-icon>
|
||||||
|
<b-icon
|
||||||
|
v-if="row.item.state === 'PENDING' && row.item.messageCount > 0"
|
||||||
|
icon="exclamation-circle-fill"
|
||||||
|
variant="warning"
|
||||||
|
></b-icon>
|
||||||
|
<b-icon
|
||||||
|
v-if="row.item.state === 'IN_PROGRESS' && row.item.messageCount > 0"
|
||||||
|
icon="question-diamond"
|
||||||
|
variant="light"
|
||||||
|
></b-icon>
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(confirm)="row">
|
<template #cell(confirm)="row">
|
||||||
<b-button variant="success" size="md" @click="$emit('show-overlay', row.item)" class="mr-2">
|
<div v-if="$store.state.moderator.id !== row.item.userId">
|
||||||
<b-icon icon="check" scale="2" variant=""></b-icon>
|
<b-button
|
||||||
</b-button>
|
variant="success"
|
||||||
|
size="md"
|
||||||
|
@click="$emit('show-overlay', row.item)"
|
||||||
|
class="mr-2"
|
||||||
|
>
|
||||||
|
<b-icon icon="check" scale="2" variant=""></b-icon>
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #row-details="row">
|
<template #row-details="row">
|
||||||
<row-details
|
<row-details
|
||||||
@ -33,10 +55,10 @@
|
|||||||
type="show-creation"
|
type="show-creation"
|
||||||
slotName="show-creation"
|
slotName="show-creation"
|
||||||
:index="0"
|
:index="0"
|
||||||
@row-toggle-details="rowToggleDetails"
|
@row-toggle-details="rowToggleDetails(row, 0)"
|
||||||
>
|
>
|
||||||
<template #show-creation>
|
<template #show-creation>
|
||||||
<div>
|
<div v-if="row.item.moderator">
|
||||||
<edit-creation-formular
|
<edit-creation-formular
|
||||||
type="singleCreation"
|
type="singleCreation"
|
||||||
:creation="row.item.creation"
|
:creation="row.item.creation"
|
||||||
@ -44,6 +66,12 @@
|
|||||||
:row="row"
|
:row="row"
|
||||||
:creationUserData="creationUserData"
|
:creationUserData="creationUserData"
|
||||||
@update-creation-data="updateCreationData"
|
@update-creation-data="updateCreationData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<contribution-messages-list
|
||||||
|
:contributionId="row.item.id"
|
||||||
|
@update-state="updateState"
|
||||||
@update-user-data="updateUserData"
|
@update-user-data="updateUserData"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -58,6 +86,7 @@
|
|||||||
import { toggleRowDetails } from '../../mixins/toggleRowDetails'
|
import { toggleRowDetails } from '../../mixins/toggleRowDetails'
|
||||||
import RowDetails from '../RowDetails.vue'
|
import RowDetails from '../RowDetails.vue'
|
||||||
import EditCreationFormular from '../EditCreationFormular.vue'
|
import EditCreationFormular from '../EditCreationFormular.vue'
|
||||||
|
import ContributionMessagesList from '../ContributionMessages/ContributionMessagesList.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OpenCreationsTable',
|
name: 'OpenCreationsTable',
|
||||||
@ -65,6 +94,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
EditCreationFormular,
|
EditCreationFormular,
|
||||||
RowDetails,
|
RowDetails,
|
||||||
|
ContributionMessagesList,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
items: {
|
items: {
|
||||||
@ -98,6 +128,9 @@ export default {
|
|||||||
updateUserData(rowItem, newCreation) {
|
updateUserData(rowItem, newCreation) {
|
||||||
rowItem.creation = newCreation
|
rowItem.creation = newCreation
|
||||||
},
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.$emit('update-state', id)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
15
admin/src/graphql/adminCreateContributionMessage.js
Normal file
15
admin/src/graphql/adminCreateContributionMessage.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const adminCreateContributionMessage = gql`
|
||||||
|
mutation ($contributionId: Float!, $message: String!) {
|
||||||
|
adminCreateContributionMessage(contributionId: $contributionId, message: $message) {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
25
admin/src/graphql/listContributionMessages.js
Normal file
25
admin/src/graphql/listContributionMessages.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const listContributionMessages = gql`
|
||||||
|
query ($contributionId: Float!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) {
|
||||||
|
listContributionMessages(
|
||||||
|
contributionId: $contributionId
|
||||||
|
pageSize: $pageSize
|
||||||
|
currentPage: $currentPage
|
||||||
|
order: $order
|
||||||
|
) {
|
||||||
|
count
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
userId
|
||||||
|
isModerator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -6,12 +6,15 @@ export const listUnconfirmedContributions = gql`
|
|||||||
id
|
id
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
|
userId
|
||||||
email
|
email
|
||||||
amount
|
amount
|
||||||
memo
|
memo
|
||||||
date
|
date
|
||||||
moderator
|
moderator
|
||||||
creation
|
creation
|
||||||
|
state
|
||||||
|
messageCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -69,6 +69,10 @@
|
|||||||
},
|
},
|
||||||
"short_hash": "({shortHash})"
|
"short_hash": "({shortHash})"
|
||||||
},
|
},
|
||||||
|
"form": {
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"submit": "Senden"
|
||||||
|
},
|
||||||
"GDD": "GDD",
|
"GDD": "GDD",
|
||||||
"hide_details": "Details verbergen",
|
"hide_details": "Details verbergen",
|
||||||
"lastname": "Nachname",
|
"lastname": "Nachname",
|
||||||
@ -78,6 +82,9 @@
|
|||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
|
"message": {
|
||||||
|
"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.",
|
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
|||||||
@ -69,6 +69,10 @@
|
|||||||
},
|
},
|
||||||
"short_hash": "({shortHash})"
|
"short_hash": "({shortHash})"
|
||||||
},
|
},
|
||||||
|
"form": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"submit": "Send"
|
||||||
|
},
|
||||||
"GDD": "GDD",
|
"GDD": "GDD",
|
||||||
"hide_details": "Hide details",
|
"hide_details": "Hide details",
|
||||||
"lastname": "Lastname",
|
"lastname": "Lastname",
|
||||||
@ -78,6 +82,9 @@
|
|||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
|
"message": {
|
||||||
|
"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.",
|
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
|||||||
@ -14,21 +14,23 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
|||||||
id: 1,
|
id: 1,
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
|
userId: 99,
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
amount: 500,
|
amount: 500,
|
||||||
memo: 'Danke für alles',
|
memo: 'Danke für alles',
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
moderator: 2,
|
moderator: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
firstName: 'Räuber',
|
firstName: 'Räuber',
|
||||||
lastName: 'Hotzenplotz',
|
lastName: 'Hotzenplotz',
|
||||||
|
userId: 100,
|
||||||
email: 'raeuber@hotzenplotz.de',
|
email: 'raeuber@hotzenplotz.de',
|
||||||
amount: 1000000,
|
amount: 1000000,
|
||||||
memo: 'Gut Ergattert',
|
memo: 'Gut Ergattert',
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
moderator: 2,
|
moderator: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -41,6 +43,15 @@ const mocks = {
|
|||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => d),
|
||||||
$store: {
|
$store: {
|
||||||
commit: storeCommitMock,
|
commit: storeCommitMock,
|
||||||
|
state: {
|
||||||
|
moderator: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
isAdmin: '2022-08-30T07:41:31.000Z',
|
||||||
|
id: 263,
|
||||||
|
language: 'de',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
$apollo: {
|
$apollo: {
|
||||||
query: apolloQueryMock,
|
query: apolloQueryMock,
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
:fields="fields"
|
:fields="fields"
|
||||||
@remove-creation="removeCreation"
|
@remove-creation="removeCreation"
|
||||||
@show-overlay="showOverlay"
|
@show-overlay="showOverlay"
|
||||||
|
@update-state="updateState"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -93,6 +94,10 @@ export default {
|
|||||||
this.overlay = true
|
this.overlay = true
|
||||||
this.item = item
|
this.item = item
|
||||||
},
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.pendingCreations.find((obj) => obj.id === id).messagesCount++
|
||||||
|
this.pendingCreations.find((obj) => obj.id === id).state = 'IN_PROGRESS'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fields() {
|
fields() {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-backend",
|
"name": "gradido-backend",
|
||||||
"version": "1.11.0",
|
"version": "1.12.0",
|
||||||
"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",
|
||||||
@ -14,7 +14,8 @@
|
|||||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
|
|||||||
@ -33,6 +33,8 @@ export enum RIGHTS {
|
|||||||
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
||||||
COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS',
|
COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS',
|
||||||
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
||||||
|
CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE',
|
||||||
|
LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES',
|
||||||
// Admin
|
// Admin
|
||||||
SEARCH_USERS = 'SEARCH_USERS',
|
SEARCH_USERS = 'SEARCH_USERS',
|
||||||
SET_USER_ROLE = 'SET_USER_ROLE',
|
SET_USER_ROLE = 'SET_USER_ROLE',
|
||||||
@ -50,4 +52,5 @@ export enum RIGHTS {
|
|||||||
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
||||||
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
||||||
UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK',
|
UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK',
|
||||||
|
ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,8 @@ export const ROLE_USER = new Role('user', [
|
|||||||
RIGHTS.SEARCH_ADMIN_USERS,
|
RIGHTS.SEARCH_ADMIN_USERS,
|
||||||
RIGHTS.LIST_CONTRIBUTION_LINKS,
|
RIGHTS.LIST_CONTRIBUTION_LINKS,
|
||||||
RIGHTS.COMMUNITY_STATISTICS,
|
RIGHTS.COMMUNITY_STATISTICS,
|
||||||
|
RIGHTS.CREATE_CONTRIBUTION_MESSAGE,
|
||||||
|
RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES,
|
||||||
])
|
])
|
||||||
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Decimal.set({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0048-add_user_contacts_table',
|
DB_VERSION: '0049-add_user_contacts_table',
|
||||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||||
LOG4JS_CONFIG: 'log4js-config.json',
|
LOG4JS_CONFIG: 'log4js-config.json',
|
||||||
// default log level on production should be info
|
// default log level on production should be info
|
||||||
|
|||||||
11
backend/src/graphql/arg/ContributionMessageArgs.ts
Normal file
11
backend/src/graphql/arg/ContributionMessageArgs.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { ArgsType, Field, InputType } from 'type-graphql'
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
@ArgsType()
|
||||||
|
export default class ContributionMessageArgs {
|
||||||
|
@Field(() => Number)
|
||||||
|
contributionId: number
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
message: string
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { ObjectType, Field, Int } from 'type-graphql'
|
import { ObjectType, Field, Int } from 'type-graphql'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Contribution as dbContribution } from '@entity/Contribution'
|
import { Contribution as dbContribution } from '@entity/Contribution'
|
||||||
import { User } from './User'
|
import { User } from '@entity/User'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class Contribution {
|
export class Contribution {
|
||||||
@ -16,6 +16,8 @@ export class Contribution {
|
|||||||
this.confirmedAt = contribution.confirmedAt
|
this.confirmedAt = contribution.confirmedAt
|
||||||
this.confirmedBy = contribution.confirmedBy
|
this.confirmedBy = contribution.confirmedBy
|
||||||
this.contributionDate = contribution.contributionDate
|
this.contributionDate = contribution.contributionDate
|
||||||
|
this.state = contribution.contributionStatus
|
||||||
|
this.messagesCount = contribution.messages ? contribution.messages.length : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
@ -47,6 +49,12 @@ export class Contribution {
|
|||||||
|
|
||||||
@Field(() => Date)
|
@Field(() => Date)
|
||||||
contributionDate: Date
|
contributionDate: Date
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
messagesCount: number
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
state: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
|||||||
53
backend/src/graphql/model/ContributionMessage.ts
Normal file
53
backend/src/graphql/model/ContributionMessage.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Field, ObjectType } from 'type-graphql'
|
||||||
|
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
|
||||||
|
import { User } from '@entity/User'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class ContributionMessage {
|
||||||
|
constructor(contributionMessage: DbContributionMessage, user: User) {
|
||||||
|
this.id = contributionMessage.id
|
||||||
|
this.message = contributionMessage.message
|
||||||
|
this.createdAt = contributionMessage.createdAt
|
||||||
|
this.updatedAt = contributionMessage.updatedAt
|
||||||
|
this.type = contributionMessage.type
|
||||||
|
this.userFirstName = user.firstName
|
||||||
|
this.userLastName = user.lastName
|
||||||
|
this.userId = user.id
|
||||||
|
this.isModerator = contributionMessage.isModerator
|
||||||
|
}
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
message: string
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
updatedAt?: Date | null
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
type: string
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
userFirstName: string | null
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
userLastName: string | null
|
||||||
|
|
||||||
|
@Field(() => Number, { nullable: true })
|
||||||
|
userId: number | null
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
isModerator: boolean
|
||||||
|
}
|
||||||
|
@ObjectType()
|
||||||
|
export class ContributionMessageListResult {
|
||||||
|
@Field(() => Number)
|
||||||
|
count: number
|
||||||
|
|
||||||
|
@Field(() => [ContributionMessage])
|
||||||
|
messages: ContributionMessage[]
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@ import { User } from '@entity/User'
|
|||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class UnconfirmedContribution {
|
export class UnconfirmedContribution {
|
||||||
constructor(contribution: Contribution, user: User, creations: Decimal[]) {
|
constructor(contribution: Contribution, user: User | undefined, creations: Decimal[]) {
|
||||||
this.id = contribution.id
|
this.id = contribution.id
|
||||||
this.userId = contribution.userId
|
this.userId = contribution.userId
|
||||||
this.amount = contribution.amount
|
this.amount = contribution.amount
|
||||||
@ -14,7 +14,10 @@ export class UnconfirmedContribution {
|
|||||||
this.firstName = user ? user.firstName : ''
|
this.firstName = user ? user.firstName : ''
|
||||||
this.lastName = user ? user.lastName : ''
|
this.lastName = user ? user.lastName : ''
|
||||||
this.email = user ? user.emailContact.email : ''
|
this.email = user ? user.emailContact.email : ''
|
||||||
|
// this.moderator = contribution.moderatorId
|
||||||
this.creation = creations
|
this.creation = creations
|
||||||
|
this.state = contribution.contributionStatus
|
||||||
|
this.messageCount = contribution.messages ? contribution.messages.length : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
@ -46,4 +49,10 @@ export class UnconfirmedContribution {
|
|||||||
|
|
||||||
@Field(() => [Decimal])
|
@Field(() => [Decimal])
|
||||||
creation: Decimal[]
|
creation: Decimal[]
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
state: string
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
messageCount: number
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,10 @@ import {
|
|||||||
MEMO_MIN_CHARS,
|
MEMO_MIN_CHARS,
|
||||||
} from './const/const'
|
} from './const/const'
|
||||||
import { UserContact } from '@entity/UserContact'
|
import { UserContact } from '@entity/UserContact'
|
||||||
|
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
|
||||||
|
import ContributionMessageArgs from '@arg/ContributionMessageArgs'
|
||||||
|
import { ContributionMessageType } from '@enum/MessageType'
|
||||||
|
import { ContributionMessage } from '@model/ContributionMessage'
|
||||||
|
|
||||||
// const EMAIL_OPT_IN_REGISTER = 1
|
// const EMAIL_OPT_IN_REGISTER = 1
|
||||||
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
|
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
|
||||||
@ -393,7 +397,14 @@ export class AdminResolver {
|
|||||||
@Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS])
|
@Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS])
|
||||||
@Query(() => [UnconfirmedContribution])
|
@Query(() => [UnconfirmedContribution])
|
||||||
async listUnconfirmedContributions(): Promise<UnconfirmedContribution[]> {
|
async listUnconfirmedContributions(): Promise<UnconfirmedContribution[]> {
|
||||||
const contributions = await Contribution.find({ where: { confirmedAt: IsNull() } })
|
const contributions = await getConnection()
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('c')
|
||||||
|
.from(Contribution, 'c')
|
||||||
|
.leftJoinAndSelect('c.messages', 'm')
|
||||||
|
.where({ confirmedAt: IsNull() })
|
||||||
|
.getMany()
|
||||||
|
|
||||||
if (contributions.length === 0) {
|
if (contributions.length === 0) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@ -406,6 +417,12 @@ export class AdminResolver {
|
|||||||
relations: ['emailContact'],
|
relations: ['emailContact'],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return new UnconfirmedContribution(
|
||||||
|
contribution,
|
||||||
|
user,
|
||||||
|
creation ? creation.creations : FULL_CREATION_AVAILABLE,
|
||||||
|
)
|
||||||
|
/*
|
||||||
return contributions.map((contribution) => {
|
return contributions.map((contribution) => {
|
||||||
const user = users.find((u) => u.id === contribution.userId)
|
const user = users.find((u) => u.id === contribution.userId)
|
||||||
const creation = userCreations.find((c) => c.id === contribution.userId)
|
const creation = userCreations.find((c) => c.id === contribution.userId)
|
||||||
@ -423,6 +440,7 @@ export class AdminResolver {
|
|||||||
creation: creation ? creation.creations : FULL_CREATION_AVAILABLE,
|
creation: creation ? creation.creations : FULL_CREATION_AVAILABLE,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
|
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
|
||||||
@ -750,4 +768,50 @@ export class AdminResolver {
|
|||||||
logger.debug(`updateContributionLink successful!`)
|
logger.debug(`updateContributionLink successful!`)
|
||||||
return new ContributionLink(dbContributionLink)
|
return new ContributionLink(dbContributionLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTION_MESSAGE])
|
||||||
|
@Mutation(() => ContributionMessage)
|
||||||
|
async adminCreateContributionMessage(
|
||||||
|
@Args() { contributionId, message }: ContributionMessageArgs,
|
||||||
|
@Ctx() context: Context,
|
||||||
|
): Promise<ContributionMessage> {
|
||||||
|
const user = getUser(context)
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
await queryRunner.connect()
|
||||||
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
|
const contributionMessage = DbContributionMessage.create()
|
||||||
|
try {
|
||||||
|
const contribution = await Contribution.findOne({ id: contributionId })
|
||||||
|
if (!contribution) {
|
||||||
|
throw new Error('Contribution not found')
|
||||||
|
}
|
||||||
|
if (contribution.userId === user.id) {
|
||||||
|
throw new Error('Admin can not answer on own contribution')
|
||||||
|
}
|
||||||
|
contributionMessage.contributionId = contributionId
|
||||||
|
contributionMessage.createdAt = new Date()
|
||||||
|
contributionMessage.message = message
|
||||||
|
contributionMessage.userId = user.id
|
||||||
|
contributionMessage.type = ContributionMessageType.DIALOG
|
||||||
|
contributionMessage.isModerator = true
|
||||||
|
await queryRunner.manager.insert(DbContributionMessage, contributionMessage)
|
||||||
|
|
||||||
|
if (
|
||||||
|
contribution.contributionStatus === ContributionStatus.DELETED ||
|
||||||
|
contribution.contributionStatus === ContributionStatus.DENIED ||
|
||||||
|
contribution.contributionStatus === ContributionStatus.PENDING
|
||||||
|
) {
|
||||||
|
contribution.contributionStatus = ContributionStatus.IN_PROGRESS
|
||||||
|
await queryRunner.manager.update(Contribution, { id: contributionId }, contribution)
|
||||||
|
}
|
||||||
|
await queryRunner.commitTransaction()
|
||||||
|
} catch (e) {
|
||||||
|
await queryRunner.rollbackTransaction()
|
||||||
|
logger.error(`ContributionMessage was not successful: ${e}`)
|
||||||
|
throw new Error(`ContributionMessage was not successful: ${e}`)
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
return new ContributionMessage(contributionMessage, user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
329
backend/src/graphql/resolver/ContributionMessageResolver.test.ts
Normal file
329
backend/src/graphql/resolver/ContributionMessageResolver.test.ts
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
|
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||||
|
import { GraphQLError } from 'graphql'
|
||||||
|
import {
|
||||||
|
adminCreateContributionMessage,
|
||||||
|
createContribution,
|
||||||
|
createContributionMessage,
|
||||||
|
} from '@/seeds/graphql/mutations'
|
||||||
|
import { listContributionMessages, login } from '@/seeds/graphql/queries'
|
||||||
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
|
|
||||||
|
let mutate: any, query: any, con: any
|
||||||
|
let testEnv: any
|
||||||
|
let result: any
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
testEnv = await testEnvironment()
|
||||||
|
mutate = testEnv.mutate
|
||||||
|
query = testEnv.query
|
||||||
|
con = testEnv.con
|
||||||
|
await cleanDB()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('ContributionMessageResolver', () => {
|
||||||
|
describe('adminCreateContributionMessage', () => {
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: adminCreateContributionMessage,
|
||||||
|
variables: { contributionId: 1, message: 'This is a test message' },
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await userFactory(testEnv, bibiBloxberg)
|
||||||
|
await userFactory(testEnv, peterLustig)
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
result = await mutate({
|
||||||
|
mutation: createContribution,
|
||||||
|
variables: {
|
||||||
|
amount: 100.0,
|
||||||
|
memo: 'Test env contribution',
|
||||||
|
creationDate: new Date().toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('input not valid', () => {
|
||||||
|
it('throws error when contribution does not exist', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: adminCreateContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: -1,
|
||||||
|
message: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not successful: Error: Contribution not found',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when contribution.userId equals user.id', async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
const result2 = await mutate({
|
||||||
|
mutation: createContribution,
|
||||||
|
variables: {
|
||||||
|
amount: 100.0,
|
||||||
|
memo: 'Test env contribution',
|
||||||
|
creationDate: new Date().toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: adminCreateContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: result2.data.createContribution.id,
|
||||||
|
message: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not successful: Error: Admin can not answer on own contribution',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('valid input', () => {
|
||||||
|
it('creates ContributionMessage', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: adminCreateContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: result.data.createContribution.id,
|
||||||
|
message: 'Admin Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
adminCreateContributionMessage: expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
message: 'Admin Test',
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createContributionMessage', () => {
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createContributionMessage,
|
||||||
|
variables: { contributionId: 1, message: 'This is a test message' },
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('input not valid', () => {
|
||||||
|
it('throws error when contribution does not exist', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: -1,
|
||||||
|
message: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not successful: Error: Contribution not found',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when other user tries to send createContributionMessage', async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: result.data.createContribution.id,
|
||||||
|
message: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [
|
||||||
|
new GraphQLError(
|
||||||
|
'ContributionMessage was not successful: Error: Can not send message to contribution of another user',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('valid input', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates ContributionMessage', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: result.data.createContribution.id,
|
||||||
|
message: 'User Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
createContributionMessage: expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
message: 'User Test',
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Bibi',
|
||||||
|
userLastName: 'Bloxberg',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('listContributionMessages', () => {
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: listContributionMessages,
|
||||||
|
variables: { contributionId: 1 },
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await query({
|
||||||
|
query: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns a list of contributionmessages', async () => {
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: listContributionMessages,
|
||||||
|
variables: { contributionId: result.data.createContribution.id },
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listContributionMessages: {
|
||||||
|
count: 2,
|
||||||
|
messages: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
message: 'Admin Test',
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(Number),
|
||||||
|
message: 'User Test',
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Bibi',
|
||||||
|
userLastName: 'Bloxberg',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
85
backend/src/graphql/resolver/ContributionMessageResolver.ts
Normal file
85
backend/src/graphql/resolver/ContributionMessageResolver.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
|
import { Context, getUser } from '@/server/context'
|
||||||
|
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
|
||||||
|
import { Arg, Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
|
||||||
|
import ContributionMessageArgs from '@arg/ContributionMessageArgs'
|
||||||
|
import { Contribution } from '@entity/Contribution'
|
||||||
|
import { ContributionMessageType } from '@enum/MessageType'
|
||||||
|
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||||
|
import { getConnection } from '@dbTools/typeorm'
|
||||||
|
import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage'
|
||||||
|
import Paginated from '@arg/Paginated'
|
||||||
|
import { Order } from '@enum/Order'
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
export class ContributionMessageResolver {
|
||||||
|
@Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE])
|
||||||
|
@Mutation(() => ContributionMessage)
|
||||||
|
async createContributionMessage(
|
||||||
|
@Args() { contributionId, message }: ContributionMessageArgs,
|
||||||
|
@Ctx() context: Context,
|
||||||
|
): Promise<ContributionMessage> {
|
||||||
|
const user = getUser(context)
|
||||||
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
|
await queryRunner.connect()
|
||||||
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
|
const contributionMessage = DbContributionMessage.create()
|
||||||
|
try {
|
||||||
|
const contribution = await Contribution.findOne({ id: contributionId })
|
||||||
|
if (!contribution) {
|
||||||
|
throw new Error('Contribution not found')
|
||||||
|
}
|
||||||
|
if (contribution.userId !== user.id) {
|
||||||
|
throw new Error('Can not send message to contribution of another user')
|
||||||
|
}
|
||||||
|
|
||||||
|
contributionMessage.contributionId = contributionId
|
||||||
|
contributionMessage.createdAt = new Date()
|
||||||
|
contributionMessage.message = message
|
||||||
|
contributionMessage.userId = user.id
|
||||||
|
contributionMessage.type = ContributionMessageType.DIALOG
|
||||||
|
contributionMessage.isModerator = false
|
||||||
|
await queryRunner.manager.insert(DbContributionMessage, contributionMessage)
|
||||||
|
|
||||||
|
if (contribution.contributionStatus === ContributionStatus.IN_PROGRESS) {
|
||||||
|
contribution.contributionStatus = ContributionStatus.PENDING
|
||||||
|
await queryRunner.manager.update(Contribution, { id: contributionId }, contribution)
|
||||||
|
}
|
||||||
|
await queryRunner.commitTransaction()
|
||||||
|
} catch (e) {
|
||||||
|
await queryRunner.rollbackTransaction()
|
||||||
|
logger.error(`ContributionMessage was not successful: ${e}`)
|
||||||
|
throw new Error(`ContributionMessage was not successful: ${e}`)
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release()
|
||||||
|
}
|
||||||
|
return new ContributionMessage(contributionMessage, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES])
|
||||||
|
@Query(() => ContributionMessageListResult)
|
||||||
|
async listContributionMessages(
|
||||||
|
@Arg('contributionId') contributionId: number,
|
||||||
|
@Args()
|
||||||
|
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
||||||
|
): Promise<ContributionMessageListResult> {
|
||||||
|
const [contributionMessages, count] = await getConnection()
|
||||||
|
.createQueryBuilder()
|
||||||
|
.select('cm')
|
||||||
|
.from(DbContributionMessage, 'cm')
|
||||||
|
.leftJoinAndSelect('cm.user', 'u')
|
||||||
|
.where({ contributionId: contributionId })
|
||||||
|
.orderBy('cm.createdAt', order)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset((currentPage - 1) * pageSize)
|
||||||
|
.getManyAndCount()
|
||||||
|
|
||||||
|
return {
|
||||||
|
count,
|
||||||
|
messages: contributionMessages.map(
|
||||||
|
(message) => new ContributionMessage(message, message.user),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,6 @@ import { ContributionType } from '@enum/ContributionType'
|
|||||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||||
import { Contribution, ContributionListResult } from '@model/Contribution'
|
import { Contribution, ContributionListResult } from '@model/Contribution'
|
||||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||||
import { User } from '@model/User'
|
|
||||||
import { validateContribution, getUserCreation, updateCreations } from './util/creations'
|
import { validateContribution, getUserCreation, updateCreations } from './util/creations'
|
||||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||||
|
|
||||||
@ -90,19 +89,23 @@ export class ContributionResolver {
|
|||||||
userId: number
|
userId: number
|
||||||
confirmedBy?: FindOperator<number> | null
|
confirmedBy?: FindOperator<number> | null
|
||||||
} = { userId: user.id }
|
} = { userId: user.id }
|
||||||
|
|
||||||
if (filterConfirmed) where.confirmedBy = IsNull()
|
if (filterConfirmed) where.confirmedBy = IsNull()
|
||||||
const [contributions, count] = await dbContribution.findAndCount({
|
|
||||||
where,
|
const [contributions, count] = await getConnection()
|
||||||
order: {
|
.createQueryBuilder()
|
||||||
createdAt: order,
|
.select('c')
|
||||||
},
|
.from(dbContribution, 'c')
|
||||||
withDeleted: true,
|
.leftJoinAndSelect('c.messages', 'm')
|
||||||
skip: (currentPage - 1) * pageSize,
|
.where(where)
|
||||||
take: pageSize,
|
.orderBy('c.createdAt', order)
|
||||||
})
|
.limit(pageSize)
|
||||||
|
.offset((currentPage - 1) * pageSize)
|
||||||
|
.getManyAndCount()
|
||||||
|
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
contributions.map((contribution) => new Contribution(contribution, new User(user))),
|
contributions.map((contribution) => new Contribution(contribution, user)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +126,7 @@ export class ContributionResolver {
|
|||||||
.getManyAndCount()
|
.getManyAndCount()
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
dbContributions.map(
|
dbContributions.map((contribution) => new Contribution(contribution, contribution.user)),
|
||||||
(contribution) => new Contribution(contribution, new User(contribution.user)),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,49 +7,48 @@ import { getConnection } from '@dbTools/typeorm'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class StatisticsResolver {
|
export class StatisticsResolver {
|
||||||
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
||||||
@Query(() => CommunityStatistics)
|
@Query(() => CommunityStatistics)
|
||||||
async communityStatistics(): Promise<CommunityStatistics> {
|
async communityStatistics(): Promise<CommunityStatistics> {
|
||||||
const allUsers = await DbUser.find({ withDeleted: true })
|
const allUsers = await DbUser.count({ withDeleted: true })
|
||||||
|
const totalUsers = await DbUser.count()
|
||||||
let totalUsers = 0
|
const deletedUsers = allUsers - totalUsers
|
||||||
let activeUsers = 0
|
|
||||||
let deletedUsers = 0
|
|
||||||
|
|
||||||
let totalGradidoAvailable: Decimal = new Decimal(0)
|
let totalGradidoAvailable: Decimal = new Decimal(0)
|
||||||
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
||||||
|
|
||||||
const receivedCallDate = new Date()
|
const receivedCallDate = new Date()
|
||||||
|
|
||||||
for (let i = 0; i < allUsers.length; i++) {
|
|
||||||
if (allUsers[i].deletedAt) {
|
|
||||||
deletedUsers++
|
|
||||||
} else {
|
|
||||||
totalUsers++
|
|
||||||
const lastTransaction = await DbTransaction.findOne({
|
|
||||||
where: { userId: allUsers[i].id },
|
|
||||||
order: { balanceDate: 'DESC' },
|
|
||||||
})
|
|
||||||
if (lastTransaction) {
|
|
||||||
activeUsers++
|
|
||||||
const decay = calculateDecay(
|
|
||||||
lastTransaction.balance,
|
|
||||||
lastTransaction.balanceDate,
|
|
||||||
receivedCallDate,
|
|
||||||
)
|
|
||||||
if (decay) {
|
|
||||||
totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString())
|
|
||||||
totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
await queryRunner.connect()
|
await queryRunner.connect()
|
||||||
|
|
||||||
|
const lastUserTransactions = await queryRunner.manager
|
||||||
|
.createQueryBuilder(DbUser, 'user')
|
||||||
|
.select('transaction.balance', 'balance')
|
||||||
|
.addSelect('transaction.balance_date', 'balanceDate')
|
||||||
|
.innerJoin(DbTransaction, 'transaction', 'user.id = transaction.user_id')
|
||||||
|
.where(
|
||||||
|
`transaction.balance_date = (SELECT MAX(t.balance_date) FROM transactions AS t WHERE t.user_id = user.id)`,
|
||||||
|
)
|
||||||
|
.orderBy('transaction.balance_date', 'DESC')
|
||||||
|
.addOrderBy('transaction.id', 'DESC')
|
||||||
|
.getRawMany()
|
||||||
|
|
||||||
|
const activeUsers = lastUserTransactions.length
|
||||||
|
|
||||||
|
lastUserTransactions.forEach(({ balance, balanceDate }) => {
|
||||||
|
const decay = calculateDecay(new Decimal(balance), new Date(balanceDate), receivedCallDate)
|
||||||
|
if (decay) {
|
||||||
|
totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString())
|
||||||
|
totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const { totalGradidoCreated } = await queryRunner.manager
|
const { totalGradidoCreated } = await queryRunner.manager
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
||||||
|
|||||||
@ -261,3 +261,31 @@ export const deleteContribution = gql`
|
|||||||
deleteContribution(id: $id)
|
deleteContribution(id: $id)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const createContributionMessage = gql`
|
||||||
|
mutation ($contributionId: Float!, $message: String!) {
|
||||||
|
createContributionMessage(contributionId: $contributionId, message: $message) {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const adminCreateContributionMessage = gql`
|
||||||
|
mutation ($contributionId: Float!, $message: String!) {
|
||||||
|
adminCreateContributionMessage(contributionId: $contributionId, message: $message) {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -292,3 +292,26 @@ export const searchAdminUsers = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const listContributionMessages = gql`
|
||||||
|
query ($contributionId: Float!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) {
|
||||||
|
listContributionMessages(
|
||||||
|
contributionId: $contributionId
|
||||||
|
pageSize: $pageSize
|
||||||
|
currentPage: $currentPage
|
||||||
|
order: $order
|
||||||
|
) {
|
||||||
|
count
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
28
backend/src/util/klicktipp.ts
Normal file
28
backend/src/util/klicktipp.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import connection from '@/typeorm/connection'
|
||||||
|
import { getKlickTippUser } from '@/apis/KlicktippController'
|
||||||
|
import { User } from '@entity/User'
|
||||||
|
|
||||||
|
export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
||||||
|
const con = await connection()
|
||||||
|
if (!con) {
|
||||||
|
throw new Error('No connection to database')
|
||||||
|
}
|
||||||
|
const users = await User.find()
|
||||||
|
const notRegisteredUser = []
|
||||||
|
for (let i = 0; i < users.length; i++) {
|
||||||
|
const user = users[i]
|
||||||
|
try {
|
||||||
|
await getKlickTippUser(user.email)
|
||||||
|
} catch (err) {
|
||||||
|
notRegisteredUser.push(user.email)
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`${user.email}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await con.close()
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('User die nicht bei KlickTipp vorhanden sind: ', notRegisteredUser)
|
||||||
|
return notRegisteredUser
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieveNotRegisteredEmails()
|
||||||
@ -8,6 +8,7 @@ import {
|
|||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from 'typeorm'
|
} from 'typeorm'
|
||||||
import { Contribution } from '../Contribution'
|
import { Contribution } from '../Contribution'
|
||||||
|
import { User } from '../User'
|
||||||
|
|
||||||
@Entity('contribution_messages', {
|
@Entity('contribution_messages', {
|
||||||
engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
|
engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
|
||||||
@ -26,6 +27,10 @@ export class ContributionMessage extends BaseEntity {
|
|||||||
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
||||||
userId: number
|
userId: number
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.messages)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
user: User
|
||||||
|
|
||||||
@Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
message: string
|
message: string
|
||||||
|
|
||||||
|
|||||||
116
database/entity/0047-messages_tables/User.ts
Normal file
116
database/entity/0047-messages_tables/User.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
BaseEntity,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
DeleteDateColumn,
|
||||||
|
OneToMany,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm'
|
||||||
|
import { Contribution } from '../Contribution'
|
||||||
|
import { ContributionMessage } from '../ContributionMessage'
|
||||||
|
|
||||||
|
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
|
export class User extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'gradido_id',
|
||||||
|
length: 36,
|
||||||
|
nullable: false,
|
||||||
|
unique: true,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
gradidoID: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'alias',
|
||||||
|
length: 20,
|
||||||
|
nullable: true,
|
||||||
|
unique: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
alias: string
|
||||||
|
|
||||||
|
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
pubKey: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||||
|
privKey: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'first_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
firstName: string
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'last_name',
|
||||||
|
length: 255,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
lastName: string
|
||||||
|
|
||||||
|
@DeleteDateColumn()
|
||||||
|
deletedAt: Date | null
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||||
|
password: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||||
|
emailHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||||
|
emailChecked: boolean
|
||||||
|
|
||||||
|
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||||
|
language: string
|
||||||
|
|
||||||
|
@Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null })
|
||||||
|
isAdmin: Date | null
|
||||||
|
|
||||||
|
@Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null })
|
||||||
|
referrerId?: number | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'contribution_link_id',
|
||||||
|
type: 'int',
|
||||||
|
unsigned: true,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
contributionLinkId?: number | null
|
||||||
|
|
||||||
|
@Column({ name: 'publisher_id', default: 0 })
|
||||||
|
publisherId: number
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
name: 'passphrase',
|
||||||
|
collation: 'utf8mb4_unicode_ci',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
passphrase: string
|
||||||
|
|
||||||
|
@OneToMany(() => Contribution, (contribution) => contribution.user)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
contributions?: Contribution[]
|
||||||
|
|
||||||
|
@OneToMany(() => ContributionMessage, (message) => message.user)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
messages?: ContributionMessage[]
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
BaseEntity,
|
||||||
|
Column,
|
||||||
|
DeleteDateColumn,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm'
|
||||||
|
import { Contribution } from '../Contribution'
|
||||||
|
import { User } from '../User'
|
||||||
|
|
||||||
|
@Entity('contribution_messages', {
|
||||||
|
engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
|
||||||
|
})
|
||||||
|
export class ContributionMessage extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'contribution_id', unsigned: true, nullable: false })
|
||||||
|
contributionId: number
|
||||||
|
|
||||||
|
@ManyToOne(() => Contribution, (contribution) => contribution.messages)
|
||||||
|
@JoinColumn({ name: 'contribution_id' })
|
||||||
|
contribution: Contribution
|
||||||
|
|
||||||
|
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.messages)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
user: User
|
||||||
|
|
||||||
|
@Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
message: string
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' })
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', default: null, nullable: true, name: 'updated_at' })
|
||||||
|
updatedAt: Date
|
||||||
|
|
||||||
|
@DeleteDateColumn({ name: 'deleted_at' })
|
||||||
|
deletedAt: Date | null
|
||||||
|
|
||||||
|
@Column({ name: 'deleted_by', default: null, unsigned: true, nullable: true })
|
||||||
|
deletedBy: number
|
||||||
|
|
||||||
|
@Column({ length: 12, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
type: string
|
||||||
|
|
||||||
|
@Column({ name: 'is_moderator', type: 'bool', nullable: false, default: false })
|
||||||
|
isModerator: boolean
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
OneToOne,
|
OneToOne,
|
||||||
} from 'typeorm'
|
} from 'typeorm'
|
||||||
import { Contribution } from '../Contribution'
|
import { Contribution } from '../Contribution'
|
||||||
|
import { ContributionMessage } from '../ContributionMessage'
|
||||||
import { UserContact } from '../UserContact'
|
import { UserContact } from '../UserContact'
|
||||||
|
|
||||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||||
@ -43,7 +44,7 @@ export class User extends BaseEntity {
|
|||||||
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
email: string
|
email: string
|
||||||
*/
|
*/
|
||||||
@OneToOne(() => UserContact, (emailContact) => emailContact.user)
|
@OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user)
|
||||||
@JoinColumn({ name: 'email_id' })
|
@JoinColumn({ name: 'email_id' })
|
||||||
emailContact: UserContact
|
emailContact: UserContact
|
||||||
|
|
||||||
@ -115,7 +116,11 @@ export class User extends BaseEntity {
|
|||||||
@JoinColumn({ name: 'user_id' })
|
@JoinColumn({ name: 'user_id' })
|
||||||
contributions?: Contribution[]
|
contributions?: Contribution[]
|
||||||
|
|
||||||
@OneToMany(() => UserContact, (usercontact) => usercontact.user)
|
@OneToMany(() => ContributionMessage, (message) => message.user)
|
||||||
|
@JoinColumn({ name: 'user_id' })
|
||||||
|
messages?: ContributionMessage[]
|
||||||
|
|
||||||
|
@OneToMany(() => UserContact, (usercontact: UserContact) => usercontact.user)
|
||||||
@JoinColumn({ name: 'user_id' })
|
@JoinColumn({ name: 'user_id' })
|
||||||
usercontacts?: UserContact[]
|
usercontacts?: UserContact[]
|
||||||
}
|
}
|
||||||
@ -1 +1 @@
|
|||||||
export { ContributionMessage } from './0047-messages_tables/ContributionMessage'
|
export { ContributionMessage } from './0048-add_is_moderator_to_contribution_messages/ContributionMessage'
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export { User } from './0048-add_user_contacts_table/User'
|
export { User } from './0049-add_user_contacts_table/User'
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export { UserContact } from './0048-add_user_contacts_table/UserContact'
|
export { UserContact } from './0049-add_user_contacts_table/UserContact'
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(
|
||||||
|
`ALTER TABLE \`contribution_messages\` ADD COLUMN \`is_moderator\` boolean NOT NULL DEFAULT false;`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`ALTER TABLE \`contribution_messages\` DROP COLUMN \`is_moderator\`;`)
|
||||||
|
}
|
||||||
@ -61,7 +61,30 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
|
|||||||
users as u,
|
users as u,
|
||||||
login_email_opt_in as e
|
login_email_opt_in as e
|
||||||
WHERE
|
WHERE
|
||||||
u.id = e.user_id;`)
|
u.id = e.user_id AND
|
||||||
|
e.id in (
|
||||||
|
WITH opt_in AS (
|
||||||
|
SELECT
|
||||||
|
le.id, le.user_id, le.created, le.updated, ROW_NUMBER() OVER (PARTITION BY le.user_id ORDER BY le.created DESC) AS row_num
|
||||||
|
FROM
|
||||||
|
login_email_opt_in as le
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
opt_in.id
|
||||||
|
FROM
|
||||||
|
opt_in
|
||||||
|
WHERE
|
||||||
|
row_num = 1);`)
|
||||||
|
/*
|
||||||
|
// SELECT
|
||||||
|
// le.id
|
||||||
|
// FROM
|
||||||
|
// login_email_opt_in as le
|
||||||
|
// WHERE
|
||||||
|
// le.user_id = u.id
|
||||||
|
// ORDER BY
|
||||||
|
// le.updated DESC, le.created DESC LIMIT 1);`)
|
||||||
|
*/
|
||||||
|
|
||||||
// insert in users table the email_id of the new created email-contacts
|
// insert in users table the email_id of the new created email-contacts
|
||||||
const contacts = await queryFn(`SELECT c.id, c.user_id FROM user_contacts as c`)
|
const contacts = await queryFn(`SELECT c.id, c.user_id FROM user_contacts as c`)
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-database",
|
"name": "gradido-database",
|
||||||
"version": "1.11.0",
|
"version": "1.12.0",
|
||||||
"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",
|
||||||
|
|||||||
@ -68,7 +68,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
'vue-i18n': {
|
'vue-i18n': {
|
||||||
localeDir: './src/locales/*.json',
|
localeDir: './src/locales/{en,de}.json',
|
||||||
// Specify the version of `vue-i18n` you are using.
|
// Specify the version of `vue-i18n` you are using.
|
||||||
// If not specified, the message will be parsed twice.
|
// If not specified, the message will be parsed twice.
|
||||||
messageSyntaxVersion: '^8.22.4',
|
messageSyntaxVersion: '^8.22.4',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bootstrap-vue-gradido-wallet",
|
"name": "bootstrap-vue-gradido-wallet",
|
||||||
"version": "1.11.0",
|
"version": "1.12.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node run/server.js",
|
"start": "node run/server.js",
|
||||||
|
|||||||
@ -0,0 +1,111 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesFormular from './ContributionMessagesFormular.vue'
|
||||||
|
import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const apolloMutateMock = jest.fn().mockResolvedValue()
|
||||||
|
|
||||||
|
describe('ContributionMessagesFormular', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$apollo: {
|
||||||
|
mutate: apolloMutateMock,
|
||||||
|
},
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesFormular, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-formular', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-formular').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on trigger reset', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.find('form').trigger('reset')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('form has empty text', () => {
|
||||||
|
expect(wrapper.vm.form).toEqual({
|
||||||
|
text: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('on trigger submit', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emitted "get-list-contribution-messages" with data', async () => {
|
||||||
|
expect(wrapper.emitted('get-list-contribution-messages')).toEqual(
|
||||||
|
expect.arrayContaining([expect.arrayContaining([42])]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emitted "update-state" with data', async () => {
|
||||||
|
expect(wrapper.emitted('update-state')).toEqual(
|
||||||
|
expect.arrayContaining([expect.arrayContaining([42])]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send contribution message with error', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })
|
||||||
|
wrapper = Wrapper()
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an error message', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send contribution message with success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper.setData({
|
||||||
|
form: {
|
||||||
|
text: 'text form message',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
wrapper = Wrapper()
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an success message', () => {
|
||||||
|
expect(toastSuccessSpy).toBeCalledWith('message.reply')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-formular">
|
||||||
|
<div>
|
||||||
|
<b-form @submit.prevent="onSubmit" @reset="onReset">
|
||||||
|
<b-form-textarea
|
||||||
|
id="textarea"
|
||||||
|
v-model="form.text"
|
||||||
|
:placeholder="$t('form.memo')"
|
||||||
|
rows="3"
|
||||||
|
max-rows="6"
|
||||||
|
></b-form-textarea>
|
||||||
|
<b-row class="mt-4 mb-6">
|
||||||
|
<b-col>
|
||||||
|
<b-button type="reset" variant="danger">{{ $t('form.cancel') }}</b-button>
|
||||||
|
</b-col>
|
||||||
|
<b-col class="text-right">
|
||||||
|
<b-button type="submit" variant="primary" :disabled="disabled">
|
||||||
|
{{ $t('form.reply') }}
|
||||||
|
</b-button>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { createContributionMessage } from '../../graphql/mutations.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesFormular',
|
||||||
|
props: {
|
||||||
|
contributionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
form: {
|
||||||
|
text: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onSubmit() {
|
||||||
|
this.$apollo
|
||||||
|
.mutate({
|
||||||
|
mutation: createContributionMessage,
|
||||||
|
variables: {
|
||||||
|
contributionId: this.contributionId,
|
||||||
|
message: this.form.text,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.$emit('get-list-contribution-messages', this.contributionId)
|
||||||
|
this.$emit('update-state', this.contributionId)
|
||||||
|
this.form.text = ''
|
||||||
|
this.toastSuccess(this.$t('message.reply'))
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.toastError(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onReset() {
|
||||||
|
this.form.text = ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
disabled() {
|
||||||
|
if (this.form.text !== '') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesList from './ContributionMessagesList.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('ContributionMessagesList', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
state: 'IN_PROGRESS',
|
||||||
|
messages: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$i18n: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesList, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-list', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-list').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a Component ContributionMessagesFormular', () => {
|
||||||
|
expect(wrapper.findComponent({ name: 'ContributionMessagesFormular' }).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('get List Contribution Messages', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.vm.getListContributionMessages()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits getListContributionMessages', async () => {
|
||||||
|
expect(wrapper.vm.$emit('get-list-contribution-messages')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('update State', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.vm.updateState()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits getListContributionMessages', async () => {
|
||||||
|
expect(wrapper.vm.$emit('update-state')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-list">
|
||||||
|
<b-container>
|
||||||
|
<div v-for="message in messages" v-bind:key="message.id">
|
||||||
|
<contribution-messages-list-item :message="message" />
|
||||||
|
</div>
|
||||||
|
</b-container>
|
||||||
|
<contribution-messages-formular
|
||||||
|
v-if="['PENDING', 'IN_PROGRESS'].includes(state)"
|
||||||
|
:contributionId="contributionId"
|
||||||
|
@get-list-contribution-messages="getListContributionMessages"
|
||||||
|
@update-state="updateState"
|
||||||
|
/>
|
||||||
|
<div v-b-toggle="'collapse' + String(contributionId)" class="text-center pointer h2">
|
||||||
|
<b-icon icon="arrow-up-short"></b-icon>
|
||||||
|
{{ $t('form.close') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import ContributionMessagesListItem from '@/components/ContributionMessages/ContributionMessagesListItem.vue'
|
||||||
|
import ContributionMessagesFormular from '@/components/ContributionMessages/ContributionMessagesFormular.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesList',
|
||||||
|
components: {
|
||||||
|
ContributionMessagesListItem,
|
||||||
|
ContributionMessagesFormular,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contributionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getListContributionMessages() {
|
||||||
|
this.$emit('get-list-contribution-messages', this.contributionId)
|
||||||
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.$emit('update-state', id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.temp-message {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ContributionMessagesList from './ContributionMessagesList.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('ContributionMessagesList', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
$store: {
|
||||||
|
state: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
state: 'PENDING0',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
id: 111,
|
||||||
|
message: 'asd asda sda sda',
|
||||||
|
createdAt: '2022-08-29T12:23:27.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
userId: 107,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(ContributionMessagesList, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .contribution-messages-list-item', () => {
|
||||||
|
expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="contribution-messages-list-item">
|
||||||
|
<is-not-moderator v-if="isNotModerator" :message="message"></is-not-moderator>
|
||||||
|
<is-moderator v-else :message="message"></is-moderator>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import IsModerator from '@/components/ContributionMessages/slots/IsModerator.vue'
|
||||||
|
import IsNotModerator from '@/components/ContributionMessages/slots/IsNotModerator.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ContributionMessagesListItem',
|
||||||
|
components: {
|
||||||
|
IsModerator,
|
||||||
|
IsNotModerator,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
storeName: `${this.$store.state.firstName} ${this.$store.state.lastName}`,
|
||||||
|
moderationName: `${this.message.userFirstName} ${this.message.userLastName}`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNotModerator() {
|
||||||
|
return this.storeName === this.moderationName
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import IsModerator from './IsModerator.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('IsModerator', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
message: {
|
||||||
|
id: 111,
|
||||||
|
message: 'asd asda sda sda',
|
||||||
|
createdAt: '2022-08-29T12:23:27.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Peter',
|
||||||
|
userLastName: 'Lustig',
|
||||||
|
userId: 107,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(IsModerator, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .slot-is-moderator', () => {
|
||||||
|
expect(wrapper.find('div.slot-is-moderator').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('props.message.default', () => {
|
||||||
|
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
<template>
|
||||||
|
<div class="slot-is-moderator">
|
||||||
|
<b-avatar square :text="initialLetters" variant="warning"></b-avatar>
|
||||||
|
<span class="ml-2 mr-2">{{ message.userFirstName }} {{ message.userLastName }}</span>
|
||||||
|
<span class="ml-2">{{ $d(new Date(message.createdAt), 'short') }}</span>
|
||||||
|
<small class="ml-4 text-success">{{ $t('community.moderator') }}</small>
|
||||||
|
<div class="mt-2">{{ message.message }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
initialLetters() {
|
||||||
|
return `${this.message.userFirstName[0]} ${this.message.userLastName[0]}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.slot-is-moderator {
|
||||||
|
clear: both;
|
||||||
|
/* background-color: rgb(255, 242, 227); */
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import IsNotModerator from './IsNotModerator.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('IsNotModerator', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
message: {
|
||||||
|
id: 113,
|
||||||
|
message: 'asda sdad ad asdasd ',
|
||||||
|
createdAt: '2022-08-29T12:25:34.000Z',
|
||||||
|
updatedAt: null,
|
||||||
|
type: 'DIALOG',
|
||||||
|
userFirstName: 'Bibi',
|
||||||
|
userLastName: 'Bloxberg',
|
||||||
|
userId: 108,
|
||||||
|
__typename: 'ContributionMessage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(IsNotModerator, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV .slot-is-not-moderator', () => {
|
||||||
|
expect(wrapper.find('div.slot-is-not-moderator').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('props.message.default', () => {
|
||||||
|
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div class="slot-is-not-moderator">
|
||||||
|
<div class="text-right">
|
||||||
|
<b-avatar :text="initialLetters" variant="info"></b-avatar>
|
||||||
|
<span class="ml-2 mr-2">{{ message.userFirstName }} {{ message.userLastName }}</span>
|
||||||
|
<span class="ml-2">{{ $d(new Date(message.createdAt), 'short') }}</span>
|
||||||
|
<div class="mt-2">{{ message.message }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
initialLetters() {
|
||||||
|
return `${this.message.userFirstName[0]} ${this.message.userLastName[0]}`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.slot-is-not-moderator {
|
||||||
|
float: right;
|
||||||
|
width: 75%;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -3,8 +3,10 @@
|
|||||||
<div class="list-group" v-for="item in items" :key="item.id">
|
<div class="list-group" v-for="item in items" :key="item.id">
|
||||||
<contribution-list-item
|
<contribution-list-item
|
||||||
v-bind="item"
|
v-bind="item"
|
||||||
|
:contributionId="item.id"
|
||||||
@update-contribution-form="updateContributionForm"
|
@update-contribution-form="updateContributionForm"
|
||||||
@delete-contribution="deleteContribution"
|
@delete-contribution="deleteContribution"
|
||||||
|
@update-state="updateState"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<b-pagination
|
<b-pagination
|
||||||
@ -46,6 +48,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
messages: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -62,6 +65,9 @@ export default {
|
|||||||
deleteContribution(item) {
|
deleteContribution(item) {
|
||||||
this.$emit('delete-contribution', item)
|
this.$emit('delete-contribution', item)
|
||||||
},
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.$emit('update-state', id)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isPaginationVisible() {
|
isPaginationVisible() {
|
||||||
|
|||||||
@ -12,6 +12,9 @@ describe('ContributionListItem', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const propsData = {
|
const propsData = {
|
||||||
|
contributionId: 42,
|
||||||
|
state: 'PENDING',
|
||||||
|
messagesCount: 2,
|
||||||
id: 1,
|
id: 1,
|
||||||
createdAt: '26/07/2022',
|
createdAt: '26/07/2022',
|
||||||
contributionDate: '07/06/2022',
|
contributionDate: '07/06/2022',
|
||||||
@ -37,22 +40,6 @@ describe('ContributionListItem', () => {
|
|||||||
expect(wrapper.find('div.contribution-list-item').exists()).toBe(true)
|
expect(wrapper.find('div.contribution-list-item').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('contribution type', () => {
|
|
||||||
it('is pending by default', () => {
|
|
||||||
expect(wrapper.vm.type).toBe('pending')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('is deleted when deletedAt is present', async () => {
|
|
||||||
await wrapper.setProps({ deletedAt: new Date().toISOString() })
|
|
||||||
expect(wrapper.vm.type).toBe('deleted')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('is confirmed when confirmedAt is present', async () => {
|
|
||||||
await wrapper.setProps({ confirmedAt: new Date().toISOString() })
|
|
||||||
expect(wrapper.vm.type).toBe('confirmed')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('contribution icon', () => {
|
describe('contribution icon', () => {
|
||||||
it('is bell-fill by default', () => {
|
it('is bell-fill by default', () => {
|
||||||
expect(wrapper.vm.icon).toBe('bell-fill')
|
expect(wrapper.vm.icon).toBe('bell-fill')
|
||||||
@ -83,6 +70,11 @@ describe('ContributionListItem', () => {
|
|||||||
await wrapper.setProps({ confirmedAt: new Date().toISOString() })
|
await wrapper.setProps({ confirmedAt: new Date().toISOString() })
|
||||||
expect(wrapper.vm.variant).toBe('success')
|
expect(wrapper.vm.variant).toBe('success')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('is warning at when state is IN_PROGRESS', async () => {
|
||||||
|
await wrapper.setProps({ state: 'IN_PROGRESS' })
|
||||||
|
expect(wrapper.vm.variant).toBe('warning')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('date', () => {
|
describe('date', () => {
|
||||||
@ -133,7 +125,7 @@ describe('ContributionListItem', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
|
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
|
||||||
spy.mockImplementation(() => Promise.resolve(false))
|
spy.mockImplementation(() => Promise.resolve(false))
|
||||||
await wrapper.findAll('div.pointer').at(1).trigger('click')
|
await wrapper.findAll('div.pointer').at(2).trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not emit delete contribution', () => {
|
it('does not emit delete contribution', () => {
|
||||||
|
|||||||
@ -2,47 +2,97 @@
|
|||||||
<div class="contribution-list-item">
|
<div class="contribution-list-item">
|
||||||
<slot>
|
<slot>
|
||||||
<div class="border p-3 w-100 mb-1" :class="`border-${variant}`">
|
<div class="border p-3 w-100 mb-1" :class="`border-${variant}`">
|
||||||
<div class="d-inline-flex">
|
<div>
|
||||||
<div class="mr-2"><b-icon :icon="icon" :variant="variant" class="h2"></b-icon></div>
|
<div class="d-inline-flex">
|
||||||
<div v-if="firstName" class="mr-3">{{ firstName }} {{ lastName }}</div>
|
<div class="mr-2">
|
||||||
<div class="mr-2" :class="type != 'deleted' ? 'font-weight-bold' : ''">
|
<b-icon
|
||||||
{{ amount | GDD }}
|
v-if="state === 'IN_PROGRESS'"
|
||||||
|
icon="question-square"
|
||||||
|
font-scale="2"
|
||||||
|
variant="warning"
|
||||||
|
></b-icon>
|
||||||
|
<b-icon v-else :icon="icon" :variant="variant" class="h2"></b-icon>
|
||||||
|
</div>
|
||||||
|
<div v-if="firstName" class="mr-3">{{ firstName }} {{ lastName }}</div>
|
||||||
|
<div class="mr-2" :class="state !== 'DELETED' ? 'font-weight-bold' : ''">
|
||||||
|
{{ amount | GDD }}
|
||||||
|
</div>
|
||||||
|
{{ $t('math.minus') }}
|
||||||
|
<div class="mx-2">{{ $d(new Date(date), 'short') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">
|
||||||
|
<span>{{ $t('contribution.date') }}</span>
|
||||||
|
<span>
|
||||||
|
{{ $d(new Date(contributionDate), 'monthAndYear') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mr-2">{{ memo }}</div>
|
||||||
|
<div class="d-flex flex-row-reverse">
|
||||||
|
<div
|
||||||
|
v-if="!['CONFIRMED', 'DELETED'].includes(state)"
|
||||||
|
class="pointer ml-5"
|
||||||
|
@click="
|
||||||
|
$emit('update-contribution-form', {
|
||||||
|
id: id,
|
||||||
|
contributionDate: contributionDate,
|
||||||
|
memo: memo,
|
||||||
|
amount: amount,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<b-icon icon="pencil" class="h2"></b-icon>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!['CONFIRMED', 'DELETED'].includes(state)"
|
||||||
|
class="pointer"
|
||||||
|
@click="deleteContribution({ id })"
|
||||||
|
>
|
||||||
|
<b-icon icon="trash" class="h2"></b-icon>
|
||||||
|
</div>
|
||||||
|
<div v-if="messagesCount > 0" class="pointer">
|
||||||
|
<b-icon
|
||||||
|
v-b-toggle="collapsId"
|
||||||
|
icon="chat-dots"
|
||||||
|
class="h2 mr-5"
|
||||||
|
@click="getListContributionMessages"
|
||||||
|
></b-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ $t('math.minus') }}
|
|
||||||
<div class="mx-2">{{ $d(new Date(date), 'short') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mr-2">
|
<div v-if="messagesCount > 0">
|
||||||
<span>{{ $t('contribution.date') }}</span>
|
<b-button
|
||||||
<span>
|
v-if="state === 'IN_PROGRESS'"
|
||||||
{{ $d(new Date(contributionDate), 'monthAndYear') }}
|
v-b-toggle="collapsId"
|
||||||
</span>
|
variant="warning"
|
||||||
</div>
|
@click="getListContributionMessages"
|
||||||
<div class="mr-2">{{ memo }}</div>
|
|
||||||
<div v-if="type === 'pending' && !firstName" class="d-flex flex-row-reverse">
|
|
||||||
<div
|
|
||||||
class="pointer ml-5"
|
|
||||||
@click="
|
|
||||||
$emit('update-contribution-form', {
|
|
||||||
id: id,
|
|
||||||
contributionDate: contributionDate,
|
|
||||||
memo: memo,
|
|
||||||
amount: amount,
|
|
||||||
})
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<b-icon icon="pencil" class="h2"></b-icon>
|
{{ $t('contribution.alert.answerQuestion') }}
|
||||||
</div>
|
</b-button>
|
||||||
<div class="pointer" @click="deleteContribution({ id })">
|
<b-collapse :id="collapsId" class="mt-2">
|
||||||
<b-icon icon="trash" class="h2"></b-icon>
|
<b-card>
|
||||||
</div>
|
<contribution-messages-list
|
||||||
|
:messages="messages_get"
|
||||||
|
:state="state"
|
||||||
|
:contributionId="contributionId"
|
||||||
|
@get-list-contribution-messages="getListContributionMessages"
|
||||||
|
@update-state="updateState"
|
||||||
|
/>
|
||||||
|
</b-card>
|
||||||
|
</b-collapse>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import ContributionMessagesList from '@/components/ContributionMessages/ContributionMessagesList.vue'
|
||||||
|
import { listContributionMessages } from '../../graphql/queries.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ContributionListItem',
|
name: 'ContributionListItem',
|
||||||
|
components: {
|
||||||
|
ContributionMessagesList,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -79,13 +129,26 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
state: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
messagesCount: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
contributionId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inProcess: true,
|
||||||
|
messages_get: [],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
type() {
|
|
||||||
if (this.deletedAt) return 'deleted'
|
|
||||||
if (this.confirmedAt) return 'confirmed'
|
|
||||||
return 'pending'
|
|
||||||
},
|
|
||||||
icon() {
|
icon() {
|
||||||
if (this.deletedAt) return 'x-circle'
|
if (this.deletedAt) return 'x-circle'
|
||||||
if (this.confirmedAt) return 'check'
|
if (this.confirmedAt) return 'check'
|
||||||
@ -94,14 +157,15 @@ export default {
|
|||||||
variant() {
|
variant() {
|
||||||
if (this.deletedAt) return 'danger'
|
if (this.deletedAt) return 'danger'
|
||||||
if (this.confirmedAt) return 'success'
|
if (this.confirmedAt) return 'success'
|
||||||
|
if (this.state === 'IN_PROGRESS') return 'warning'
|
||||||
return 'primary'
|
return 'primary'
|
||||||
},
|
},
|
||||||
date() {
|
date() {
|
||||||
// if (this.deletedAt) return this.deletedAt
|
|
||||||
// if (this.confirmedAt) return this.confirmedAt
|
|
||||||
// return this.contributionDate
|
|
||||||
return this.createdAt
|
return this.createdAt
|
||||||
},
|
},
|
||||||
|
collapsId() {
|
||||||
|
return 'collapse' + String(this.id)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteContribution(item) {
|
deleteContribution(item) {
|
||||||
@ -109,6 +173,27 @@ export default {
|
|||||||
if (value) this.$emit('delete-contribution', item)
|
if (value) this.$emit('delete-contribution', item)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
getListContributionMessages() {
|
||||||
|
// console.log('getListContributionMessages', this.contributionId)
|
||||||
|
this.$apollo
|
||||||
|
.query({
|
||||||
|
query: listContributionMessages,
|
||||||
|
variables: {
|
||||||
|
contributionId: this.contributionId,
|
||||||
|
},
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
// console.log('result', result.data.listContributionMessages.messages)
|
||||||
|
this.messages_get = result.data.listContributionMessages.messages
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.toastError(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.$emit('update-state', id)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -45,7 +45,7 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
|
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with locales en, de and es', () => {
|
describe('with locales en, de, es, fr, and nl', () => {
|
||||||
describe('empty store', () => {
|
describe('empty store', () => {
|
||||||
describe('navigator language is "en-US"', () => {
|
describe('navigator language is "en-US"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
@ -94,11 +94,11 @@ describe('LanguageSwitch', () => {
|
|||||||
describe('navigator language is "nl-NL"', () => {
|
describe('navigator language is "nl-NL"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows Dutch as language ', async () => {
|
it('shows Nederlands as language ', async () => {
|
||||||
languageGetter.mockReturnValue('nl-NL')
|
languageGetter.mockReturnValue('nl-NL')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl')
|
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Nederlands - nl')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -153,16 +153,16 @@ describe('LanguageSwitch', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('language "nl" in store', () => {
|
describe('language "nl" in store', () => {
|
||||||
it('shows Dutch as language', async () => {
|
it('shows Nederlands as language', async () => {
|
||||||
wrapper.vm.$store.state.language = 'nl'
|
wrapper.vm.$store.state.language = 'nl'
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl')
|
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Nederlands - nl')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('dropdown menu', () => {
|
describe('dropdown menu', () => {
|
||||||
it('has English and German as languages to choose', () => {
|
it('has five languages to choose from', () => {
|
||||||
expect(wrapper.findAll('li')).toHaveLength(5)
|
expect(wrapper.findAll('li')).toHaveLength(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -174,16 +174,16 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('li').at(1).text()).toBe('Deutsch')
|
expect(wrapper.findAll('li').at(1).text()).toBe('Deutsch')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has Español as second language to choose', () => {
|
it('has Español as third language to choose', () => {
|
||||||
expect(wrapper.findAll('li').at(2).text()).toBe('Español')
|
expect(wrapper.findAll('li').at(2).text()).toBe('Español')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has French as second language to choose', () => {
|
it('has French as fourth language to choose', () => {
|
||||||
expect(wrapper.findAll('li').at(3).text()).toBe('Français')
|
expect(wrapper.findAll('li').at(3).text()).toBe('Français')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has Dutch as second language to choose', () => {
|
it('has Nederlands as fith language to choose', () => {
|
||||||
expect(wrapper.findAll('li').at(4).text()).toBe('Holandés')
|
expect(wrapper.findAll('li').at(4).text()).toBe('Nederlands')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -46,10 +46,11 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.find('div.language-switch').exists()).toBe(true)
|
expect(wrapper.find('div.language-switch').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with locales en and de', () => {
|
describe('with locales en, de, es, fr, and nl', () => {
|
||||||
describe('empty store', () => {
|
describe('empty store', () => {
|
||||||
describe('navigator language is "en-US"', () => {
|
describe('navigator language is "en-US"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows English as default navigator langauge', async () => {
|
it('shows English as default navigator langauge', async () => {
|
||||||
languageGetter.mockReturnValue('en-US')
|
languageGetter.mockReturnValue('en-US')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -57,8 +58,10 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('navigator language is "de-DE"', () => {
|
describe('navigator language is "de-DE"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows Deutsch as language ', async () => {
|
it('shows Deutsch as language ', async () => {
|
||||||
languageGetter.mockReturnValue('de-DE')
|
languageGetter.mockReturnValue('de-DE')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -66,8 +69,10 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('navigator language is "es-ES"', () => {
|
describe('navigator language is "es-ES"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows Español as language ', async () => {
|
it('shows Español as language ', async () => {
|
||||||
languageGetter.mockReturnValue('es-ES')
|
languageGetter.mockReturnValue('es-ES')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -75,8 +80,10 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español')
|
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('navigator language is "fr-FR"', () => {
|
describe('navigator language is "fr-FR"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows French as language ', async () => {
|
it('shows French as language ', async () => {
|
||||||
languageGetter.mockReturnValue('fr-FR')
|
languageGetter.mockReturnValue('fr-FR')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -84,17 +91,21 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français')
|
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('navigator language is "nl-NL"', () => {
|
describe('navigator language is "nl-NL"', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
it('shows Dutch as language ', async () => {
|
|
||||||
|
it('shows Nederlands as language ', async () => {
|
||||||
languageGetter.mockReturnValue('nl-NL')
|
languageGetter.mockReturnValue('nl-NL')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Holandés')
|
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Nederlands')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('navigator language is "it-IT" (not supported)', () => {
|
describe('navigator language is "it-IT" (not supported)', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows English as language ', async () => {
|
it('shows English as language ', async () => {
|
||||||
languageGetter.mockReturnValue('it-IT')
|
languageGetter.mockReturnValue('it-IT')
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -102,8 +113,10 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('no navigator langauge', () => {
|
describe('no navigator langauge', () => {
|
||||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||||
|
|
||||||
it('shows English as language ', async () => {
|
it('shows English as language ', async () => {
|
||||||
languageGetter.mockReturnValue(null)
|
languageGetter.mockReturnValue(null)
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
@ -112,6 +125,7 @@ describe('LanguageSwitch', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('language "de" in store', () => {
|
describe('language "de" in store', () => {
|
||||||
it('shows Deutsch as language', async () => {
|
it('shows Deutsch as language', async () => {
|
||||||
wrapper.vm.$store.state.language = 'de'
|
wrapper.vm.$store.state.language = 'de'
|
||||||
@ -120,6 +134,7 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('English')
|
expect(wrapper.findAll('span.locales').at(1).text()).toBe('English')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('language "es" in store', () => {
|
describe('language "es" in store', () => {
|
||||||
it('shows Español as language', async () => {
|
it('shows Español as language', async () => {
|
||||||
wrapper.vm.$store.state.language = 'es'
|
wrapper.vm.$store.state.language = 'es'
|
||||||
@ -128,6 +143,7 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch')
|
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('language "fr" in store', () => {
|
describe('language "fr" in store', () => {
|
||||||
it('shows French as language', async () => {
|
it('shows French as language', async () => {
|
||||||
wrapper.vm.$store.state.language = 'fr'
|
wrapper.vm.$store.state.language = 'fr'
|
||||||
@ -136,43 +152,77 @@ describe('LanguageSwitch', () => {
|
|||||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español')
|
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('language "nl" in store', () => {
|
describe('language "nl" in store', () => {
|
||||||
it('shows Dutch as language', async () => {
|
it('shows Nederlands as language', async () => {
|
||||||
wrapper.vm.$store.state.language = 'nl'
|
wrapper.vm.$store.state.language = 'nl'
|
||||||
wrapper.vm.setCurrentLanguage()
|
wrapper.vm.setCurrentLanguage()
|
||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français')
|
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('language menu', () => {
|
describe('language menu', () => {
|
||||||
it('has English, German and Español as languages to choose', () => {
|
beforeAll(async () => {
|
||||||
|
wrapper.vm.$store.state.language = 'en'
|
||||||
|
wrapper.vm.setCurrentLanguage()
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has five languages to choose from', () => {
|
||||||
expect(wrapper.findAll('span.locales')).toHaveLength(5)
|
expect(wrapper.findAll('span.locales')).toHaveLength(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has English as first language to choose', () => {
|
it('has English as first language to choose', () => {
|
||||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('Holandés')
|
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
||||||
})
|
})
|
||||||
it('has German as second language to choose', () => {
|
|
||||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('English')
|
it('has Deutsch as second language to choose', () => {
|
||||||
|
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has Español as third language to choose', () => {
|
it('has Español as third language to choose', () => {
|
||||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch')
|
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español')
|
||||||
})
|
})
|
||||||
it('has French as third language to choose', () => {
|
|
||||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español')
|
it('has Français as fourth language to choose', () => {
|
||||||
|
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français')
|
||||||
})
|
})
|
||||||
it('has Dutch as third language to choose', () => {
|
|
||||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français')
|
it('has Nederlands as fifth language to choose', () => {
|
||||||
|
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Nederlands')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('calls the API', () => {
|
describe('calls the API', () => {
|
||||||
it("with locale 'de'", () => {
|
it("with locale 'de'", () => {
|
||||||
wrapper.findAll('span.locales').at(2).trigger('click')
|
wrapper.findAll('span.locales').at(1).trigger('click')
|
||||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||||
expect.objectContaining({ variables: { locale: 'de' } }),
|
expect.objectContaining({ variables: { locale: 'de' } }),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("with locale 'es'", () => {
|
||||||
|
wrapper.findAll('span.locales').at(2).trigger('click')
|
||||||
|
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({ variables: { locale: 'es' } }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("with locale 'fr'", () => {
|
||||||
|
wrapper.findAll('span.locales').at(3).trigger('click')
|
||||||
|
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({ variables: { locale: 'fr' } }),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("with locale 'nl'", () => {
|
||||||
|
wrapper.findAll('span.locales').at(4).trigger('click')
|
||||||
|
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({ variables: { locale: 'nl' } }),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -122,3 +122,17 @@ export const deleteContribution = gql`
|
|||||||
deleteContribution(id: $id)
|
deleteContribution(id: $id)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const createContributionMessage = gql`
|
||||||
|
mutation($contributionId: Float!, $message: String!) {
|
||||||
|
createContributionMessage(contributionId: $contributionId, message: $message) {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -206,6 +206,8 @@ export const listContributions = gql`
|
|||||||
confirmedAt
|
confirmedAt
|
||||||
confirmedBy
|
confirmedBy
|
||||||
deletedAt
|
deletedAt
|
||||||
|
state
|
||||||
|
messagesCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,3 +257,26 @@ export const searchAdminUsers = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const listContributionMessages = gql`
|
||||||
|
query($contributionId: Float!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) {
|
||||||
|
listContributionMessages(
|
||||||
|
contributionId: $contributionId
|
||||||
|
pageSize: $pageSize
|
||||||
|
currentPage: $currentPage
|
||||||
|
order: $order
|
||||||
|
) {
|
||||||
|
count
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
type
|
||||||
|
userFirstName
|
||||||
|
userLastName
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -21,23 +21,23 @@
|
|||||||
"community": "Gemeinschaft",
|
"community": "Gemeinschaft",
|
||||||
"continue-to-registration": "Weiter zur Registrierung",
|
"continue-to-registration": "Weiter zur Registrierung",
|
||||||
"current-community": "Aktuelle Gemeinschaft",
|
"current-community": "Aktuelle Gemeinschaft",
|
||||||
"members": "Mitglieder",
|
"moderator": "Moderator",
|
||||||
"moderators": "Moderatoren",
|
"moderators": "Moderatoren",
|
||||||
"myContributions": "Meine Beiträge zum Gemeinwohl",
|
"myContributions": "Meine Beiträge zum Gemeinwohl",
|
||||||
"openContributionLinks": "öffentliche Beitrags-Linkliste",
|
"openContributionLinks": "öffentliche Beitrags-Linkliste",
|
||||||
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
||||||
"other-communities": "Weitere Gemeinschaften",
|
"other-communities": "Weitere Gemeinschaften",
|
||||||
"statistic": "Statistik",
|
|
||||||
"submitContribution": "Beitrag einreichen",
|
"submitContribution": "Beitrag einreichen",
|
||||||
"switch-to-this-community": "zu dieser Gemeinschaft wechseln"
|
"switch-to-this-community": "zu dieser Gemeinschaft wechseln"
|
||||||
},
|
},
|
||||||
"contribution": {
|
"contribution": {
|
||||||
"activity": "Tätigkeit",
|
"activity": "Tätigkeit",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
"answerQuestion": "Bitte beantworte die Nachfrage",
|
||||||
"communityNoteList": "Hier findest du alle eingereichten und bestätigten Beiträge von allen Mitgliedern aus dieser Gemeinschaft.",
|
"communityNoteList": "Hier findest du alle eingereichten und bestätigten Beiträge von allen Mitgliedern aus dieser Gemeinschaft.",
|
||||||
"confirm": "bestätigt",
|
"confirm": "bestätigt",
|
||||||
|
"in_progress": "Es gibt eine Rückfrage der Moderatoren.",
|
||||||
"myContributionNoteList": "Eingereichte Beiträge, die noch nicht bestätigt wurden, kannst du jederzeit bearbeiten oder löschen.",
|
"myContributionNoteList": "Eingereichte Beiträge, die noch nicht bestätigt wurden, kannst du jederzeit bearbeiten oder löschen.",
|
||||||
"myContributionNoteSupport": "Es wird bald an dieser Stelle die Möglichkeit geben das ein Dialog zwischen Moderatoren und dir stattfinden kann. Solltest du jetzt Probleme haben bitte nimm Kontakt mit dem Support auf.",
|
|
||||||
"pending": "Eingereicht und wartet auf Bestätigung",
|
"pending": "Eingereicht und wartet auf Bestätigung",
|
||||||
"rejected": "abgelehnt"
|
"rejected": "abgelehnt"
|
||||||
},
|
},
|
||||||
@ -130,6 +130,7 @@
|
|||||||
"password_new_repeat": "Neues Passwort wiederholen",
|
"password_new_repeat": "Neues Passwort wiederholen",
|
||||||
"password_old": "Altes Passwort",
|
"password_old": "Altes Passwort",
|
||||||
"recipient": "Empfänger",
|
"recipient": "Empfänger",
|
||||||
|
"reply": "Antworten",
|
||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"scann_code": "<strong>QR Code Scanner</strong> - Scanne den QR Code deines Partners",
|
"scann_code": "<strong>QR Code Scanner</strong> - Scanne den QR Code deines Partners",
|
||||||
@ -219,6 +220,7 @@
|
|||||||
"email": "Wir haben dir eine E-Mail gesendet.",
|
"email": "Wir haben dir eine E-Mail gesendet.",
|
||||||
"errorTitle": "Achtung!",
|
"errorTitle": "Achtung!",
|
||||||
"register": "Du bist jetzt registriert, bitte überprüfe deine Emails und klicke auf den Aktivierungslink.",
|
"register": "Du bist jetzt registriert, bitte überprüfe deine Emails und klicke auf den Aktivierungslink.",
|
||||||
|
"reply": "Danke, Deine Antwort wurde abgesendet.",
|
||||||
"reset": "Dein Passwort wurde geändert.",
|
"reset": "Dein Passwort wurde geändert.",
|
||||||
"title": "Danke!",
|
"title": "Danke!",
|
||||||
"unsetPassword": "Dein Passwort wurde noch nicht gesetzt. Bitte setze es neu."
|
"unsetPassword": "Dein Passwort wurde noch nicht gesetzt. Bitte setze es neu."
|
||||||
@ -251,7 +253,7 @@
|
|||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
"nl": "Dutch",
|
"nl": "Nederlands",
|
||||||
"success": "Deine Sprache wurde erfolgreich geändert."
|
"success": "Deine Sprache wurde erfolgreich geändert."
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
@ -303,11 +305,6 @@
|
|||||||
"uppercase": "Großbuchstabe erforderlich."
|
"uppercase": "Großbuchstabe erforderlich."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"statistic": {
|
|
||||||
"totalGradidoAvailable": "GDD insgesamt im Umlauf",
|
|
||||||
"totalGradidoCreated": "GDD insgesamt geschöpft",
|
|
||||||
"totalGradidoDecayed": "GDD insgesamt verfallen"
|
|
||||||
},
|
|
||||||
"success": "Erfolg",
|
"success": "Erfolg",
|
||||||
"time": {
|
"time": {
|
||||||
"days": "Tage",
|
"days": "Tage",
|
||||||
|
|||||||
@ -21,23 +21,23 @@
|
|||||||
"community": "Community",
|
"community": "Community",
|
||||||
"continue-to-registration": "Continue to registration",
|
"continue-to-registration": "Continue to registration",
|
||||||
"current-community": "Current community",
|
"current-community": "Current community",
|
||||||
"members": "Members",
|
"moderator": "Moderator",
|
||||||
"moderators": "Moderators",
|
"moderators": "Moderators",
|
||||||
"myContributions": "My contributions to the common good",
|
"myContributions": "My contributions to the common good",
|
||||||
"openContributionLinks": "open Contribution links list",
|
"openContributionLinks": "open Contribution links list",
|
||||||
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
||||||
"other-communities": "Other communities",
|
"other-communities": "Other communities",
|
||||||
"statistic": "Statistics",
|
|
||||||
"submitContribution": "Submit contribution",
|
"submitContribution": "Submit contribution",
|
||||||
"switch-to-this-community": "Switch to this community"
|
"switch-to-this-community": "Switch to this community"
|
||||||
},
|
},
|
||||||
"contribution": {
|
"contribution": {
|
||||||
"activity": "Activity",
|
"activity": "Activity",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
"answerQuestion": "Please answer the question",
|
||||||
"communityNoteList": "Here you will find all submitted and confirmed contributions from all members of this community.",
|
"communityNoteList": "Here you will find all submitted and confirmed contributions from all members of this community.",
|
||||||
"confirm": "confirmed",
|
"confirm": "confirmed",
|
||||||
|
"in_progress": "There is a question from the moderators.",
|
||||||
"myContributionNoteList": "You can edit or delete entries that have not yet been confirmed at any time.",
|
"myContributionNoteList": "You can edit or delete entries that have not yet been confirmed at any time.",
|
||||||
"myContributionNoteSupport": "Soon there will be the possibility for a dialogue between moderators and you. If you have any problems now, please contact the support.",
|
|
||||||
"pending": "Submitted and waiting for confirmation",
|
"pending": "Submitted and waiting for confirmation",
|
||||||
"rejected": "deleted"
|
"rejected": "deleted"
|
||||||
},
|
},
|
||||||
@ -130,6 +130,7 @@
|
|||||||
"password_new_repeat": "Repeat new password",
|
"password_new_repeat": "Repeat new password",
|
||||||
"password_old": "Old password",
|
"password_old": "Old password",
|
||||||
"recipient": "Recipient",
|
"recipient": "Recipient",
|
||||||
|
"reply": "Reply",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"scann_code": "<strong>QR Code Scanner</strong> - Scan the QR Code of your partner",
|
"scann_code": "<strong>QR Code Scanner</strong> - Scan the QR Code of your partner",
|
||||||
@ -219,6 +220,7 @@
|
|||||||
"email": "We have sent you an email.",
|
"email": "We have sent you an email.",
|
||||||
"errorTitle": "Attention!",
|
"errorTitle": "Attention!",
|
||||||
"register": "You are registered now, please check your emails and click the activation link.",
|
"register": "You are registered now, please check your emails and click the activation link.",
|
||||||
|
"reply": "Thank you, your reply has been sent.",
|
||||||
"reset": "Your password has been changed.",
|
"reset": "Your password has been changed.",
|
||||||
"title": "Thank you!",
|
"title": "Thank you!",
|
||||||
"unsetPassword": "Your password has not been set yet. Please set it again."
|
"unsetPassword": "Your password has not been set yet. Please set it again."
|
||||||
@ -251,7 +253,7 @@
|
|||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
"nl": "Holandés",
|
"nl": "Nederlands",
|
||||||
"success": "Your language has been successfully updated."
|
"success": "Your language has been successfully updated."
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
@ -303,11 +305,6 @@
|
|||||||
"uppercase": "One uppercase letter required."
|
"uppercase": "One uppercase letter required."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"statistic": {
|
|
||||||
"totalGradidoAvailable": "Total GDD in circulation",
|
|
||||||
"totalGradidoCreated": "Total GDD created",
|
|
||||||
"totalGradidoDecayed": "Total GDD decayed"
|
|
||||||
},
|
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"time": {
|
"time": {
|
||||||
"days": "Days",
|
"days": "Days",
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"continue-to-registration": "Continuar con el registro",
|
"continue-to-registration": "Continuar con el registro",
|
||||||
"current-community": "Comunidad actual",
|
"current-community": "Comunidad actual",
|
||||||
"members": "Miembros",
|
"members": "Miembros",
|
||||||
|
"moderator": "Moderador",
|
||||||
"moderators": "Moderadores",
|
"moderators": "Moderadores",
|
||||||
"myContributions": "Mis contribuciones al bien común",
|
"myContributions": "Mis contribuciones al bien común",
|
||||||
"openContributionLinks": "lista de enlaces de contribuciones públicas",
|
"openContributionLinks": "lista de enlaces de contribuciones públicas",
|
||||||
@ -34,10 +35,11 @@
|
|||||||
"contribution": {
|
"contribution": {
|
||||||
"activity": "Actividad",
|
"activity": "Actividad",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
"answerQuestion": "Por favor, contesta las preguntas",
|
||||||
"communityNoteList": "Aquí encontrarás todas las contribuciones enviadas y confirmadas de todos los miembros de esta comunidad.",
|
"communityNoteList": "Aquí encontrarás todas las contribuciones enviadas y confirmadas de todos los miembros de esta comunidad.",
|
||||||
"confirm": "confirmado",
|
"confirm": "confirmado",
|
||||||
|
"in_progress": "Hay una pregunta de los moderatores.",
|
||||||
"myContributionNoteList": "Puedes editar o eliminar las contribuciones enviadas que aún no han sido confirmadas en cualquier momento.",
|
"myContributionNoteList": "Puedes editar o eliminar las contribuciones enviadas que aún no han sido confirmadas en cualquier momento.",
|
||||||
"myContributionNoteSupport": "Pronto existirá la posibilidad de que puedas dialogar con los moderadores. Si tienes algún problema ahora, ponte en contacto con el equipo de asistencia.",
|
|
||||||
"pending": "Enviado y a la espera de confirmación",
|
"pending": "Enviado y a la espera de confirmación",
|
||||||
"rejected": "rechazado"
|
"rejected": "rechazado"
|
||||||
},
|
},
|
||||||
@ -130,6 +132,7 @@
|
|||||||
"password_new_repeat": "Repetir contraseña nueva",
|
"password_new_repeat": "Repetir contraseña nueva",
|
||||||
"password_old": "contraseña antigua",
|
"password_old": "contraseña antigua",
|
||||||
"recipient": "Destinatario",
|
"recipient": "Destinatario",
|
||||||
|
"reply": "Respuesta",
|
||||||
"reset": "Restablecer",
|
"reset": "Restablecer",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"scann_code": "<strong>QR Code Scanner</strong> - Escanea el código QR de tu pareja",
|
"scann_code": "<strong>QR Code Scanner</strong> - Escanea el código QR de tu pareja",
|
||||||
@ -219,6 +222,7 @@
|
|||||||
"email": "Te hemos enviado un correo electrónico.",
|
"email": "Te hemos enviado un correo electrónico.",
|
||||||
"errorTitle": "Atención!",
|
"errorTitle": "Atención!",
|
||||||
"register": "Ya estás registrado, por favor revisa tu correo electrónico y haz clic en el enlace de activación.",
|
"register": "Ya estás registrado, por favor revisa tu correo electrónico y haz clic en el enlace de activación.",
|
||||||
|
"reply": "Gracias, tu respuesta ha sido enviada.",
|
||||||
"reset": "Tu contraseña ha sido cambiada.",
|
"reset": "Tu contraseña ha sido cambiada.",
|
||||||
"title": "Gracias!",
|
"title": "Gracias!",
|
||||||
"unsetPassword": "Tu contraseña aún no ha sido configurada. Por favor reinícialo."
|
"unsetPassword": "Tu contraseña aún no ha sido configurada. Por favor reinícialo."
|
||||||
@ -250,8 +254,8 @@
|
|||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
"fr": "Francés",
|
"fr": "Français",
|
||||||
"nl": "Holandés",
|
"nl": "Nederlands",
|
||||||
"success": "Tu idioma ha sido cambiado con éxito."
|
"success": "Tu idioma ha sido cambiado con éxito."
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"continue-to-registration": "Continuez l´inscription",
|
"continue-to-registration": "Continuez l´inscription",
|
||||||
"current-community": "Communauté actuelle",
|
"current-community": "Communauté actuelle",
|
||||||
"members": "Membres",
|
"members": "Membres",
|
||||||
|
"moderator": "Modérateur",
|
||||||
"moderators": "Modérateurs",
|
"moderators": "Modérateurs",
|
||||||
"myContributions": "Mes contributions aux biens communs",
|
"myContributions": "Mes contributions aux biens communs",
|
||||||
"openContributionLinks": "liste de liens de contribution publique",
|
"openContributionLinks": "liste de liens de contribution publique",
|
||||||
@ -34,10 +35,11 @@
|
|||||||
"contribution": {
|
"contribution": {
|
||||||
"activity": "Activité",
|
"activity": "Activité",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
"answerQuestion": "S'il te plais répond à la question",
|
||||||
"communityNoteList": "Vous trouverez ci-contre toutes les contributions versées et certifiées de tous les membres de cette communauté.",
|
"communityNoteList": "Vous trouverez ci-contre toutes les contributions versées et certifiées de tous les membres de cette communauté.",
|
||||||
"confirm": " Approuvé",
|
"confirm": " Approuvé",
|
||||||
|
"in_progress": "Il y a une question du modérateur.",
|
||||||
"myContributionNoteList": "À tout moment vous pouvez éditer ou supprimer les données qui n´ont pas été confirmées.",
|
"myContributionNoteList": "À tout moment vous pouvez éditer ou supprimer les données qui n´ont pas été confirmées.",
|
||||||
"myContributionNoteSupport": "Vous aurez bientôt la possibilité de dialoguer avec un médiateur. Si vous rencontrez un problème maintenant, merci de contacter l´aide en ligne.",
|
|
||||||
"pending": "Inscription en attente de validation",
|
"pending": "Inscription en attente de validation",
|
||||||
"rejected": "supprimé"
|
"rejected": "supprimé"
|
||||||
},
|
},
|
||||||
@ -130,6 +132,7 @@
|
|||||||
"password_new_repeat": "Répétez le nouveau mot de passe",
|
"password_new_repeat": "Répétez le nouveau mot de passe",
|
||||||
"password_old": "Ancien mot de passe",
|
"password_old": "Ancien mot de passe",
|
||||||
"recipient": "Destinataire",
|
"recipient": "Destinataire",
|
||||||
|
"reply": "Répondre",
|
||||||
"reset": "Réinitialiser",
|
"reset": "Réinitialiser",
|
||||||
"save": "Sauvegarder",
|
"save": "Sauvegarder",
|
||||||
"scann_code": "<strong>QR Code Scanner</strong> - Scannez le QR code de votre partenaire",
|
"scann_code": "<strong>QR Code Scanner</strong> - Scannez le QR code de votre partenaire",
|
||||||
@ -219,6 +222,7 @@
|
|||||||
"email": "Nous vous avons envoyé un email.",
|
"email": "Nous vous avons envoyé un email.",
|
||||||
"errorTitle": "Attention!",
|
"errorTitle": "Attention!",
|
||||||
"register": "Vous êtes enregistré maintenant, merci de vérifier votre boîte mail et cliquer sur le lien d´activation.",
|
"register": "Vous êtes enregistré maintenant, merci de vérifier votre boîte mail et cliquer sur le lien d´activation.",
|
||||||
|
"reply": "Merci, ta réponse a été envoyée.",
|
||||||
"reset": "Votre mot de passe a été modifié.",
|
"reset": "Votre mot de passe a été modifié.",
|
||||||
"title": "Merci!",
|
"title": "Merci!",
|
||||||
"unsetPassword": "Votre mot de passe n´a pas été accepté. Merci de le réinitialiser."
|
"unsetPassword": "Votre mot de passe n´a pas été accepté. Merci de le réinitialiser."
|
||||||
@ -247,11 +251,11 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"language": {
|
"language": {
|
||||||
"changeLanguage": "Changer la langue",
|
"changeLanguage": "Changer la langue",
|
||||||
"de": "Allemand",
|
"de": "Deutsch",
|
||||||
"en": "Anglais",
|
"en": "English",
|
||||||
"es": "Espagnol",
|
"es": "Español",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
"nl": "Néerlandais",
|
"nl": "Nederlands",
|
||||||
"success": "Votre langue de préférence a bien été actualisée."
|
"success": "Votre langue de préférence a bien été actualisée."
|
||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const locales = [
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Holandés',
|
name: 'Nederlands',
|
||||||
code: 'nl',
|
code: 'nl',
|
||||||
iso: 'nl-NL',
|
iso: 'nl-NL',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"continue-to-registration": "Verder ter registratie",
|
"continue-to-registration": "Verder ter registratie",
|
||||||
"current-community": "Actuele gemeenschap",
|
"current-community": "Actuele gemeenschap",
|
||||||
"members": "Leden",
|
"members": "Leden",
|
||||||
|
"moderator": "Moderator",
|
||||||
"moderators": "Moderators",
|
"moderators": "Moderators",
|
||||||
"myContributions": "Mijn bijdragen voor het algemeen belang",
|
"myContributions": "Mijn bijdragen voor het algemeen belang",
|
||||||
"openContributionLinks": "openbare lijst van bijdragen",
|
"openContributionLinks": "openbare lijst van bijdragen",
|
||||||
@ -34,10 +35,11 @@
|
|||||||
"contribution": {
|
"contribution": {
|
||||||
"activity": "Activiteit",
|
"activity": "Activiteit",
|
||||||
"alert": {
|
"alert": {
|
||||||
|
"answerQuestion": "Please answer the question",
|
||||||
"communityNoteList": "Hier vind je alle ingediende en bevestigde bijdragen van alle leden uit deze gemeenschap.",
|
"communityNoteList": "Hier vind je alle ingediende en bevestigde bijdragen van alle leden uit deze gemeenschap.",
|
||||||
"confirm": "bevestigt",
|
"confirm": "bevestigt",
|
||||||
|
"in_progress": "There is a question from the moderators.",
|
||||||
"myContributionNoteList": "Ingediende bijdragen, die nog niet bevestigd zijn, kun je op elk moment wijzigen of verwijderen.",
|
"myContributionNoteList": "Ingediende bijdragen, die nog niet bevestigd zijn, kun je op elk moment wijzigen of verwijderen.",
|
||||||
"myContributionNoteSupport": "Hier heb je binnenkort de mogelijkheid een gesprek met een moderator te voeren. Mocht je nu problemen hebben, dan neem alsjeblieft contact op met Support.",
|
|
||||||
"pending": "Ingediend en wacht op bevestiging",
|
"pending": "Ingediend en wacht op bevestiging",
|
||||||
"rejected": "afgewezen"
|
"rejected": "afgewezen"
|
||||||
},
|
},
|
||||||
@ -130,6 +132,7 @@
|
|||||||
"password_new_repeat": "Nieuw wachtwoord herhalen",
|
"password_new_repeat": "Nieuw wachtwoord herhalen",
|
||||||
"password_old": "Oud wachtwoord",
|
"password_old": "Oud wachtwoord",
|
||||||
"recipient": "Ontvanger",
|
"recipient": "Ontvanger",
|
||||||
|
"reply": "Antwoord",
|
||||||
"reset": "Resetten",
|
"reset": "Resetten",
|
||||||
"save": "Opslaan",
|
"save": "Opslaan",
|
||||||
"scann_code": "<strong>QR Code Scanner</strong> - Scan de QR Code van uw partner",
|
"scann_code": "<strong>QR Code Scanner</strong> - Scan de QR Code van uw partner",
|
||||||
@ -219,6 +222,7 @@
|
|||||||
"email": "We hebben jou een email gestuurd.",
|
"email": "We hebben jou een email gestuurd.",
|
||||||
"errorTitle": "Opgelet!",
|
"errorTitle": "Opgelet!",
|
||||||
"register": "Je bent nu geregistreerd. Controleer alsjeblieft je emails en klik op de activeringslink.",
|
"register": "Je bent nu geregistreerd. Controleer alsjeblieft je emails en klik op de activeringslink.",
|
||||||
|
"reply": "Dank u, uw antwoord is verzonden.",
|
||||||
"reset": "Jouw wachtwoord werd gewijzigd.",
|
"reset": "Jouw wachtwoord werd gewijzigd.",
|
||||||
"title": "Dankjewel!",
|
"title": "Dankjewel!",
|
||||||
"unsetPassword": "Jouw wachtwoord werd nog niet ingesteld. Doe het alsjeblieft opnieuw."
|
"unsetPassword": "Jouw wachtwoord werd nog niet ingesteld. Doe het alsjeblieft opnieuw."
|
||||||
@ -247,10 +251,10 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"language": {
|
"language": {
|
||||||
"changeLanguage": "Taal veranderen",
|
"changeLanguage": "Taal veranderen",
|
||||||
"de": "Duits",
|
"de": "Deutsch",
|
||||||
"en": "Engels",
|
"en": "English",
|
||||||
"es": "Spaans",
|
"es": "Español",
|
||||||
"fr": "Frans",
|
"fr": "Français",
|
||||||
"nl": "Nederlands",
|
"nl": "Nederlands",
|
||||||
"success": "Jouw taal werd succesvol veranderd."
|
"success": "Jouw taal werd succesvol veranderd."
|
||||||
},
|
},
|
||||||
|
|||||||
@ -93,9 +93,7 @@ describe('Community', () => {
|
|||||||
expect(wrapper.findAll('div[role="tabpanel"]')).toHaveLength(3)
|
expect(wrapper.findAll('div[role="tabpanel"]')).toHaveLength(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has first tab active by default', () => {
|
it.todo('check for correct tabIndex if state is "IN_PROGRESS" or not')
|
||||||
expect(wrapper.findAll('div[role="tabpanel"]').at(0).classes('active')).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('API calls after creation', () => {
|
describe('API calls after creation', () => {
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<div class="community-page">
|
<div class="community-page">
|
||||||
<div>
|
<div>
|
||||||
<b-tabs v-model="tabIndex" content-class="mt-3" align="center">
|
<b-tabs v-model="tabIndex" content-class="mt-3" align="center">
|
||||||
<b-tab :title="$t('community.submitContribution')" active>
|
<b-tab :title="$t('community.submitContribution')">
|
||||||
<contribution-form
|
<contribution-form
|
||||||
@set-contribution="setContribution"
|
@set-contribution="setContribution"
|
||||||
@update-contribution="updateContribution"
|
@update-contribution="updateContribution"
|
||||||
@ -22,6 +22,10 @@
|
|||||||
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
||||||
{{ $t('contribution.alert.pending') }}
|
{{ $t('contribution.alert.pending') }}
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="question-square" variant="warning"></b-icon>
|
||||||
|
{{ $t('contribution.alert.in_progress') }}
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<b-icon icon="check" variant="success"></b-icon>
|
<b-icon icon="check" variant="success"></b-icon>
|
||||||
{{ $t('contribution.alert.confirm') }}
|
{{ $t('contribution.alert.confirm') }}
|
||||||
@ -32,9 +36,6 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<p class="mb-0">
|
|
||||||
{{ $t('contribution.alert.myContributionNoteSupport') }}
|
|
||||||
</p>
|
|
||||||
</b-alert>
|
</b-alert>
|
||||||
</div>
|
</div>
|
||||||
<contribution-list
|
<contribution-list
|
||||||
@ -42,6 +43,7 @@
|
|||||||
@update-list-contributions="updateListContributions"
|
@update-list-contributions="updateListContributions"
|
||||||
@update-contribution-form="updateContributionForm"
|
@update-contribution-form="updateContributionForm"
|
||||||
@delete-contribution="deleteContribution"
|
@delete-contribution="deleteContribution"
|
||||||
|
@update-state="updateState"
|
||||||
:contributionCount="contributionCount"
|
:contributionCount="contributionCount"
|
||||||
:showPagination="true"
|
:showPagination="true"
|
||||||
:pageSize="pageSize"
|
:pageSize="pageSize"
|
||||||
@ -226,6 +228,11 @@ export default {
|
|||||||
} = result
|
} = result
|
||||||
this.contributionCount = listContributions.contributionCount
|
this.contributionCount = listContributions.contributionCount
|
||||||
this.items = listContributions.contributionList
|
this.items = listContributions.contributionList
|
||||||
|
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
|
||||||
|
this.tabIndex = 1
|
||||||
|
} else {
|
||||||
|
this.tabIndex = 0
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.toastError(err.message)
|
this.toastError(err.message)
|
||||||
@ -258,6 +265,9 @@ export default {
|
|||||||
updateTransactions(pagination) {
|
updateTransactions(pagination) {
|
||||||
this.$emit('update-transactions', pagination)
|
this.$emit('update-transactions', pagination)
|
||||||
},
|
},
|
||||||
|
updateState(id) {
|
||||||
|
this.items.find((item) => item.id === id).state = 'PENDING'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
// verifyLogin is important at this point so that creation is updated on reload if they are deleted in a session in the admin area.
|
// verifyLogin is important at this point so that creation is updated on reload if they are deleted in a session in the admin area.
|
||||||
@ -271,6 +281,7 @@ export default {
|
|||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
})
|
})
|
||||||
this.updateTransactions(0)
|
this.updateTransactions(0)
|
||||||
|
this.tabIndex = 1
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -98,7 +98,7 @@ describe('InfoStatistic', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls getCommunityStatistics', () => {
|
it.skip('calls getCommunityStatistics', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
query: communityStatistics,
|
query: communityStatistics,
|
||||||
@ -115,12 +115,12 @@ describe('InfoStatistic', () => {
|
|||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toasts three error messages', () => {
|
it('toasts two error messages', () => {
|
||||||
expect(toastErrorSpy).toBeCalledWith(
|
expect(toastErrorSpy).toBeCalledWith(
|
||||||
'listContributionLinks has no result, use default data',
|
'listContributionLinks has no result, use default data',
|
||||||
)
|
)
|
||||||
expect(toastErrorSpy).toBeCalledWith('searchAdminUsers has no result, use default data')
|
expect(toastErrorSpy).toBeCalledWith('searchAdminUsers has no result, use default data')
|
||||||
expect(toastErrorSpy).toBeCalledWith('communityStatistics has no result, use default data')
|
// expect(toastErrorSpy).toBeCalledWith('communityStatistics has no result, use default data')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<b-link href="mailto: abc@example.com">{{ supportMail }}</b-link>
|
<b-link href="mailto: abc@example.com">{{ supportMail }}</b-link>
|
||||||
</b-container>
|
</b-container>
|
||||||
|
<!--
|
||||||
<hr />
|
<hr />
|
||||||
<b-container>
|
<b-container>
|
||||||
<div class="h3">{{ $t('community.statistic') }}</div>
|
<div class="h3">{{ $t('community.statistic') }}</div>
|
||||||
@ -65,11 +66,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-container>
|
</b-container>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import { listContributionLinks, communityStatistics, searchAdminUsers } from '@/graphql/queries'
|
import { listContributionLinks, searchAdminUsers } from '@/graphql/queries'
|
||||||
|
// , communityStatistics
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'InfoStatistic',
|
name: 'InfoStatistic',
|
||||||
@ -115,23 +118,25 @@ export default {
|
|||||||
this.toastError('searchAdminUsers has no result, use default data')
|
this.toastError('searchAdminUsers has no result, use default data')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
getCommunityStatistics() {
|
/*
|
||||||
this.$apollo
|
getCommunityStatistics() {
|
||||||
|
this.$apollo
|
||||||
.query({
|
.query({
|
||||||
query: communityStatistics,
|
query: communityStatistics,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
this.totalUsers = result.data.communityStatistics.totalUsers
|
this.totalUsers = result.data.communityStatistics.totalUsers
|
||||||
this.totalGradidoCreated =
|
this.totalGradidoCreated = result.data.communityStatistics.totalGradidoCreated
|
||||||
Number(result.data.communityStatistics.totalGradidoCreated) -
|
this.totalGradidoDecayed =
|
||||||
Number(result.data.communityStatistics.totalGradidoUnbookedDecayed)
|
Number(result.data.communityStatistics.totalGradidoDecayed) +
|
||||||
this.totalGradidoDecayed = result.data.communityStatistics.totalGradidoDecayed
|
Number(result.data.communityStatistics.totalGradidoUnbookedDecayed)
|
||||||
this.totalGradidoAvailable = result.data.communityStatistics.totalGradidoAvailable
|
this.totalGradidoAvailable = result.data.communityStatistics.totalGradidoAvailable
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.toastError('communityStatistics has no result, use default data')
|
this.toastError('communityStatistics has no result, use default data')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
updateTransactions(pagination) {
|
updateTransactions(pagination) {
|
||||||
this.$emit('update-transactions', pagination)
|
this.$emit('update-transactions', pagination)
|
||||||
},
|
},
|
||||||
@ -139,7 +144,7 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.getContributionLinks()
|
this.getContributionLinks()
|
||||||
this.getAdminUsers()
|
this.getAdminUsers()
|
||||||
this.getCommunityStatistics()
|
// this.getCommunityStatistics()
|
||||||
this.updateTransactions(0)
|
this.updateTransactions(0)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido",
|
"name": "gradido",
|
||||||
"version": "1.11.0",
|
"version": "1.12.0",
|
||||||
"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