mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch '1923-Allow-Admin-to-CRUD-Contribution-Links' into merge-1979-with-1923
This commit is contained in:
commit
c3ea48c546
@ -39,7 +39,7 @@ module.exports = {
|
||||
{
|
||||
src: './src',
|
||||
extensions: ['.js', '.vue'],
|
||||
ignores: [],
|
||||
ignores: ['contributionLink.options.repetition.null'],
|
||||
enableFix: false,
|
||||
},
|
||||
],
|
||||
|
||||
@ -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)'],
|
||||
|
||||
@ -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",
|
||||
|
||||
BIN
admin/public/img/gdd-coin.png
Normal file
BIN
admin/public/img/gdd-coin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
31
admin/src/components/ContributionLink.spec.js
Normal file
31
admin/src/components/ContributionLink.spec.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ContributionLink from './ContributionLink.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
}
|
||||
|
||||
describe('ContributionLink', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(ContributionLink, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the Div Element ".contribution-link"', () => {
|
||||
expect(wrapper.find('div.contribution-link').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('emits toggle::collapse new Contribution', async () => {
|
||||
wrapper.vm.editContributionLinkData()
|
||||
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
62
admin/src/components/ContributionLink.vue
Normal file
62
admin/src/components/ContributionLink.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<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="items.length > 1"
|
||||
: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,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
visible: false,
|
||||
contributionLinkData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editContributionLinkData(data) {
|
||||
if (!this.visible) this.$root.$emit('bv::toggle::collapse', 'newContribution')
|
||||
this.contributionLinkData = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
64
admin/src/components/ContributionLinkForm.spec.js
Normal file
64
admin/src/components/ContributionLinkForm.spec.js
Normal file
@ -0,0 +1,64 @@
|
||||
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),
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
it('function onReset', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setData({
|
||||
form: {
|
||||
name: 'name',
|
||||
memo: 'memo',
|
||||
amount: 100,
|
||||
startDate: 'startDate',
|
||||
endDate: 'endDate',
|
||||
cycle: 'once',
|
||||
repetition: '1',
|
||||
maxAmount: 100,
|
||||
},
|
||||
})
|
||||
wrapper.vm.onReset()
|
||||
})
|
||||
expect(wrapper.vm.form).toEqual({
|
||||
amount: null,
|
||||
cycle: 'once',
|
||||
endDate: null,
|
||||
maxAmount: null,
|
||||
memo: null,
|
||||
name: null,
|
||||
repetition: null,
|
||||
startDate: null,
|
||||
})
|
||||
})
|
||||
|
||||
it('onSubmit valid form', () => {
|
||||
wrapper.vm.onSubmit()
|
||||
})
|
||||
})
|
||||
})
|
||||
212
admin/src/components/ContributionLinkForm.vue
Normal file
212
admin/src/components/ContributionLinkForm.vue
Normal file
@ -0,0 +1,212 @@
|
||||
<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.startDate')">
|
||||
<b-form-datepicker
|
||||
v-model="form.startDate"
|
||||
size="lg"
|
||||
:min="min"
|
||||
class="mb-4"
|
||||
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.endDate')">
|
||||
<b-form-datepicker
|
||||
v-model="form.endDate"
|
||||
size="lg"
|
||||
:min="form.startDate ? form.startDate : min"
|
||||
class="mb-4"
|
||||
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
|
||||
></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
|
||||
></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
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
|
||||
<b-jumbotron>
|
||||
<b-row class="mb-4">
|
||||
<b-col>
|
||||
<!-- Cycle -->
|
||||
<label for="cycle-repetition">{{ $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>
|
||||
<!-- Repetition -->
|
||||
<label for="cycle-repetition">{{ $t('contributionLink.repetition') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.repetition"
|
||||
:options="repetition"
|
||||
: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.maxAmount"
|
||||
size="lg"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
</b-jumbotron>
|
||||
<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,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
cycle: 'once',
|
||||
repetition: null,
|
||||
maxAmount: null,
|
||||
},
|
||||
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') },
|
||||
],
|
||||
repetition: [
|
||||
{ value: null, text: this.$t('contributionLink.options.repetition.null') },
|
||||
{ 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.startDate === null)
|
||||
return this.toastError(this.$t('contributionLink.noStartDate'))
|
||||
if (this.form.endDate === null) return this.toastError(this.$t('contributionLink.noEndDate'))
|
||||
alert(JSON.stringify(this.form))
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
startDate: this.form.startDate,
|
||||
endDate: this.form.endDate,
|
||||
name: this.form.name,
|
||||
amount: this.form.amount,
|
||||
memo: this.form.memo,
|
||||
cycle: this.form.cycle,
|
||||
repetition: this.form.repetition,
|
||||
maxAmount: this.form.maxAmount,
|
||||
},
|
||||
})
|
||||
.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.startDate = null
|
||||
this.form.endDate = 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.startDate = this.contributionLinkData.startDate
|
||||
this.form.endDate = this.contributionLinkData.endDate
|
||||
this.form.cycle = this.contributionLinkData.cycle
|
||||
this.form.repetition = this.contributionLinkData.repetition
|
||||
this.form.maxAmount = this.contributionLinkData.maxAmount
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
150
admin/src/components/ContributionLinkList.spec.js
Normal file
150
admin/src/components/ContributionLinkList.spec.js
Normal file
@ -0,0 +1,150 @@
|
||||
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',
|
||||
startDate: '2022-04-01',
|
||||
endDate: '2022-08-01',
|
||||
cycle: 'täglich',
|
||||
repetition: '3',
|
||||
maxAmount: 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()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('edit contribution link', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
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('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('show contribution link', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
modalData: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Meditation',
|
||||
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
|
||||
amount: '200',
|
||||
startDate: '2022-04-01',
|
||||
endDate: '2022-08-01',
|
||||
cycle: 'täglich',
|
||||
repetition: '3',
|
||||
maxAmount: 0,
|
||||
link: 'https://localhost/redeem/CL-1a2345678',
|
||||
},
|
||||
],
|
||||
})
|
||||
wrapper.vm.showContributionLink()
|
||||
})
|
||||
|
||||
it('shows modalData', () => {
|
||||
expect(wrapper.emitted('modalData')).toEqual()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
103
admin/src/components/ContributionLinkList.vue
Normal file
103
admin/src/components/ContributionLinkList.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<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" @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 },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
'name',
|
||||
'memo',
|
||||
'amount',
|
||||
'cycle',
|
||||
'repetition',
|
||||
{ key: 'startDate', label: 'Start' },
|
||||
{ key: 'endDate', label: 'Ende' },
|
||||
'delete',
|
||||
'edit',
|
||||
'show',
|
||||
],
|
||||
modalData: null,
|
||||
modalDataLink: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteContributionLink() {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm('Automatische Creations wirklich löschen?')
|
||||
.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>
|
||||
30
admin/src/components/FigureQrCode.spec.js
Normal file
30
admin/src/components/FigureQrCode.spec.js
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
53
admin/src/components/FigureQrCode.vue
Normal file
53
admin/src/components/FigureQrCode.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<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,
|
||||
},
|
||||
}
|
||||
},
|
||||
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>
|
||||
@ -69,6 +69,7 @@ const propsData = {
|
||||
{ key: 'edit_creation', label: 'edit' },
|
||||
{ key: 'confirm', label: 'save' },
|
||||
],
|
||||
toggleDetails: false,
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
@ -125,5 +126,9 @@ describe('OpenCreationsTable', () => {
|
||||
expect(wrapper.find('div.component-edit-creation-formular').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
it('funtion updateUserData', () => {
|
||||
wrapper.vm.updateUserData([111, 222, 333], [444, 555, 666])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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) {
|
||||
|
||||
27
admin/src/graphql/createContributionLink.js
Normal file
27
admin/src/graphql/createContributionLink.js
Normal file
@ -0,0 +1,27 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const createContributionLink = gql`
|
||||
mutation (
|
||||
$startDate: String!
|
||||
$endDate: String!
|
||||
$name: String!
|
||||
$amount: Decimal!
|
||||
$memo: String!
|
||||
$cycle: String
|
||||
$repetition: String
|
||||
$maxAmount: Decimal
|
||||
) {
|
||||
createContributionLink(
|
||||
startDate: $startDate
|
||||
endDate: $endDate
|
||||
name: $name
|
||||
amount: $amount
|
||||
memo: $memo
|
||||
cycle: $cycle
|
||||
repetition: $repetition
|
||||
maxAmount: $maxAmount
|
||||
) {
|
||||
link
|
||||
}
|
||||
}
|
||||
`
|
||||
7
admin/src/graphql/deleteContributionLink.js
Normal file
7
admin/src/graphql/deleteContributionLink.js
Normal file
@ -0,0 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const deleteContributionLink = gql`
|
||||
mutation ($id: Int!) {
|
||||
deleteContributionLink(id: $id)
|
||||
}
|
||||
`
|
||||
18
admin/src/graphql/listContributionLinks.js
Normal file
18
admin/src/graphql/listContributionLinks.js
Normal file
@ -0,0 +1,18 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const listContributionLinks = gql`
|
||||
query {
|
||||
listContributionLinks {
|
||||
id
|
||||
startDate
|
||||
endDate
|
||||
name
|
||||
memo
|
||||
amount
|
||||
cycle
|
||||
repetition
|
||||
maxAmount
|
||||
link
|
||||
}
|
||||
}
|
||||
`
|
||||
18
admin/src/graphql/showContributionLink.js
Normal file
18
admin/src/graphql/showContributionLink.js
Normal file
@ -0,0 +1,18 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const showContributionLink = gql`
|
||||
query ($id: Int!) {
|
||||
showContributionLink {
|
||||
id
|
||||
startDate
|
||||
endDate
|
||||
name
|
||||
memo
|
||||
amount
|
||||
cycle
|
||||
repetition
|
||||
maxAmount
|
||||
code
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -1,6 +1,37 @@
|
||||
{
|
||||
"all_emails": "Alle Nutzer",
|
||||
"back": "zurück",
|
||||
"contributionLink": {
|
||||
"amount": "Betrag",
|
||||
"clear": "Löschen",
|
||||
"contributionLinks": "Beitragslinks",
|
||||
"create": "Anlegen",
|
||||
"cycle": "Zyklus",
|
||||
"endDate": "Enddatum",
|
||||
"maximumAmount": "maximaler Betrag",
|
||||
"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"
|
||||
},
|
||||
"repetition": {
|
||||
"null": "Bitte wähle eine Wiederholung"
|
||||
}
|
||||
},
|
||||
"repetition": "Wiederholung",
|
||||
"startDate": "Startdatum"
|
||||
},
|
||||
"creation": "Schöpfung",
|
||||
"creationList": "Schöpfungsliste",
|
||||
"creation_form": {
|
||||
@ -44,7 +75,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.",
|
||||
|
||||
@ -1,6 +1,37 @@
|
||||
{
|
||||
"all_emails": "All users",
|
||||
"back": "back",
|
||||
"contributionLink": {
|
||||
"amount": "Amount",
|
||||
"clear": "Clear",
|
||||
"contributionLinks": "Contribution Links",
|
||||
"create": "Create",
|
||||
"cycle": "Cycle",
|
||||
"endDate": "End-Date",
|
||||
"maximumAmount": "Maximum amount",
|
||||
"memo": "Memo",
|
||||
"name": "Name",
|
||||
"newContributionLink": "New contribution link",
|
||||
"noContributionLinks": "No contribution links have been created.",
|
||||
"noDateSelected": "No date selected",
|
||||
"noEndDate": "No end-date",
|
||||
"noStartDate": "No start-date",
|
||||
"options": {
|
||||
"cycle": {
|
||||
"daily": "daily",
|
||||
"hourly": "hourly",
|
||||
"monthly": "monthly",
|
||||
"once": "unique",
|
||||
"weekly": "weekly",
|
||||
"yearly": "yearly"
|
||||
},
|
||||
"repetition": {
|
||||
"null": "please select a repetition"
|
||||
}
|
||||
},
|
||||
"repetition": "Repetition",
|
||||
"startDate": "Start-date"
|
||||
},
|
||||
"creation": "Creation",
|
||||
"creationList": "Creation list",
|
||||
"creation_form": {
|
||||
@ -44,7 +75,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.",
|
||||
|
||||
@ -28,13 +28,24 @@
|
||||
</b-link>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
<contribution-link :items="items" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getPendingCreations } from '../graphql/getPendingCreations'
|
||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||
import ContributionLink from '../components/ContributionLink.vue'
|
||||
|
||||
export default {
|
||||
name: 'overview',
|
||||
components: {
|
||||
ContributionLink,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getPendingCreations() {
|
||||
this.$apollo
|
||||
@ -46,9 +57,62 @@ export default {
|
||||
this.$store.commit('setOpenCreations', result.data.getPendingCreations.length)
|
||||
})
|
||||
},
|
||||
async getContributionLinks() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listContributionLinks,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.toastSuccess('TODO! change this.items')
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('listContributionLinks has no result, use default data')
|
||||
})
|
||||
|
||||
this.items = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Meditation',
|
||||
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
|
||||
amount: '200',
|
||||
startDate: '2022-04-01',
|
||||
endDate: '2022-08-01',
|
||||
cycle: 'täglich',
|
||||
repetition: '3',
|
||||
maxAmount: 0,
|
||||
link: 'https://localhost/redeem/CL-1a2345678',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Teamarbeit',
|
||||
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt',
|
||||
amount: '300',
|
||||
startDate: '2022-04-01',
|
||||
endDate: '2022-12-01',
|
||||
cycle: 'monatlich',
|
||||
repetition: '2',
|
||||
maxAmount: 0,
|
||||
link: 'https://localhost/redeem/CL-1b2345678',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Documenta Kassel 2022',
|
||||
memo: 'New Account Register by Documenta Kassel, Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt',
|
||||
amount: '400',
|
||||
startDate: '2022-06-18',
|
||||
endDate: '2022-10-01',
|
||||
cycle: 'null',
|
||||
repetition: '1',
|
||||
maxAmount: 0,
|
||||
link: 'https://localhost/redeem/CL-1c2345678',
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getPendingCreations()
|
||||
this.getContributionLinks()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user