Merge branch 'master' into redeem-contribution-link

This commit is contained in:
Moriz Wahl 2022-06-16 13:28:35 +02:00 committed by GitHub
commit 9020d29e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1018 additions and 13 deletions

View File

@ -22,7 +22,7 @@ module.exports = {
'^.+\\.(js|jsx)?$': 'babel-jest',
'<rootDir>/node_modules/vee-validate/dist/rules': 'babel-jest',
},
setupFiles: ['<rootDir>/test/testSetup.js'],
setupFiles: ['<rootDir>/test/testSetup.js', 'jest-canvas-mock'],
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
// snapshotSerializers: ['jest-serializer-vue'],
transformIgnorePatterns: ['<rootDir>/node_modules/(?!vee-validate/dist/rules)'],

View File

@ -38,7 +38,9 @@
"graphql": "^15.6.1",
"identity-obj-proxy": "^3.0.0",
"jest": "26.6.3",
"jest-canvas-mock": "^2.3.1",
"portal-vue": "^2.1.7",
"qrcanvas-vue": "2.1.1",
"regenerator-runtime": "^0.13.9",
"stats-webpack-plugin": "^0.7.0",
"vue": "^2.6.11",

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,49 @@
import { mount } from '@vue/test-utils'
import ContributionLink from './ContributionLink.vue'
const localVue = global.localVue
const mocks = {
$t: jest.fn((t) => t),
}
const propsData = {
items: [
{
id: 1,
name: 'Meditation',
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
amount: '200',
validFrom: '2022-04-01',
validTo: '2022-08-01',
cycle: 'täglich',
maxPerCycle: '3',
maxAmountPerMonth: 0,
link: 'https://localhost/redeem/CL-1a2345678',
},
],
count: 1,
}
describe('ContributionLink', () => {
let wrapper
const Wrapper = () => {
return mount(ContributionLink, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the Div Element ".contribution-link"', () => {
expect(wrapper.find('div.contribution-link').exists()).toBe(true)
})
it('emits toggle::collapse new Contribution', async () => {
wrapper.vm.editContributionLinkData()
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
})
})
})

View File

@ -0,0 +1,66 @@
<template>
<div class="contribution-link">
<b-card
border-variant="success"
:header="$t('contributionLink.contributionLinks')"
header-bg-variant="success"
header-text-variant="white"
header-class="text-center"
class="mt-5"
>
<b-button v-b-toggle.newContribution class="my-3 d-flex justify-content-left">
{{ $t('math.plus') }} {{ $t('contributionLink.newContributionLink') }}
</b-button>
<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" />
</b-card>
</b-collapse>
<b-card-text>
<contribution-link-list
v-if="count > 0"
:items="items"
@editContributionLinkData="editContributionLinkData"
/>
<div v-else>{{ $t('contributionLink.noContributionLinks') }}</div>
</b-card-text>
</b-card>
</div>
</template>
<script>
import ContributionLinkForm from './ContributionLinkForm.vue'
import ContributionLinkList from './ContributionLinkList.vue'
export default {
name: 'ContributionLink',
components: {
ContributionLinkForm,
ContributionLinkList,
},
props: {
items: {
type: Array,
required: true,
},
count: {
type: Number,
required: true,
},
},
data: function () {
return {
visible: false,
contributionLinkData: {},
}
},
methods: {
editContributionLinkData(data) {
if (!this.visible) this.$root.$emit('bv::toggle::collapse', 'newContribution')
this.contributionLinkData = data
},
},
}
</script>

View File

@ -0,0 +1,102 @@
import { mount } from '@vue/test-utils'
import ContributionLinkForm from './ContributionLinkForm.vue'
const localVue = global.localVue
global.alert = jest.fn()
const propsData = {
contributionLinkData: {},
}
const mocks = {
$t: jest.fn((t) => t),
}
// const mockAPIcall = jest.fn()
describe('ContributionLinkForm', () => {
let wrapper
const Wrapper = () => {
return mount(ContributionLinkForm, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the Div Element ".contribution-link-form"', () => {
expect(wrapper.find('div.contribution-link-form').exists()).toBe(true)
})
describe('call onReset', () => {
it('form has the set data', () => {
beforeEach(() => {
wrapper.setData({
form: {
name: 'name',
memo: 'memo',
amount: 100,
validFrom: 'validFrom',
validTo: 'validTo',
cycle: 'ONCE',
maxPerCycle: 1,
maxAmountPerMonth: 100,
},
})
wrapper.vm.onReset()
})
expect(wrapper.vm.form).toEqual({
amount: null,
cycle: 'ONCE',
validTo: null,
maxAmountPerMonth: '0',
memo: null,
name: null,
maxPerCycle: 1,
validFrom: null,
})
})
})
describe('call onSubmit', () => {
it('response with the contribution link url', () => {
wrapper.vm.onSubmit()
})
})
// describe('successfull submit', () => {
// beforeEach(async () => {
// mockAPIcall.mockResolvedValue({
// data: {
// createContributionLink: {
// link: 'https://localhost/redeem/CL-1a2345678',
// },
// },
// })
// await wrapper.find('input.test-validFrom').setValue('2022-6-18')
// await wrapper.find('input.test-validTo').setValue('2022-7-18')
// await wrapper.find('input.test-name').setValue('test name')
// await wrapper.find('input.test-memo').setValue('test memo')
// await wrapper.find('input.test-amount').setValue('100')
// await wrapper.find('form').trigger('submit')
// })
// it('calls the API', () => {
// expect(mockAPIcall).toHaveBeenCalledWith(
// expect.objectContaining({
// variables: {
// link: 'https://localhost/redeem/CL-1a2345678',
// },
// }),
// )
// })
// it('displays the new username', () => {
// expect(wrapper.find('div.display-username').text()).toEqual('@username')
// })
// })
})
})

View File

@ -0,0 +1,217 @@
<template>
<div class="contribution-link-form">
<div v-if="updateData" class="text-light bg-info p-3">
{{ updateData }}
</div>
<b-form class="m-5" @submit.prevent="onSubmit" ref="contributionLinkForm">
<!-- Date -->
<b-row>
<b-col>
<b-form-group :label="$t('contributionLink.validFrom')">
<b-form-datepicker
v-model="form.validFrom"
size="lg"
:min="min"
class="mb-4 test-validFrom"
reset-value=""
:label-no-date-selected="$t('contributionLink.noDateSelected')"
required
></b-form-datepicker>
</b-form-group>
</b-col>
<b-col>
<b-form-group :label="$t('contributionLink.validTo')">
<b-form-datepicker
v-model="form.validTo"
size="lg"
:min="form.validFrom ? form.validFrom : min"
class="mb-4 test-validTo"
reset-value=""
:label-no-date-selected="$t('contributionLink.noDateSelected')"
required
></b-form-datepicker>
</b-form-group>
</b-col>
</b-row>
<!-- Name -->
<b-form-group :label="$t('contributionLink.name')">
<b-form-input
v-model="form.name"
size="lg"
type="text"
placeholder="Name"
required
maxlength="100"
class="test-name"
></b-form-input>
</b-form-group>
<!-- Desc -->
<b-form-group :label="$t('contributionLink.memo')">
<b-form-textarea
v-model="form.memo"
size="lg"
:placeholder="$t('contributionLink.memo')"
required
maxlength="255"
class="test-memo"
></b-form-textarea>
</b-form-group>
<!-- Amount -->
<b-form-group :label="$t('contributionLink.amount')">
<b-form-input
v-model="form.amount"
size="lg"
type="number"
placeholder="0"
required
class="test-amount"
></b-form-input>
</b-form-group>
<b-collapse id="collapse-2">
<b-jumbotron>
<b-row class="mb-4">
<b-col>
<!-- Cycle -->
<label for="cycle">{{ $t('contributionLink.cycle') }}</label>
<b-form-select
v-model="form.cycle"
:options="cycle"
:disabled="disabled"
class="mb-3"
size="lg"
></b-form-select>
</b-col>
<b-col>
<!-- maxPerCycle -->
<label for="maxPerCycle">{{ $t('contributionLink.maxPerCycle') }}</label>
<b-form-select
v-model="form.maxPerCycle"
:options="maxPerCycle"
:disabled="disabled"
class="mb-3"
size="lg"
></b-form-select>
</b-col>
</b-row>
<!-- Max amount -->
<b-form-group :label="$t('contributionLink.maximumAmount')">
<b-form-input
v-model="form.maxAmountPerMonth"
size="lg"
:disabled="disabled"
type="number"
placeholder="0"
></b-form-input>
</b-form-group>
</b-jumbotron>
</b-collapse>
<div class="mt-6">
<b-button type="submit" variant="primary">{{ $t('contributionLink.create') }}</b-button>
<b-button type="reset" variant="danger" @click.prevent="onReset">
{{ $t('contributionLink.clear') }}
</b-button>
</div>
</b-form>
</div>
</template>
<script>
import { createContributionLink } from '@/graphql/createContributionLink.js'
export default {
name: 'ContributionLinkForm',
props: {
contributionLinkData: {
type: Object,
default() {
return {}
},
},
},
data() {
return {
form: {
name: null,
memo: null,
amount: null,
validFrom: null,
validTo: null,
cycle: 'ONCE',
maxPerCycle: 1,
maxAmountPerMonth: '0',
},
min: new Date(),
cycle: [
{ value: 'ONCE', text: this.$t('contributionLink.options.cycle.once') },
{ value: 'hourly', text: this.$t('contributionLink.options.cycle.hourly') },
{ value: 'daily', text: this.$t('contributionLink.options.cycle.daily') },
{ value: 'weekly', text: this.$t('contributionLink.options.cycle.weekly') },
{ value: 'monthly', text: this.$t('contributionLink.options.cycle.monthly') },
{ value: 'yearly', text: this.$t('contributionLink.options.cycle.yearly') },
],
maxPerCycle: [
{ value: '1', text: '1 x' },
{ value: '2', text: '2 x' },
{ value: '3', text: '3 x' },
{ value: '4', text: '4 x' },
{ value: '5', text: '5 x' },
],
}
},
methods: {
onSubmit() {
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,
variables: {
validFrom: this.form.validFrom,
validTo: this.form.validTo,
name: this.form.name,
amount: this.form.amount,
memo: this.form.memo,
cycle: this.form.cycle,
maxPerCycle: this.form.maxPerCycle,
maxAmountPerMonth: this.form.maxAmountPerMonth,
},
})
.then((result) => {
this.link = result.data.createContributionLink.link
this.toastSuccess(this.link)
})
.catch((error) => {
this.toastError(error.message)
})
},
onReset() {
this.$refs.contributionLinkForm.reset()
this.form.validFrom = null
this.form.validTo = null
},
},
computed: {
updateData() {
return this.contributionLinkData
},
disabled() {
if (this.form.cycle === 'ONCE') return true
return false
},
},
watch: {
contributionLinkData() {
this.form.name = this.contributionLinkData.name
this.form.memo = this.contributionLinkData.memo
this.form.amount = this.contributionLinkData.amount
this.form.validFrom = this.contributionLinkData.validFrom
this.form.validTo = this.contributionLinkData.validTo
this.form.cycle = this.contributionLinkData.cycle
this.form.maxPerCycle = this.contributionLinkData.maxPerCycle
this.form.maxAmountPerMonth = this.contributionLinkData.maxAmountPerMonth
},
},
}
</script>

View File

@ -0,0 +1,147 @@
import { mount } from '@vue/test-utils'
import ContributionLinkList from './ContributionLinkList.vue'
import { toastSuccessSpy, toastErrorSpy } from '../../test/testSetup'
// import { deleteContributionLink } from '../graphql/deleteContributionLink'
const localVue = global.localVue
const mockAPIcall = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$apollo: {
mutate: mockAPIcall,
},
}
const propsData = {
items: [
{
id: 1,
name: 'Meditation',
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
amount: '200',
validFrom: '2022-04-01',
validTo: '2022-08-01',
cycle: 'täglich',
maxPerCycle: '3',
maxAmountPerMonth: 0,
link: 'https://localhost/redeem/CL-1a2345678',
},
],
}
describe('ContributionLinkList', () => {
let wrapper
const Wrapper = () => {
return mount(ContributionLinkList, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the Div Element ".contribution-link-list"', () => {
expect(wrapper.find('div.contribution-link-list').exists()).toBe(true)
})
it('renders table with contribution link', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'Meditation',
)
})
describe('edit contribution link', () => {
beforeEach(() => {
wrapper.vm.editContributionLink()
})
it('emits editContributionLinkData', async () => {
expect(wrapper.vm.$emit('editContributionLinkData')).toBeTruthy()
})
})
describe('delete contribution link', () => {
let spy
beforeEach(async () => {
jest.clearAllMocks()
wrapper.vm.deleteContributionLink()
})
describe('with success', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve('some value'))
mockAPIcall.mockResolvedValue()
await wrapper.find('.test-delete-link').trigger('click')
})
it('opens the modal ', () => {
expect(spy).toBeCalled()
})
it.skip('calls the API', () => {
// expect(mockAPIcall).toBeCalledWith(
// expect.objectContaining({
// mutation: deleteContributionLink,
// variables: {
// id: 1,
// },
// }),
// )
})
it('toasts a success message', () => {
expect(toastSuccessSpy).toBeCalledWith('TODO: request message deleted ')
})
})
describe('with error', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve('some value'))
mockAPIcall.mockRejectedValue({ message: 'Something went wrong :(' })
await wrapper.find('.test-delete-link').trigger('click')
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Something went wrong :(')
})
})
describe('cancel delete', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(false))
mockAPIcall.mockResolvedValue()
await wrapper.find('.test-delete-link').trigger('click')
})
it('does not call the API', () => {
expect(mockAPIcall).not.toBeCalled()
})
})
})
describe('onClick showButton', () => {
it('modelData contains contribution link', () => {
wrapper.find('button.test-show').trigger('click')
expect(wrapper.vm.modalData).toEqual({
amount: '200',
cycle: 'täglich',
id: 1,
link: 'https://localhost/redeem/CL-1a2345678',
maxAmountPerMonth: 0,
maxPerCycle: '3',
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
name: 'Meditation',
validFrom: '2022-04-01',
validTo: '2022-08-01',
})
})
})
})
})

View File

@ -0,0 +1,106 @@
<template>
<div class="contribution-link-list">
<b-table striped hover :items="items" :fields="fields">
<template #cell(delete)>
<b-button
variant="danger"
size="md"
class="mr-2 test-delete-link"
@click="deleteContributionLink"
>
<b-icon icon="trash" variant="light"></b-icon>
</b-button>
</template>
<template #cell(edit)="data">
<b-button variant="success" size="md" class="mr-2" @click="editContributionLink(data.item)">
<b-icon icon="pencil" variant="light"></b-icon>
</b-button>
</template>
<template #cell(show)="data">
<b-button
variant="info"
size="md"
class="mr-2 test-show"
@click="showContributionLink(data.item)"
>
<b-icon icon="eye" variant="light"></b-icon>
</b-button>
</template>
</b-table>
<b-modal ref="my-modal" ok-only hide-header-close>
<b-card header-tag="header" footer-tag="footer">
<template #header>
<h6 class="mb-0">{{ modalData ? modalData.name : '' }}</h6>
</template>
<b-card-text>
{{ modalData }}
<figure-qr-code :link="modalData ? modalData.link : ''" />
</b-card-text>
<template #footer>
<em>{{ modalData ? modalData.link : '' }}</em>
</template>
</b-card>
</b-modal>
</div>
</template>
<script>
import { deleteContributionLink } from '@/graphql/deleteContributionLink.js'
import FigureQrCode from './FigureQrCode.vue'
export default {
name: 'ContributionLinkList',
components: {
FigureQrCode,
},
props: {
items: { type: Array, required: true },
},
data() {
return {
fields: [
'name',
'memo',
'amount',
{ key: 'cycle', label: this.$t('contributionLink.cycle') },
{ key: 'maxPerCycle', label: this.$t('contributionLink.maxPerCycle') },
{ key: 'validFrom', label: this.$t('contributionLink.validFrom') },
{ key: 'validTo', label: this.$t('contributionLink.validTo') },
'delete',
'edit',
'show',
],
modalData: null,
modalDataLink: null,
}
},
methods: {
deleteContributionLink() {
this.$bvModal.msgBoxConfirm(this.$t('contributionLink.deleteNow')).then(async (value) => {
if (value)
await this.$apollo
.mutate({
mutation: deleteContributionLink,
variables: {
id: this.id,
},
})
.then(() => {
this.toastSuccess('TODO: request message deleted ')
})
.catch((err) => {
this.toastError(err.message)
})
})
},
editContributionLink(row) {
this.$emit('editContributionLinkData', row)
},
showContributionLink(row) {
this.modalData = row
this.$refs['my-modal'].show()
},
},
}
</script>

View File

@ -103,7 +103,7 @@ export default {
data() {
return {
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
value: !this.creationUserData.amount ? 0 : Number(this.creationUserData.amount),
rangeMin: 0,
rangeMax: 1000,
selected: '',
@ -155,7 +155,7 @@ export default {
const month = this.$d(new Date(this.creationUserData.date), 'month')
const index = this.radioOptions.findIndex((obj) => obj.item.short === month)
this.selected = this.radioOptions[index].item
this.rangeMax = this.creation[index] + this.creationUserData.amount
this.rangeMax = Number(this.creation[index]) + Number(this.creationUserData.amount)
}
},
}

View File

@ -0,0 +1,30 @@
import { mount } from '@vue/test-utils'
import FigureQrCode from './FigureQrCode.vue'
const localVue = global.localVue
const propsData = {
link: '',
}
describe('FigureQrCode', () => {
let wrapper
const Wrapper = () => {
return mount(FigureQrCode, { localVue, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('renders the Div Element ".figure-qr-code"', () => {
expect(wrapper.find('div.figure-qr-code').exists()).toBe(true)
})
it('renders the QRCanvas Element ".canvas"', () => {
expect(wrapper.find('.canvas').exists()).toBe(true)
})
})
})

View File

@ -0,0 +1,56 @@
<template>
<div class="figure-qr-code">
<div class="qrbox">
<q-r-canvas :options="options" class="canvas" />
</div>
</div>
</template>
<script>
import { QRCanvas } from 'qrcanvas-vue'
export default {
name: 'FigureQrCode',
components: {
QRCanvas,
},
props: {
link: { type: String, required: true },
},
data() {
return {
options: {
cellSize: 8,
correctLevel: 'H',
data: this.link,
logo: {
image: null,
},
},
}
},
created() {
const image = new Image()
image.src = 'img/gdd-coin.png'
image.onload = () => {
this.options = {
...this.options,
logo: {
image,
},
}
}
},
}
</script>
<style scoped>
.qrbox {
padding: 20px;
background-color: rgb(255, 255, 255);
}
.canvas {
width: 90%;
max-width: 300px;
padding: 5px;
background-color: rgb(255, 255, 255);
}
</style>

View File

@ -69,6 +69,7 @@ const propsData = {
{ key: 'edit_creation', label: 'edit' },
{ key: 'confirm', label: 'save' },
],
toggleDetails: false,
}
const mocks = {
@ -101,7 +102,7 @@ describe('OpenCreationsTable', () => {
})
it('has a DIV element with the class .open-creations-table', () => {
expect(wrapper.find('div.open-creations-table').exists()).toBeTruthy()
expect(wrapper.find('div.open-creations-table').exists()).toBe(true)
})
it('has a table with three rows', () => {
@ -109,7 +110,7 @@ describe('OpenCreationsTable', () => {
})
it('find first button.bi-pencil-square for open EditCreationFormular ', () => {
expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBeTruthy()
expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBe(true)
})
describe('show edit details', () => {
@ -122,7 +123,15 @@ describe('OpenCreationsTable', () => {
})
it.skip('renders the component component-edit-creation-formular', () => {
expect(wrapper.find('div.component-edit-creation-formular').exists()).toBeTruthy()
expect(wrapper.find('div.component-edit-creation-formular').exists()).toBe(true)
})
})
describe('call updateUserData', () => {
it('user creations has updated data', async () => {
wrapper.vm.updateUserData(propsData.items[0], [444, 555, 666])
await wrapper.vm.$nextTick()
expect(wrapper.vm.items[0].creation).toEqual([444, 555, 666])
})
})
})

View File

@ -70,12 +70,23 @@ export default {
required: true,
},
},
data() {
return {
creationUserData: {
amount: null,
date: null,
memo: null,
moderator: null,
},
}
},
methods: {
updateCreationData(data) {
this.creationUserData.amount = data.amount
this.creationUserData.date = data.date
this.creationUserData.memo = data.memo
this.creationUserData.moderator = data.moderator
this.creationUserData = data
// this.creationUserData.amount = data.amount
// this.creationUserData.date = data.date
// this.creationUserData.memo = data.memo
// this.creationUserData.moderator = data.moderator
data.row.toggleDetails()
},
updateUserData(rowItem, newCreation) {

View File

@ -0,0 +1,27 @@
import gql from 'graphql-tag'
export const createContributionLink = gql`
mutation (
$validFrom: String!
$validTo: String!
$name: String!
$amount: Decimal!
$memo: String!
$cycle: String!
$maxPerCycle: Int! = 1
$maxAmountPerMonth: Decimal
) {
createContributionLink(
validFrom: $validFrom
validTo: $validTo
name: $name
amount: $amount
memo: $memo
cycle: $cycle
maxPerCycle: $maxPerCycle
maxAmountPerMonth: $maxAmountPerMonth
) {
link
}
}
`

View File

@ -0,0 +1,7 @@
import gql from 'graphql-tag'
export const deleteContributionLink = gql`
mutation ($id: Int!) {
deleteContributionLink(id: $id)
}
`

View File

@ -0,0 +1,23 @@
import gql from 'graphql-tag'
export const listContributionLinks = gql`
query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
listContributionLinks(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
links {
id
amount
name
memo
code
link
createdAt
validFrom
validTo
maxAmountPerMonth
cycle
maxPerCycle
}
count
}
}
`

View File

@ -0,0 +1,18 @@
import gql from 'graphql-tag'
export const showContributionLink = gql`
query ($id: Int!) {
showContributionLink {
id
validFrom
validTo
name
memo
amount
cycle
maxPerCycle
maxAmountPerMonth
code
}
}
`

View File

@ -1,6 +1,35 @@
{
"all_emails": "Alle Nutzer",
"back": "zurück",
"contributionLink": {
"amount": "Betrag",
"clear": "Löschen",
"contributionLinks": "Beitragslinks",
"create": "Anlegen",
"cycle": "Zyklus",
"deleteNow": "Automatische Creations wirklich löschen?",
"maximumAmount": "maximaler Betrag",
"maxPerCycle": "Wiederholungen",
"memo": "Nachricht",
"name": "Name",
"newContributionLink": "Neuer Beitragslink",
"noContributionLinks": "Es sind keine Beitragslinks angelegt.",
"noDateSelected": "Kein Datum ausgewählt",
"noEndDate": "Kein Enddatum gewählt.",
"noStartDate": "Kein Startdatum gewählt.",
"options": {
"cycle": {
"daily": "täglich",
"hourly": "stündlich",
"monthly": "monatlich",
"once": "einmalig",
"weekly": "wöchentlich",
"yearly": "jährlich"
}
},
"validFrom": "Startdatum",
"validTo": "Enddatum"
},
"creation": "Schöpfung",
"creationList": "Schöpfungsliste",
"creation_form": {
@ -44,7 +73,8 @@
"lastname": "Nachname",
"math": {
"exclaim": "!",
"pipe": "|"
"pipe": "|",
"plus": "+"
},
"moderator": "Moderator",
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",

View File

@ -1,6 +1,35 @@
{
"all_emails": "All users",
"back": "back",
"contributionLink": {
"amount": "Amount",
"clear": "Clear",
"contributionLinks": "Contribution Links",
"create": "Create",
"cycle": "Cycle",
"deleteNow": "Do you really delete automatic creations?",
"maximumAmount": "Maximum amount",
"maxPerCycle": "Repetition",
"memo": "Memo",
"name": "Name",
"newContributionLink": "New contribution link",
"noContributionLinks": "No contribution link has been created.",
"noDateSelected": "No date selected",
"noEndDate": "No end-date",
"noStartDate": "No start-date",
"options": {
"cycle": {
"daily": "daily",
"hourly": "hourly",
"monthly": "monthly",
"once": "once",
"weekly": "weekly",
"yearly": "yearly"
}
},
"validFrom": "Start-date",
"validTo": "End-Date"
},
"creation": "Creation",
"creationList": "Creation list",
"creation_form": {
@ -44,7 +73,8 @@
"lastname": "Lastname",
"math": {
"exclaim": "!",
"pipe": "|"
"pipe": "|",
"plus": "+"
},
"moderator": "Moderator",
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",

View File

@ -28,13 +28,25 @@
</b-link>
</b-card-text>
</b-card>
<contribution-link :items="items" :count="count" />
</div>
</template>
<script>
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
import ContributionLink from '../components/ContributionLink.vue'
import { listUnconfirmedContributions } from '../graphql/listUnconfirmedContributions'
export default {
name: 'overview',
components: {
ContributionLink,
},
data() {
return {
items: [],
count: 0,
}
},
methods: {
async getPendingCreations() {
this.$apollo
@ -46,9 +58,24 @@ export default {
this.$store.commit('setOpenCreations', result.data.listUnconfirmedContributions.length)
})
},
async getContributionLinks() {
this.$apollo
.query({
query: listContributionLinks,
fetchPolicy: 'network-only',
})
.then((result) => {
this.count = result.data.listContributionLinks.count
this.items = result.data.listContributionLinks.links
})
.catch(() => {
this.toastError('listContributionLinks has no result, use default data')
})
},
},
created() {
this.getPendingCreations()
this.getContributionLinks()
},
}
</script>

View File

@ -932,6 +932,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.11.2", "@babel/runtime@^7.16.0":
version "7.18.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.14.0":
version "7.17.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825"
@ -4397,7 +4404,7 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@^1.0.0, color-name@~1.1.4:
color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@ -4845,6 +4852,11 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
cssfontparser@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
integrity sha512-6tun4LoZnj7VN6YeegOVb67KBX/7JJsqvj+pv3ZA7F878/eN33AbGa5b/S/wXxS/tcp8nc40xRUrsPlxIyNUPg==
cssnano-preset-default@^4.0.0, cssnano-preset-default@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff"
@ -7821,6 +7833,14 @@ javascript-stringify@^2.0.1:
resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79"
integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==
jest-canvas-mock@^2.3.1:
version "2.4.0"
resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341"
integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==
dependencies:
cssfontparser "^1.2.1"
moo-color "^1.0.2"
jest-changed-files@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039"
@ -9478,6 +9498,13 @@ mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
moo-color@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74"
integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==
dependencies:
color-name "^1.1.4"
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -10959,6 +10986,27 @@ q@^1.1.2:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qrcanvas-vue@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/qrcanvas-vue/-/qrcanvas-vue-2.1.1.tgz#27b449f99eaf46f324b300215469bfdf8ef77d88"
integrity sha512-86NMjOJ5XJGrrqrD2t+zmZxZKNuW1Is7o88UOiM8qFxDBjuTyfq9VJE9/2rN5XxThsjBuY4bRrQqL9blVwnI9w==
dependencies:
"@babel/runtime" "^7.16.0"
qrcanvas "^3.1.2"
qrcanvas@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/qrcanvas/-/qrcanvas-3.1.2.tgz#81a25e91b2c27e9ace91da95591cbfb100d68702"
integrity sha512-lNcAyCHN0Eno/mJ5eBc7lHV/5ejAJxII0UELthG3bNnlLR+u8hCc7CR+hXBawbYUf96kNIosXfG2cJzx92ZWKg==
dependencies:
"@babel/runtime" "^7.11.2"
qrcode-generator "^1.4.4"
qrcode-generator@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7"
integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"