mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into contribution-resolver-logs-and-events
This commit is contained in:
commit
0ddd08d1e1
@ -15,7 +15,10 @@
|
||||
<b-collapse v-model="visible" id="newContribution" class="mt-2">
|
||||
<b-card>
|
||||
<p class="h2 ml-5">{{ $t('contributionLink.contributionLinks') }}</p>
|
||||
<contribution-link-form :contributionLinkData="contributionLinkData" />
|
||||
<contribution-link-form
|
||||
:contributionLinkData="contributionLinkData"
|
||||
@get-contribution-links="$emit('get-contribution-links')"
|
||||
/>
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
|
||||
@ -24,6 +27,7 @@
|
||||
v-if="count > 0"
|
||||
:items="items"
|
||||
@editContributionLinkData="editContributionLinkData"
|
||||
@get-contribution-links="$emit('get-contribution-links')"
|
||||
/>
|
||||
<div v-else>{{ $t('contributionLink.noContributionLinks') }}</div>
|
||||
</b-card-text>
|
||||
|
||||
@ -163,7 +163,6 @@ export default {
|
||||
if (this.form.validFrom === null)
|
||||
return this.toastError(this.$t('contributionLink.noStartDate'))
|
||||
if (this.form.validTo === null) return this.toastError(this.$t('contributionLink.noEndDate'))
|
||||
// alert(JSON.stringify(this.form))
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: createContributionLink,
|
||||
@ -182,6 +181,8 @@ export default {
|
||||
this.link = result.data.createContributionLink.link
|
||||
this.toastSuccess(this.link)
|
||||
this.onReset()
|
||||
this.$root.$emit('bv::toggle::collapse', 'newContribution')
|
||||
this.$emit('get-contribution-links')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
|
||||
@ -95,7 +95,7 @@ describe('ContributionLinkList', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('TODO: request message deleted ')
|
||||
expect(toastSuccessSpy).toBeCalledWith('contributionLink.deleted')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="contribution-link-list">
|
||||
<b-table striped hover :items="items" :fields="fields">
|
||||
<template #cell(delete)>
|
||||
<template #cell(delete)="data">
|
||||
<b-button
|
||||
variant="danger"
|
||||
size="md"
|
||||
class="mr-2 test-delete-link"
|
||||
@click="deleteContributionLink"
|
||||
@click="deleteContributionLink(data.item.id, data.item.name)"
|
||||
>
|
||||
<b-icon icon="trash" variant="light"></b-icon>
|
||||
</b-button>
|
||||
@ -34,7 +34,7 @@
|
||||
<h6 class="mb-0">{{ modalData ? modalData.name : '' }}</h6>
|
||||
</template>
|
||||
<b-card-text>
|
||||
{{ modalData }}
|
||||
{{ modalData.memo ? modalData.memo : '' }}
|
||||
<figure-qr-code :link="modalData ? modalData.link : ''" />
|
||||
</b-card-text>
|
||||
<template #footer>
|
||||
@ -70,23 +70,25 @@ export default {
|
||||
'edit',
|
||||
'show',
|
||||
],
|
||||
modalData: null,
|
||||
modalDataLink: null,
|
||||
modalData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteContributionLink() {
|
||||
this.$bvModal.msgBoxConfirm(this.$t('contributionLink.deleteNow')).then(async (value) => {
|
||||
deleteContributionLink(id, name) {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm(this.$t('contributionLink.deleteNow', { name: name }))
|
||||
.then(async (value) => {
|
||||
if (value)
|
||||
await this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteContributionLink,
|
||||
variables: {
|
||||
id: this.id,
|
||||
id: id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.toastSuccess('TODO: request message deleted ')
|
||||
this.toastSuccess(this.$t('contributionLink.deleted'))
|
||||
this.$emit('get-contribution-links')
|
||||
})
|
||||
.catch((err) => {
|
||||
this.toastError(err.message)
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
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>
|
||||
|
||||
@ -9,32 +9,27 @@ describe('ContributionMessagesListItem', () => {
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => d),
|
||||
$store: {
|
||||
state: {
|
||||
moderator: {
|
||||
id: 107,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
describe('if message author has moderator role', () => {
|
||||
const propsData = {
|
||||
contributionId: 42,
|
||||
state: 'PENDING0',
|
||||
state: 'PENDING',
|
||||
message: {
|
||||
id: 111,
|
||||
message: 'asd asda sda sda',
|
||||
message: 'Lorem ipsum?',
|
||||
createdAt: '2022-08-29T12:23:27.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
userFirstName: 'Peter',
|
||||
userLastName: 'Lustig',
|
||||
userId: 107,
|
||||
isModerator: true,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
const ModeratorItemWrapper = () => {
|
||||
return mount(ContributionMessagesListItem, {
|
||||
localVue,
|
||||
mocks,
|
||||
@ -43,16 +38,91 @@ describe('ContributionMessagesListItem', () => {
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
beforeAll(() => {
|
||||
wrapper = ModeratorItemWrapper()
|
||||
})
|
||||
|
||||
it('has a DIV .contribution-messages-list-item', () => {
|
||||
expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
|
||||
it('has a DIV .text-right.is-moderator', () => {
|
||||
expect(wrapper.find('div.text-right.is-moderator').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('props.message.default', () => {
|
||||
expect(wrapper.vm.$options.props.message.default.call()).toEqual({})
|
||||
it('has the complete user name', () => {
|
||||
expect(wrapper.find('div.text-right.is-moderator > span:nth-child(2)').text()).toBe(
|
||||
'Peter Lustig',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message creation date', () => {
|
||||
expect(wrapper.find('div.text-right.is-moderator > span:nth-child(3)').text()).toMatch(
|
||||
'Mon Aug 29 2022 12:23:27 GMT+0000',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the moderator label', () => {
|
||||
expect(wrapper.find('div.text-right.is-moderator > small:nth-child(4)').text()).toBe(
|
||||
'moderator',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message', () => {
|
||||
expect(wrapper.find('div.text-right.is-moderator > div:nth-child(5)').text()).toBe(
|
||||
'Lorem ipsum?',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('if message author does not have moderator role', () => {
|
||||
const propsData = {
|
||||
contributionId: 42,
|
||||
state: 'PENDING',
|
||||
message: {
|
||||
id: 113,
|
||||
message: 'Asda sdad ad asdasd, das Ass das Das. ',
|
||||
createdAt: '2022-08-29T12:25:34.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
userFirstName: 'Bibi',
|
||||
userLastName: 'Bloxberg',
|
||||
userId: 108,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
}
|
||||
|
||||
const ItemWrapper = () => {
|
||||
return mount(ContributionMessagesListItem, {
|
||||
localVue,
|
||||
mocks,
|
||||
propsData,
|
||||
})
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = ItemWrapper()
|
||||
})
|
||||
|
||||
it('has a DIV .text-left.is-not-moderator', () => {
|
||||
expect(wrapper.find('div.text-left.is-not-moderator').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has the complete user name', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-left > span:nth-child(2)').text()).toBe(
|
||||
'Bibi Bloxberg',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message creation date', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-left > span:nth-child(3)').text()).toMatch(
|
||||
'Mon Aug 29 2022 12:25:34 GMT+0000',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-left > div:nth-child(4)').text()).toBe(
|
||||
'Asda sdad ad asdasd, das Ass das Das.',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,27 +1,44 @@
|
||||
<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 v-if="message.isModerator" class="text-right 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('moderator') }}</small>
|
||||
<div class="mt-2">{{ message.message }}</div>
|
||||
</div>
|
||||
<div v-else class="text-left is-not-moderator">
|
||||
<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>
|
||||
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>
|
||||
<style>
|
||||
.is-not-moderator {
|
||||
clear: both;
|
||||
width: 75%;
|
||||
margin-top: 20px;
|
||||
/* background-color: rgb(261, 204, 221); */
|
||||
}
|
||||
.is-moderator {
|
||||
clear: both;
|
||||
float: right;
|
||||
width: 75%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
/* background-color: rgb(255, 255, 128); */
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
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({})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,37 +0,0 @@
|
||||
<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>
|
||||
@ -1,49 +0,0 @@
|
||||
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({})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,34 +0,0 @@
|
||||
<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>
|
||||
@ -6,31 +6,30 @@ const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
creationTransactionList: [
|
||||
creationTransactionList: {
|
||||
contributionCount: 2,
|
||||
contributionList: [
|
||||
{
|
||||
id: 1,
|
||||
amount: 100,
|
||||
balanceDate: 0,
|
||||
creationDate: new Date(),
|
||||
memo: 'Testing',
|
||||
linkedUser: {
|
||||
firstName: 'Gradido',
|
||||
lastName: 'Akademie',
|
||||
},
|
||||
amount: 5.8,
|
||||
createdAt: '2022-09-21T11:09:51.000Z',
|
||||
confirmedAt: null,
|
||||
contributionDate: '2022-08-01T00:00:00.000Z',
|
||||
memo: 'für deine Hilfe, Fräulein Rottenmeier',
|
||||
state: 'PENDING',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
amount: 200,
|
||||
balanceDate: 0,
|
||||
creationDate: new Date(),
|
||||
memo: 'Testing 2',
|
||||
linkedUser: {
|
||||
firstName: 'Gradido',
|
||||
lastName: 'Akademie',
|
||||
},
|
||||
amount: '47',
|
||||
createdAt: '2022-09-21T11:09:28.000Z',
|
||||
confirmedAt: '2022-09-21T11:09:28.000Z',
|
||||
contributionDate: '2022-08-01T00:00:00.000Z',
|
||||
memo: 'für deine Hilfe, Frau Holle',
|
||||
state: 'CONFIRMED',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const mocks = {
|
||||
@ -43,7 +42,7 @@ const mocks = {
|
||||
|
||||
const propsData = {
|
||||
userId: 1,
|
||||
fields: ['date', 'balance', 'name', 'memo', 'decay'],
|
||||
fields: ['createdAt', 'contributionDate', 'confirmedAt', 'amount', 'memo'],
|
||||
}
|
||||
|
||||
describe('CreationTransactionList', () => {
|
||||
@ -63,7 +62,7 @@ describe('CreationTransactionList', () => {
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
pageSize: 10,
|
||||
order: 'DESC',
|
||||
userId: 1,
|
||||
},
|
||||
|
||||
@ -1,7 +1,44 @@
|
||||
<template>
|
||||
<div class="component-creation-transaction-list">
|
||||
<div class="h3">{{ $t('transactionlist.title') }}</div>
|
||||
<b-table striped hover :fields="fields" :items="items"></b-table>
|
||||
<b-table striped hover :fields="fields" :items="items">
|
||||
<template #cell(contributionDate)="data">
|
||||
<div class="font-weight-bold">
|
||||
{{ $d(new Date(data.item.contributionDate), 'month') }}
|
||||
</div>
|
||||
<div>{{ $d(new Date(data.item.contributionDate)) }}</div>
|
||||
</template>
|
||||
</b-table>
|
||||
<div>
|
||||
<b-pagination
|
||||
pills
|
||||
size="lg"
|
||||
v-model="currentPage"
|
||||
:per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
:hide-ellipsis="true"
|
||||
></b-pagination>
|
||||
<b-button v-b-toggle.collapse-1 variant="light" size="sm">{{ $t('help.help') }}</b-button>
|
||||
<b-collapse id="collapse-1" class="mt-2">
|
||||
<div>
|
||||
{{ $t('transactionlist.submitted') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.submitted') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.period') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.periods') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.confirmed') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.confirmed') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.state') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.state') }}
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -13,14 +50,37 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 10,
|
||||
fields: [
|
||||
{
|
||||
key: 'creationDate',
|
||||
label: this.$t('transactionlist.date'),
|
||||
key: 'createdAt',
|
||||
label: this.$t('transactionlist.submitted'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('transactionlist.period'),
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: this.$t('transactionlist.confirmed'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'state',
|
||||
label: this.$t('transactionlist.state'),
|
||||
},
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('transactionlist.amount'),
|
||||
@ -28,23 +88,8 @@ export default {
|
||||
return `${value} GDD`
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'linkedUser',
|
||||
label: this.$t('transactionlist.community'),
|
||||
formatter: (value, key, item) => {
|
||||
return `${value.firstName} ${value.lastName}`
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('transactionlist.memo') },
|
||||
{
|
||||
key: 'balanceDate',
|
||||
label: this.$t('transactionlist.balanceDate'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
],
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -53,14 +98,15 @@ export default {
|
||||
.query({
|
||||
query: creationTransactionList,
|
||||
variables: {
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
order: 'DESC',
|
||||
userId: parseInt(this.userId),
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.items = result.data.creationTransactionList
|
||||
this.rows = result.data.creationTransactionList.contributionCount
|
||||
this.items = result.data.creationTransactionList.contributionList
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
@ -70,5 +116,10 @@ export default {
|
||||
created() {
|
||||
this.getTransactions()
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.getTransactions()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -8,14 +8,15 @@ export const creationTransactionList = gql`
|
||||
order: $order
|
||||
userId: $userId
|
||||
) {
|
||||
contributionCount
|
||||
contributionList {
|
||||
id
|
||||
amount
|
||||
balanceDate
|
||||
creationDate
|
||||
createdAt
|
||||
confirmedAt
|
||||
contributionDate
|
||||
memo
|
||||
linkedUser {
|
||||
firstName
|
||||
lastName
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const showContributionLink = gql`
|
||||
query ($id: Int!) {
|
||||
showContributionLink {
|
||||
id
|
||||
validFrom
|
||||
validTo
|
||||
name
|
||||
memo
|
||||
amount
|
||||
cycle
|
||||
maxPerCycle
|
||||
maxAmountPerMonth
|
||||
code
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -7,7 +7,8 @@
|
||||
"contributionLinks": "Beitragslinks",
|
||||
"create": "Anlegen",
|
||||
"cycle": "Zyklus",
|
||||
"deleteNow": "Automatische Creations wirklich löschen?",
|
||||
"deleted": "Automatische Schöpfung gelöscht!",
|
||||
"deleteNow": "Automatische Creations '{name}' wirklich löschen?",
|
||||
"maximumAmount": "maximaler Betrag",
|
||||
"maxPerCycle": "Wiederholungen",
|
||||
"memo": "Nachricht",
|
||||
@ -74,10 +75,20 @@
|
||||
"submit": "Senden"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"help": {
|
||||
"help": "Hilfe",
|
||||
"transactionlist": {
|
||||
"confirmed": "Wann wurde es von einem Moderator / Admin bestätigt.",
|
||||
"periods": "Für welchen Zeitraum wurde vom Mitglied eingereicht.",
|
||||
"state": "[PENDING = eingereicht, DELETED = gelöscht, IN_PROGRESS = im Dialog mit Moderator, DENIED = abgelehnt, CONFIRMED = bestätigt]",
|
||||
"submitted": "Wann wurde es vom Mitglied eingereicht"
|
||||
}
|
||||
},
|
||||
"hide_details": "Details verbergen",
|
||||
"lastname": "Nachname",
|
||||
"math": {
|
||||
"colon": ":",
|
||||
"equals": "=",
|
||||
"exclaim": "!",
|
||||
"pipe": "|",
|
||||
"plus": "+"
|
||||
@ -133,10 +144,11 @@
|
||||
},
|
||||
"transactionlist": {
|
||||
"amount": "Betrag",
|
||||
"balanceDate": "Schöpfungsdatum",
|
||||
"community": "Gemeinschaft",
|
||||
"date": "Datum",
|
||||
"confirmed": "Bestätigt",
|
||||
"memo": "Nachricht",
|
||||
"period": "Zeitraum",
|
||||
"state": "Status",
|
||||
"submitted": "Eingereicht",
|
||||
"title": "Alle geschöpften Transaktionen für den Nutzer"
|
||||
},
|
||||
"undelete_user": "Nutzer wiederherstellen",
|
||||
|
||||
@ -7,7 +7,8 @@
|
||||
"contributionLinks": "Contribution Links",
|
||||
"create": "Create",
|
||||
"cycle": "Cycle",
|
||||
"deleteNow": "Do you really delete automatic creations?",
|
||||
"deleted": "Automatic creation deleted!",
|
||||
"deleteNow": "Do you really delete automatic creations '{name}'?",
|
||||
"maximumAmount": "Maximum amount",
|
||||
"maxPerCycle": "Repetition",
|
||||
"memo": "Memo",
|
||||
@ -74,10 +75,20 @@
|
||||
"submit": "Send"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"help": {
|
||||
"help": "Help",
|
||||
"transactionlist": {
|
||||
"confirmed": "When was it confirmed by a moderator / admin.",
|
||||
"periods": "For what period was it submitted by the member.",
|
||||
"state": "[PENDING = submitted, DELETED = deleted, IN_PROGRESS = in dialogue with moderator, DENIED = denied, CONFIRMED = confirmed]",
|
||||
"submitted": "When was it submitted by the member"
|
||||
}
|
||||
},
|
||||
"hide_details": "Hide details",
|
||||
"lastname": "Lastname",
|
||||
"math": {
|
||||
"colon": ":",
|
||||
"equals": "=",
|
||||
"exclaim": "!",
|
||||
"pipe": "|",
|
||||
"plus": "+"
|
||||
@ -133,10 +144,11 @@
|
||||
},
|
||||
"transactionlist": {
|
||||
"amount": "Amount",
|
||||
"balanceDate": "Creation date",
|
||||
"community": "Community",
|
||||
"date": "Date",
|
||||
"confirmed": "Confirmed",
|
||||
"memo": "Message",
|
||||
"period": "Period",
|
||||
"state": "State",
|
||||
"submitted": "Submitted",
|
||||
"title": "All creation-transactions for the user"
|
||||
},
|
||||
"undelete_user": "Undelete User",
|
||||
|
||||
@ -28,7 +28,11 @@
|
||||
</b-link>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
<contribution-link :items="items" :count="count" />
|
||||
<contribution-link
|
||||
:items="items"
|
||||
:count="count"
|
||||
@get-contribution-links="getContributionLinks"
|
||||
/>
|
||||
<community-statistic class="mt-5" v-model="statistics" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -5,41 +5,66 @@
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/access.log",
|
||||
"pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"apollo":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/apollo.log",
|
||||
"pattern": "%d{ISO8601} %p %c %m",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"backend":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/backend.log",
|
||||
"pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"klicktipp":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/klicktipp.log",
|
||||
"pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"errorFile":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/errors.log",
|
||||
"pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
},
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"errors":
|
||||
{
|
||||
@ -52,7 +77,7 @@
|
||||
"type": "stdout",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m"
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
}
|
||||
},
|
||||
"apolloOut":
|
||||
@ -60,7 +85,7 @@
|
||||
"type": "stdout",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c %m"
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@ import { User } from '@entity/User'
|
||||
|
||||
@ObjectType()
|
||||
export class Contribution {
|
||||
constructor(contribution: dbContribution, user: User) {
|
||||
constructor(contribution: dbContribution, user?: User | null) {
|
||||
this.id = contribution.id
|
||||
this.firstName = user ? user.firstName : null
|
||||
this.lastName = user ? user.lastName : null
|
||||
|
||||
@ -15,6 +15,7 @@ import { AdminCreateContributions } from '@model/AdminCreateContributions'
|
||||
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
|
||||
import { ContributionLink } from '@model/ContributionLink'
|
||||
import { ContributionLinkList } from '@model/ContributionLinkList'
|
||||
import { Contribution } from '@model/Contribution'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { UserRepository } from '@repository/User'
|
||||
import AdminCreateContributionArgs from '@arg/AdminCreateContributionArgs'
|
||||
@ -23,12 +24,10 @@ import SearchUsersArgs from '@arg/SearchUsersArgs'
|
||||
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
|
||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||
import { Transaction } from '@model/Transaction'
|
||||
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
|
||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||
import { TransactionRepository } from '@repository/Transaction'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
import { Contribution } from '@entity/Contribution'
|
||||
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||
import { User as dbUser } from '@entity/User'
|
||||
import { User } from '@model/User'
|
||||
@ -40,7 +39,6 @@ import { Decay } from '@model/Decay'
|
||||
import Paginated from '@arg/Paginated'
|
||||
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
|
||||
import { Order } from '@enum/Order'
|
||||
import { communityUser } from '@/util/communityUser'
|
||||
import { findUserByEmail, activationLink, printTimeDuration } from './UserResolver'
|
||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
||||
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
|
||||
@ -66,6 +64,7 @@ import { ContributionMessageType } from '@enum/MessageType'
|
||||
import { ContributionMessage } from '@model/ContributionMessage'
|
||||
import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail'
|
||||
import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail'
|
||||
import { ContributionListResult } from '../model/Contribution'
|
||||
|
||||
// const EMAIL_OPT_IN_REGISTER = 1
|
||||
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
|
||||
@ -248,7 +247,7 @@ export class AdminResolver {
|
||||
const creationDateObj = new Date(creationDate)
|
||||
logger.trace('creationDateObj:', creationDateObj)
|
||||
validateContribution(creations, amount, creationDateObj)
|
||||
const contribution = Contribution.create()
|
||||
const contribution = DbContribution.create()
|
||||
contribution.userId = emailContact.userId
|
||||
contribution.amount = amount
|
||||
contribution.createdAt = new Date()
|
||||
@ -259,7 +258,7 @@ export class AdminResolver {
|
||||
contribution.contributionStatus = ContributionStatus.PENDING
|
||||
|
||||
logger.trace('contribution to save', contribution)
|
||||
await Contribution.save(contribution)
|
||||
await DbContribution.save(contribution)
|
||||
return getUserCreation(emailContact.userId)
|
||||
}
|
||||
|
||||
@ -317,7 +316,7 @@ export class AdminResolver {
|
||||
|
||||
const moderator = getUser(context)
|
||||
|
||||
const contributionToUpdate = await Contribution.findOne({
|
||||
const contributionToUpdate = await DbContribution.findOne({
|
||||
where: { id, confirmedAt: IsNull() },
|
||||
})
|
||||
|
||||
@ -350,7 +349,7 @@ export class AdminResolver {
|
||||
contributionToUpdate.moderatorId = moderator.id
|
||||
contributionToUpdate.contributionStatus = ContributionStatus.PENDING
|
||||
|
||||
await Contribution.save(contributionToUpdate)
|
||||
await DbContribution.save(contributionToUpdate)
|
||||
const result = new AdminUpdateContribution()
|
||||
result.amount = amount
|
||||
result.memo = contributionToUpdate.memo
|
||||
@ -367,7 +366,7 @@ export class AdminResolver {
|
||||
const contributions = await getConnection()
|
||||
.createQueryBuilder()
|
||||
.select('c')
|
||||
.from(Contribution, 'c')
|
||||
.from(DbContribution, 'c')
|
||||
.leftJoinAndSelect('c.messages', 'm')
|
||||
.where({ confirmedAt: IsNull() })
|
||||
.getMany()
|
||||
@ -399,7 +398,7 @@ export class AdminResolver {
|
||||
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
|
||||
@Mutation(() => Boolean)
|
||||
async adminDeleteContribution(@Arg('id', () => Int) id: number): Promise<boolean> {
|
||||
const contribution = await Contribution.findOne(id)
|
||||
const contribution = await DbContribution.findOne(id)
|
||||
if (!contribution) {
|
||||
logger.error(`Contribution not found for given id: ${id}`)
|
||||
throw new Error('Contribution not found for given id.')
|
||||
@ -416,7 +415,7 @@ export class AdminResolver {
|
||||
@Arg('id', () => Int) id: number,
|
||||
@Ctx() context: Context,
|
||||
): Promise<boolean> {
|
||||
const contribution = await Contribution.findOne(id)
|
||||
const contribution = await DbContribution.findOne(id)
|
||||
if (!contribution) {
|
||||
logger.error(`Contribution not found for given id: ${id}`)
|
||||
throw new Error('Contribution not found to given id.')
|
||||
@ -481,7 +480,7 @@ export class AdminResolver {
|
||||
contribution.confirmedBy = moderatorUser.id
|
||||
contribution.transactionId = transaction.id
|
||||
contribution.contributionStatus = ContributionStatus.CONFIRMED
|
||||
await queryRunner.manager.update(Contribution, { id: contribution.id }, contribution)
|
||||
await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
logger.info('creation commited successfuly.')
|
||||
@ -506,24 +505,29 @@ export class AdminResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.CREATION_TRANSACTION_LIST])
|
||||
@Query(() => [Transaction])
|
||||
@Query(() => ContributionListResult)
|
||||
async creationTransactionList(
|
||||
@Args()
|
||||
{ currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
|
||||
@Arg('userId', () => Int) userId: number,
|
||||
): Promise<Transaction[]> {
|
||||
): Promise<ContributionListResult> {
|
||||
const offset = (currentPage - 1) * pageSize
|
||||
const transactionRepository = getCustomRepository(TransactionRepository)
|
||||
const [userTransactions] = await transactionRepository.findByUserPaged(
|
||||
userId,
|
||||
pageSize,
|
||||
offset,
|
||||
order,
|
||||
true,
|
||||
)
|
||||
const [contributionResult, count] = await getConnection()
|
||||
.createQueryBuilder()
|
||||
.select('c')
|
||||
.from(DbContribution, 'c')
|
||||
.leftJoinAndSelect('c.user', 'u')
|
||||
.where(`user_id = ${userId}`)
|
||||
.limit(pageSize)
|
||||
.offset(offset)
|
||||
.orderBy('c.created_at', order)
|
||||
.getManyAndCount()
|
||||
|
||||
const user = await dbUser.findOneOrFail({ id: userId })
|
||||
return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
|
||||
return new ContributionListResult(
|
||||
count,
|
||||
contributionResult.map((contribution) => new Contribution(contribution, contribution.user)),
|
||||
)
|
||||
// return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
||||
@ -744,7 +748,7 @@ export class AdminResolver {
|
||||
await queryRunner.startTransaction('REPEATABLE READ')
|
||||
const contributionMessage = DbContributionMessage.create()
|
||||
try {
|
||||
const contribution = await Contribution.findOne({
|
||||
const contribution = await DbContribution.findOne({
|
||||
where: { id: contributionId },
|
||||
relations: ['user'],
|
||||
})
|
||||
@ -773,7 +777,7 @@ export class AdminResolver {
|
||||
contribution.contributionStatus === ContributionStatus.PENDING
|
||||
) {
|
||||
contribution.contributionStatus = ContributionStatus.IN_PROGRESS
|
||||
await queryRunner.manager.update(Contribution, { id: contributionId }, contribution)
|
||||
await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
|
||||
}
|
||||
|
||||
await sendAddedContributionMessageEmail({
|
||||
|
||||
@ -351,7 +351,7 @@ export class UserResolver {
|
||||
}
|
||||
// add pubKey in logger-context for layout-pattern X{user} to print it in each logging message
|
||||
logger.addContext('user', dbUser.id)
|
||||
logger.debug('login credentials valid...')
|
||||
logger.debug('validation of login credentials successful...')
|
||||
|
||||
const user = new User(dbUser, await getUserCreation(dbUser.id))
|
||||
logger.debug(`user= ${JSON.stringify(user, null, 2)}`)
|
||||
@ -396,6 +396,7 @@ export class UserResolver {
|
||||
@Args()
|
||||
{ email, firstName, lastName, language, publisherId, redeemCode = null }: CreateUserArgs,
|
||||
): Promise<User> {
|
||||
logger.addContext('user', 'unknown')
|
||||
logger.info(
|
||||
`createUser(email=${email}, firstName=${firstName}, lastName=${lastName}, language=${language}, publisherId=${publisherId}, redeemCode =${redeemCode})`,
|
||||
)
|
||||
@ -548,6 +549,7 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
logger.addContext('user', dbUser.id)
|
||||
} catch (e) {
|
||||
logger.error(`error during create user with ${e}`)
|
||||
await queryRunner.rollbackTransaction()
|
||||
@ -571,6 +573,7 @@ export class UserResolver {
|
||||
@Authorized([RIGHTS.SEND_RESET_PASSWORD_EMAIL])
|
||||
@Mutation(() => Boolean)
|
||||
async forgotPassword(@Arg('email') email: string): Promise<boolean> {
|
||||
logger.addContext('user', 'unknown')
|
||||
logger.info(`forgotPassword(${email})...`)
|
||||
email = email.trim().toLowerCase()
|
||||
const user = await findUserByEmail(email).catch(() => {
|
||||
|
||||
@ -35,6 +35,7 @@ const createServer = async (
|
||||
context: any = serverContext,
|
||||
logger: Logger = apolloLogger,
|
||||
): Promise<ServerDef> => {
|
||||
logger.addContext('user', 'unknown')
|
||||
logger.debug('createServer...')
|
||||
|
||||
// open mysql connection
|
||||
|
||||
@ -14,8 +14,8 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
|
||||
\`type\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
\`user_id\` int(10) unsigned NOT NULL,
|
||||
\`email\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL UNIQUE,
|
||||
\`email_verification_code\` bigint(20) unsigned NOT NULL UNIQUE,
|
||||
\`email_opt_in_type_id\` int NOT NULL,
|
||||
\`email_verification_code\` bigint(20) unsigned DEFAULT NULL UNIQUE,
|
||||
\`email_opt_in_type_id\` int DEFAULT NULL,
|
||||
\`email_resend_count\` int DEFAULT '0',
|
||||
\`email_checked\` tinyint(4) NOT NULL DEFAULT 0,
|
||||
\`phone\` varchar(255) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
|
||||
@ -43,45 +43,11 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis
|
||||
await queryFn(`
|
||||
INSERT INTO user_contacts
|
||||
(type, user_id, email, email_verification_code, email_opt_in_type_id, email_resend_count, email_checked, created_at, updated_at, deleted_at)
|
||||
SELECT
|
||||
'EMAIL',
|
||||
u.id as user_id,
|
||||
u.email,
|
||||
e.verification_code as email_verification_code,
|
||||
e.email_opt_in_type_id,
|
||||
e.resend_count as email_resend_count,
|
||||
u.email_checked,
|
||||
e.created as created_at,
|
||||
e.updated as updated_at,
|
||||
u.deletedAt as deleted_at\
|
||||
FROM
|
||||
users as u,
|
||||
login_email_opt_in as e
|
||||
WHERE
|
||||
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);`)
|
||||
*/
|
||||
SELECT 'EMAIL', users.id, users.email, optin.verification_code, optin.email_opt_in_type_id, optin.resend_count, users.email_checked, users.created, null, users.deletedAt
|
||||
FROM users LEFT JOIN
|
||||
(SELECT le.id, le.user_id, le.verification_code, le.email_opt_in_type_id, le.resend_count, 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) AS optin ON users.id = optin.user_id AND row_num = 1;`)
|
||||
|
||||
// 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`)
|
||||
@ -113,11 +79,13 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom
|
||||
)
|
||||
|
||||
// reconstruct the previous email back from contacts to users table
|
||||
const contacts = await queryFn(`SELECT c.id, c.email, c.user_id FROM user_contacts as c`)
|
||||
const contacts = await queryFn(
|
||||
`SELECT c.id, c.email, c.user_id, c.email_checked FROM user_contacts as c`,
|
||||
)
|
||||
for (const id in contacts) {
|
||||
const contact = contacts[id]
|
||||
await queryFn(
|
||||
`UPDATE users SET email = "${contact.email}" WHERE id = "${contact.user_id}" and email_id = "${contact.id}"`,
|
||||
`UPDATE users SET email = "${contact.email}", email_checked="${contact.email_checked}" WHERE id = "${contact.user_id}" and email_id = "${contact.id}"`,
|
||||
)
|
||||
}
|
||||
await queryFn('ALTER TABLE users MODIFY COLUMN email varchar(255) NOT NULL UNIQUE;')
|
||||
|
||||
@ -1,24 +1,73 @@
|
||||
# Gradido End-to-End Testing with [Cypress](https://www.cypress.io/) (CI-ready via Docker)
|
||||
|
||||
A setup to show-case Cypress as an end-to-end testing tool for Gradido running in a Docker container.
|
||||
The tests are organized in feature files written in Gherkin syntax.
|
||||
|
||||
|
||||
## Features under test
|
||||
|
||||
So far these features are initially tested
|
||||
- [User authentication](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Authentication.feature)
|
||||
- [User profile - change password](https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/UserProfile.ChangePassword.feature)
|
||||
- [User registration]((https://github.com/gradido/gradido/blob/master/e2e-tests/cypress/tests/cypress/e2e/User.Registration.feature)) (WIP)
|
||||
|
||||
A sample setup to show-case Cypress as an end-to-end testing tool for Gradido running in a Docker container.
|
||||
Here we have a simple UI-based happy path login test running against the DEV system.
|
||||
|
||||
## Precondition
|
||||
Since dependencies and configurations for Github Actions integration is not set up yet, please run in root directory
|
||||
|
||||
Before running the tests, change to the repo's root directory (gradido).
|
||||
|
||||
### Boot up the system under test
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
to boot up the DEV system, before running the test.
|
||||
### Seed the database
|
||||
|
||||
The database has to be seeded upfront to every test run.
|
||||
|
||||
```bash
|
||||
# change to the backend directory
|
||||
cd /path/to/gradido/gradido/backend
|
||||
|
||||
# install all dependencies
|
||||
yarn
|
||||
|
||||
# seed the database (everytime before running the tests)
|
||||
yarn seed
|
||||
```
|
||||
|
||||
## Execute the test
|
||||
|
||||
This setup will be integrated in the Gradido Github Actions to automatically support the CI/CD process.
|
||||
For now the test setup can only be used locally in two modes.
|
||||
|
||||
### Run Cypress directly from the code
|
||||
|
||||
```bash
|
||||
# change to the tests directory
|
||||
cd /path/to/gradido/e2e-tests/cypress/tests
|
||||
|
||||
# install all dependencies
|
||||
yarn install
|
||||
|
||||
# a) run the tests on command line
|
||||
yarn cypress run
|
||||
|
||||
# b) open the Cypress GUI to run the tests in interactive mode
|
||||
yarn cypress open
|
||||
```
|
||||
|
||||
|
||||
### Run Cyprss from a separate Docker container
|
||||
|
||||
```bash
|
||||
# change to the cypress directory
|
||||
cd /path/to/gradido/e2e-tests/cypress/
|
||||
|
||||
# build a Docker image from the Dockerfile
|
||||
docker build -t gradido_e2e-tests-cypress .
|
||||
|
||||
# run the Docker container and execute the given tests
|
||||
docker run -it --network=host gradido_e2e-tests-cypress yarn run cypress-e2e-tests
|
||||
# run the Docker image and execute the given tests
|
||||
docker run -it --network=host gradido_e2e-tests-cypress yarn cypress-e2e
|
||||
```
|
||||
|
||||
@ -32,6 +32,7 @@ export default defineConfig({
|
||||
excludeSpecPattern: "*.js",
|
||||
baseUrl: "http://localhost:3000",
|
||||
chromeWebSecurity: false,
|
||||
defaultCommandTimeout: 10000,
|
||||
supportFile: "cypress/support/index.ts",
|
||||
viewportHeight: 720,
|
||||
viewportWidth: 1280,
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
Feature: User registration
|
||||
As a user
|
||||
I want to register to create an account
|
||||
|
||||
@skip
|
||||
Scenario: Register successfully
|
||||
Given the browser navigates to page "/register"
|
||||
When the user fills name and email "Regina" "Register" "regina@register.com"
|
||||
And the user agrees to the privacy policy
|
||||
And the user submits the registration form
|
||||
Then the user can use a provided activation link
|
||||
And the user can set a password "Aa12345_"
|
||||
And the user can login with the credentials "regina@register.com" "Aa12345_"
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
export class LoginPage {
|
||||
// selectors
|
||||
emailInput = "#Email-input-field";
|
||||
passwordInput = "#Password-input-field";
|
||||
emailInput = "input[type=email]";
|
||||
passwordInput = "input[type=password]";
|
||||
submitBtn = "[type=submit]";
|
||||
emailHint = "#vee_Email";
|
||||
passwordHint = "#vee_Password";
|
||||
|
||||
@ -4,8 +4,8 @@ export class ProfilePage {
|
||||
// selectors
|
||||
openChangePassword = "[data-test=open-password-change-form]";
|
||||
oldPasswordInput = "#password-input-field";
|
||||
newPasswordInput = "#New-password-input-field";
|
||||
newPasswordRepeatInput = "#Repeat-new-password-input-field";
|
||||
newPasswordInput = "#new-password-input-field";
|
||||
newPasswordRepeatInput = "#repeat-new-password-input-field";
|
||||
submitNewPasswordBtn = "[data-test=submit-new-password-btn]";
|
||||
|
||||
goto() {
|
||||
@ -19,12 +19,12 @@ export class ProfilePage {
|
||||
}
|
||||
|
||||
enterNewPassword(password: string) {
|
||||
cy.get(this.newPasswordInput).clear().type(password);
|
||||
cy.get(this.newPasswordInput).find("input").clear().type(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
enterRepeatPassword(password: string) {
|
||||
cy.get(this.newPasswordRepeatInput).clear().type(password);
|
||||
cy.get(this.newPasswordRepeatInput).find("input").clear().type(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
export class RegistrationPage {
|
||||
// selectors
|
||||
firstnameInput = "#registerFirstname";
|
||||
lastnameInput = "#registerLastname";
|
||||
emailInput = "#Email-input-field";
|
||||
checkbox = "#registerCheckbox";
|
||||
submitBtn = "[type=submit]";
|
||||
|
||||
RegistrationThanxHeadline = ".test-message-headline";
|
||||
RegistrationThanxText = ".test-message-subtitle";
|
||||
|
||||
goto() {
|
||||
cy.visit("/register");
|
||||
return this;
|
||||
}
|
||||
|
||||
enterFirstname(firstname: string) {
|
||||
cy.get(this.firstnameInput).clear().type(firstname);
|
||||
return this;
|
||||
}
|
||||
|
||||
enterLastname(lastname: string) {
|
||||
cy.get(this.lastnameInput).clear().type(lastname);
|
||||
return this;
|
||||
}
|
||||
|
||||
enterEmail(email: string) {
|
||||
cy.get(this.emailInput).clear().type(email);
|
||||
return this;
|
||||
}
|
||||
|
||||
checkPrivacyCheckbox() {
|
||||
cy.get(this.checkbox).click({ force: true });
|
||||
}
|
||||
|
||||
submitRegistrationPage() {
|
||||
cy.get(this.submitBtn).should("be.enabled");
|
||||
cy.get(this.submitBtn).click();
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,9 @@
|
||||
|
||||
export class Toasts {
|
||||
// selectors
|
||||
toastSlot = ".b-toaster-slot";
|
||||
toastTypeSuccess = ".b-toast-success";
|
||||
toastTypeError = ".b-toast-danger";
|
||||
toastTitle = ".gdd-toaster-title";
|
||||
toastMessage = ".gdd-toaster-body";
|
||||
}
|
||||
|
||||
@ -25,11 +25,11 @@ Then("the user is logged in with username {string}", (username: string) => {
|
||||
|
||||
Then("the user cannot login", () => {
|
||||
const toast = new Toasts();
|
||||
cy.get(toast.toastTitle).should("contain.text", "Error!");
|
||||
cy.get(toast.toastMessage).should(
|
||||
"contain.text",
|
||||
"No user with this credentials."
|
||||
);
|
||||
cy.get(toast.toastSlot).within(() => {
|
||||
cy.get(toast.toastTypeError);
|
||||
cy.get(toast.toastTitle).should("be.visible");
|
||||
cy.get(toast.toastMessage).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
@ -24,9 +24,9 @@ And("the user submits the password form", () => {
|
||||
|
||||
When("the user is presented a {string} message", (type: string) => {
|
||||
const toast = new Toasts();
|
||||
cy.get(toast.toastTitle).should("contain.text", "Success");
|
||||
cy.get(toast.toastMessage).should(
|
||||
"contain.text",
|
||||
"Your password has been changed."
|
||||
);
|
||||
cy.get(toast.toastSlot).within(() => {
|
||||
cy.get(toast.toastTypeSuccess);
|
||||
cy.get(toast.toastTitle).should("be.visible");
|
||||
cy.get(toast.toastMessage).should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"cypress": "cypress run",
|
||||
"cypress-e2e": "cypress run",
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
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>
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ContributionMessagesList from './ContributionMessagesList.vue'
|
||||
import ContributionMessagesListItem from './ContributionMessagesListItem.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('ContributionMessagesList', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
@ -17,13 +16,14 @@ describe('ContributionMessagesList', () => {
|
||||
},
|
||||
}
|
||||
|
||||
describe('ContributionMessagesList', () => {
|
||||
const propsData = {
|
||||
contributionId: 42,
|
||||
state: 'PENDING0',
|
||||
state: 'PENDING',
|
||||
messages: [
|
||||
{
|
||||
id: 111,
|
||||
message: 'asd asda sda sda',
|
||||
message: 'Lorem ipsum?',
|
||||
createdAt: '2022-08-29T12:23:27.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
@ -32,10 +32,21 @@ describe('ContributionMessagesList', () => {
|
||||
userId: 107,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
{
|
||||
id: 113,
|
||||
message: 'Asda sdad ad asdasd, das Ass das Das. ',
|
||||
createdAt: '2022-08-29T12:25:34.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
userFirstName: 'Bibi',
|
||||
userLastName: 'Bloxberg',
|
||||
userId: 108,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
const ListWrapper = () => {
|
||||
return mount(ContributionMessagesList, {
|
||||
localVue,
|
||||
mocks,
|
||||
@ -45,11 +56,123 @@ describe('ContributionMessagesList', () => {
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper = ListWrapper()
|
||||
})
|
||||
|
||||
it('has a DIV .contribution-messages-list-item', () => {
|
||||
expect(wrapper.find('div.contribution-messages-list-item').exists()).toBe(true)
|
||||
it('has two DIV .contribution-messages-list-item elements', () => {
|
||||
expect(wrapper.findAll('div.contribution-messages-list-item').length).toBe(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ContributionMessagesListItem', () => {
|
||||
describe('if message author has moderator role', () => {
|
||||
const propsData = {
|
||||
message: {
|
||||
id: 113,
|
||||
message: 'Asda sdad ad asdasd, das Ass das Das. ',
|
||||
createdAt: '2022-08-29T12:25:34.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
userFirstName: 'Bibi',
|
||||
userLastName: 'Bloxberg',
|
||||
userId: 108,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
}
|
||||
|
||||
const ItemWrapper = () => {
|
||||
return mount(ContributionMessagesListItem, {
|
||||
localVue,
|
||||
mocks,
|
||||
propsData,
|
||||
})
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = ItemWrapper()
|
||||
})
|
||||
|
||||
it('has a DIV .is-moderator.text-left', () => {
|
||||
expect(wrapper.find('div.is-moderator.text-left').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has the complete user name', () => {
|
||||
expect(wrapper.find('div.is-moderator.text-left > span:nth-child(2)').text()).toBe(
|
||||
'Bibi Bloxberg',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message creation date', () => {
|
||||
expect(wrapper.find('div.is-moderator.text-left > span:nth-child(3)').text()).toMatch(
|
||||
'Mon Aug 29 2022 12:25:34 GMT+0000',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the moderator label', () => {
|
||||
expect(wrapper.find('div.is-moderator.text-left > small:nth-child(4)').text()).toBe(
|
||||
'community.moderator',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message', () => {
|
||||
expect(wrapper.find('div.is-moderator.text-left > div:nth-child(5)').text()).toBe(
|
||||
'Asda sdad ad asdasd, das Ass das Das.',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('if message author does not have moderator role', () => {
|
||||
const propsData = {
|
||||
message: {
|
||||
id: 111,
|
||||
message: 'Lorem ipsum?',
|
||||
createdAt: '2022-08-29T12:23:27.000Z',
|
||||
updatedAt: null,
|
||||
type: 'DIALOG',
|
||||
userFirstName: 'Peter',
|
||||
userLastName: 'Lustig',
|
||||
userId: 107,
|
||||
__typename: 'ContributionMessage',
|
||||
},
|
||||
}
|
||||
|
||||
const ModeratorItemWrapper = () => {
|
||||
return mount(ContributionMessagesListItem, {
|
||||
localVue,
|
||||
mocks,
|
||||
propsData,
|
||||
})
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = ModeratorItemWrapper()
|
||||
})
|
||||
|
||||
it('has a DIV .is-not-moderator.text-right', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-right').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has the complete user name', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-right > span:nth-child(2)').text()).toBe(
|
||||
'Peter Lustig',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message creation date', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-right > span:nth-child(3)').text()).toMatch(
|
||||
'Mon Aug 29 2022 12:23:27 GMT+0000',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the message', () => {
|
||||
expect(wrapper.find('div.is-not-moderator.text-right > div:nth-child(4)').text()).toBe(
|
||||
'Lorem ipsum?',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
<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 v-if="isNotModerator" class="is-not-moderator 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 v-else class="is-moderator text-left">
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import IsModerator from '@/components/ContributionMessages/slots/IsModerator.vue'
|
||||
import IsNotModerator from '@/components/ContributionMessages/slots/IsNotModerator.vue'
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ContributionMessagesListItem',
|
||||
components: {
|
||||
IsModerator,
|
||||
IsNotModerator,
|
||||
},
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@ -36,3 +38,19 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.is-not-moderator {
|
||||
float: right;
|
||||
/* background-color: rgb(261, 204, 221); */
|
||||
width: 75%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
clear: both;
|
||||
}
|
||||
.is-moderator {
|
||||
clear: both;
|
||||
/* background-color: rgb(255, 255, 128); */
|
||||
width: 75%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
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({})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,34 +0,0 @@
|
||||
<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>
|
||||
@ -1,49 +0,0 @@
|
||||
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({})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,36 +0,0 @@
|
||||
<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>
|
||||
@ -42,7 +42,6 @@
|
||||
id="contribution-memo"
|
||||
v-model="form.memo"
|
||||
rows="3"
|
||||
max-rows="6"
|
||||
:placeholder="$t('contribution.yourActivity')"
|
||||
required
|
||||
></b-form-textarea>
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<label class="input-1 mt-4" for="input-1">{{ $t('form.recipient') }}</label>
|
||||
<b-input-group
|
||||
id="input-group-1"
|
||||
class="border border-default"
|
||||
class="border border-default border-radius"
|
||||
description="We'll never share your email with anyone else."
|
||||
size="lg"
|
||||
>
|
||||
@ -81,7 +81,11 @@
|
||||
v-slot="{ errors, valid }"
|
||||
>
|
||||
<label class="input-2" for="input-2">{{ $t('form.amount') }}</label>
|
||||
<b-input-group id="input-group-2" class="border border-default" size="lg">
|
||||
<b-input-group
|
||||
id="input-group-2"
|
||||
class="border border-default border-radius"
|
||||
size="lg"
|
||||
>
|
||||
<b-input-group-prepend class="p-2 d-none d-md-block">
|
||||
<div class="m-1 mt-2">{{ $t('GDD') }}</div>
|
||||
</b-input-group-prepend>
|
||||
@ -115,7 +119,7 @@
|
||||
v-slot="{ errors }"
|
||||
>
|
||||
<label class="input-3" for="input-3">{{ $t('form.message') }}</label>
|
||||
<b-input-group id="input-group-3" class="border border-default">
|
||||
<b-input-group id="input-group-3" class="border border-default border-radius">
|
||||
<b-input-group-prepend class="d-none d-md-block">
|
||||
<b-icon icon="chat-right-text" class="display-4 m-3 mt-4"></b-icon>
|
||||
</b-input-group-prepend>
|
||||
@ -237,4 +241,7 @@ span.errors {
|
||||
#input-3:focus {
|
||||
font-weight: bold;
|
||||
}
|
||||
.border-radius {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
required: true,
|
||||
samePassword: value.password,
|
||||
}"
|
||||
id="repeat-new-password-input-field"
|
||||
:label="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
|
||||
:immediate="true"
|
||||
:name="createId(register ? $t('form.passwordRepeat') : $t('form.password_new_repeat'))"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user