mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #1135 from gradido/admin_pending_creation
Admin pending creation
This commit is contained in:
commit
ed7c702d0e
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -441,7 +441,7 @@ jobs:
|
|||||||
report_name: Coverage Admin Interface
|
report_name: Coverage Admin Interface
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 49
|
min_coverage: 50
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -491,7 +491,7 @@ jobs:
|
|||||||
report_name: Coverage Backend
|
report_name: Coverage Backend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./backend/coverage/lcov.info
|
result_path: ./backend/coverage/lcov.info
|
||||||
min_coverage: 37
|
min_coverage: 39
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
"vue-jest": "^3.0.7",
|
"vue-jest": "^3.0.7",
|
||||||
"vue-moment": "^4.1.0",
|
"vue-moment": "^4.1.0",
|
||||||
"vue-router": "^3.5.3",
|
"vue-router": "^3.5.3",
|
||||||
|
"vue-toasted": "^1.1.28",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
"vuex-persistedstate": "^4.1.0"
|
"vuex-persistedstate": "^4.1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
admin/public/img/brand/green.png
Normal file
BIN
admin/public/img/brand/green.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@ -3,6 +3,16 @@ import CreationFormular from './CreationFormular.vue'
|
|||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const apolloMock = jest.fn().mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
verifyLogin: {
|
||||||
|
name: 'success',
|
||||||
|
id: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const stateCommitMock = jest.fn()
|
||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$moment: jest.fn(() => {
|
$moment: jest.fn(() => {
|
||||||
return {
|
return {
|
||||||
@ -14,6 +24,12 @@ const mocks = {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
$apollo: {
|
||||||
|
query: apolloMock,
|
||||||
|
},
|
||||||
|
$store: {
|
||||||
|
commit: stateCommitMock,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const propsData = {
|
const propsData = {
|
||||||
@ -39,6 +55,23 @@ describe('CreationFormular', () => {
|
|||||||
expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy()
|
expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('server sends back moderator data', () => {
|
||||||
|
it('called store commit with mocked data', () => {
|
||||||
|
expect(stateCommitMock).toBeCalledWith('moderator', { name: 'success', id: 0 })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('server throws error for moderator data call', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
apolloMock.mockRejectedValue({ message: 'Ouch!' })
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
it('has called store commit with fake data', () => {
|
||||||
|
expect(stateCommitMock).toBeCalledWith('moderator', { id: 0, name: 'Test Moderator' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('radio buttons to selcet month', () => {
|
describe('radio buttons to selcet month', () => {
|
||||||
it('has three radio buttons', () => {
|
it('has three radio buttons', () => {
|
||||||
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
|
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
? 'Einzelschöpfung für ' + item.firstName + ' ' + item.lastName + ''
|
? 'Einzelschöpfung für ' + item.firstName + ' ' + item.lastName + ''
|
||||||
: 'Mehrfachschöpfung für ' + Object.keys(this.itemsMassCreation).length + ' Mitglieder'
|
: 'Mehrfachschöpfung für ' + Object.keys(this.itemsMassCreation).length + ' Mitglieder'
|
||||||
}}
|
}}
|
||||||
{{ item }}
|
|
||||||
</h3>
|
</h3>
|
||||||
<div v-show="this.type === 'massCreation' && Object.keys(this.itemsMassCreation).length <= 0">
|
<div v-show="this.type === 'massCreation' && Object.keys(this.itemsMassCreation).length <= 0">
|
||||||
Bitte wähle ein oder Mehrere Mitglieder aus für die du Schöpfen möchtest
|
Bitte wähle ein oder Mehrere Mitglieder aus für die du Schöpfen möchtest
|
||||||
@ -24,6 +23,7 @@
|
|||||||
<b-form-radio
|
<b-form-radio
|
||||||
v-model="radioSelected"
|
v-model="radioSelected"
|
||||||
:value="beforeLastMonth"
|
:value="beforeLastMonth"
|
||||||
|
:disabled="creation[0] === 0"
|
||||||
size="lg"
|
size="lg"
|
||||||
@change="updateRadioSelected(beforeLastMonth, 0, creation[0])"
|
@change="updateRadioSelected(beforeLastMonth, 0, creation[0])"
|
||||||
>
|
>
|
||||||
@ -34,6 +34,7 @@
|
|||||||
<b-form-radio
|
<b-form-radio
|
||||||
v-model="radioSelected"
|
v-model="radioSelected"
|
||||||
:value="lastMonth"
|
:value="lastMonth"
|
||||||
|
:disabled="creation[1] === 0"
|
||||||
size="lg"
|
size="lg"
|
||||||
@change="updateRadioSelected(lastMonth, 1, creation[1])"
|
@change="updateRadioSelected(lastMonth, 1, creation[1])"
|
||||||
>
|
>
|
||||||
@ -44,6 +45,7 @@
|
|||||||
<b-form-radio
|
<b-form-radio
|
||||||
v-model="radioSelected"
|
v-model="radioSelected"
|
||||||
:value="currentMonth"
|
:value="currentMonth"
|
||||||
|
:disabled="creation[2] === 0"
|
||||||
size="lg"
|
size="lg"
|
||||||
@change="updateRadioSelected(currentMonth, 2, creation[2])"
|
@change="updateRadioSelected(currentMonth, 2, creation[2])"
|
||||||
>
|
>
|
||||||
@ -52,12 +54,10 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
|
|
||||||
<b-row class="m-4">
|
<b-row class="m-4" v-show="createdIndex">
|
||||||
<label>Betrag Auswählen</label>
|
<label>Betrag Auswählen</label>
|
||||||
<b-input-group>
|
<div>
|
||||||
<template #append>
|
<b-input-group prepend="GDD" append=".00">
|
||||||
<b-input-group-text><strong class="text-danger">GDD</strong></b-input-group-text>
|
|
||||||
</template>
|
|
||||||
<b-form-input
|
<b-form-input
|
||||||
type="number"
|
type="number"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
@ -66,16 +66,17 @@
|
|||||||
></b-form-input>
|
></b-form-input>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
|
|
||||||
<b-input
|
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
|
||||||
id="range-2"
|
<b-form-input
|
||||||
class="mt-2"
|
|
||||||
v-model="value"
|
|
||||||
type="range"
|
type="range"
|
||||||
|
v-model="value"
|
||||||
:min="rangeMin"
|
:min="rangeMin"
|
||||||
:max="rangeMax"
|
:max="rangeMax"
|
||||||
step="10"
|
step="10"
|
||||||
@load="checkFormForUpdate('range')"
|
@load="checkFormForUpdate('range')"
|
||||||
></b-input>
|
></b-form-input>
|
||||||
|
</b-input-group>
|
||||||
|
</div>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row class="m-4">
|
<b-row class="m-4">
|
||||||
<label>Text eintragen</label>
|
<label>Text eintragen</label>
|
||||||
@ -125,6 +126,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { verifyLogin } from '../graphql/verifyLogin'
|
||||||
|
import { createPendingCreation } from '../graphql/createPendingCreation'
|
||||||
export default {
|
export default {
|
||||||
name: 'CreationFormular',
|
name: 'CreationFormular',
|
||||||
props: {
|
props: {
|
||||||
@ -163,23 +166,25 @@ export default {
|
|||||||
rangeMax: 1000,
|
rangeMax: 1000,
|
||||||
currentMonth: {
|
currentMonth: {
|
||||||
short: this.$moment().format('MMMM'),
|
short: this.$moment().format('MMMM'),
|
||||||
long: this.$moment().format('DD/MM/YYYY'),
|
long: this.$moment().format('YYYY-MM-DD'),
|
||||||
},
|
},
|
||||||
lastMonth: {
|
lastMonth: {
|
||||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||||
long: this.$moment().subtract(1, 'month').format('DD/MM/YYYY'),
|
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
|
||||||
},
|
},
|
||||||
beforeLastMonth: {
|
beforeLastMonth: {
|
||||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||||
long: this.$moment().subtract(2, 'month').format('DD/MM/YYYY'),
|
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
|
||||||
},
|
},
|
||||||
submitObj: null,
|
submitObj: null,
|
||||||
isdisabled: true,
|
isdisabled: true,
|
||||||
|
createdIndex: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// Auswählen eines Zeitraumes
|
// Auswählen eines Zeitraumes
|
||||||
updateRadioSelected(name, index, openCreation) {
|
updateRadioSelected(name, index, openCreation) {
|
||||||
|
this.createdIndex = index
|
||||||
// Wenn Mehrfachschöpfung
|
// Wenn Mehrfachschöpfung
|
||||||
if (this.type === 'massCreation') {
|
if (this.type === 'massCreation') {
|
||||||
// An Creation.vue emitten und radioSelectedMass aktualisieren
|
// An Creation.vue emitten und radioSelectedMass aktualisieren
|
||||||
@ -230,10 +235,11 @@ export default {
|
|||||||
this.submitObj = [
|
this.submitObj = [
|
||||||
{
|
{
|
||||||
item: this.itemsMassCreation,
|
item: this.itemsMassCreation,
|
||||||
datum: this.radioSelected,
|
email: this.item.email,
|
||||||
|
creationDate: this.radioSelected.long,
|
||||||
amount: this.value,
|
amount: this.value,
|
||||||
text: this.text,
|
memo: this.text,
|
||||||
moderator: this.$store.state.moderator,
|
moderator: this.$store.state.moderator.id,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
alert('MehrfachSCHÖPFUNG ABSENDEN FÜR >> ' + i + ' Mitglieder')
|
alert('MehrfachSCHÖPFUNG ABSENDEN FÜR >> ' + i + ' Mitglieder')
|
||||||
@ -246,18 +252,13 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.type === 'singleCreation') {
|
if (this.type === 'singleCreation') {
|
||||||
// hinweis das eine einzelne schöpfung ausgeführt wird an (Vorname)
|
this.submitObj = {
|
||||||
alert('SUBMIT CREATION => ' + this.type + ' >> für ' + this.item.firstName + '')
|
email: this.item.email,
|
||||||
// erstellen eines Arrays (submitObj) mit allen Daten
|
creationDate: this.radioSelected.long,
|
||||||
this.submitObj = [
|
amount: Number(this.value),
|
||||||
{
|
memo: this.text,
|
||||||
item: this.item,
|
moderator: Number(this.$store.state.moderator.id),
|
||||||
datum: this.radioSelected.long,
|
}
|
||||||
amount: this.value,
|
|
||||||
text: this.text,
|
|
||||||
moderator: this.$store.state.moderator,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
if (this.pagetype === 'PageCreationConfirm') {
|
if (this.pagetype === 'PageCreationConfirm') {
|
||||||
// hinweis das eine ein einzelne Schöpfung abgesendet wird an (email)
|
// hinweis das eine ein einzelne Schöpfung abgesendet wird an (email)
|
||||||
@ -269,22 +270,48 @@ export default {
|
|||||||
text: this.text,
|
text: this.text,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// hinweis das eine ein einzelne Schöpfung abgesendet wird an (email)
|
this.$apollo
|
||||||
alert('EINZEL SCHÖPFUNG ABSENDEN FÜR >> ' + this.item.firstName + '')
|
.mutate({
|
||||||
// $store - offene Schöpfungen hochzählen
|
mutation: createPendingCreation,
|
||||||
|
variables: this.submitObj,
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.$emit('update-user-data', this.item, result.data.createPendingCreation)
|
||||||
|
this.$toasted.success(
|
||||||
|
`Offene schöpfung (${this.value} GDD) für ${this.item.email} wurde gespeichert, liegen zur bestätigung bereit`,
|
||||||
|
)
|
||||||
this.$store.commit('openCreationsPlus', 1)
|
this.$store.commit('openCreationsPlus', 1)
|
||||||
}
|
this.submitObj = null
|
||||||
}
|
this.createdIndex = null
|
||||||
|
// das creation Formular reseten
|
||||||
// das absendeergebniss im string ansehen
|
this.$refs.creationForm.reset()
|
||||||
alert(JSON.stringify(this.submitObj))
|
// Den geschöpften Wert auf o setzen
|
||||||
// das submitObj zurücksetzen
|
this.value = 0
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$toasted.error(error.message)
|
||||||
this.submitObj = null
|
this.submitObj = null
|
||||||
// das creation Formular reseten
|
// das creation Formular reseten
|
||||||
this.$refs.creationForm.reset()
|
this.$refs.creationForm.reset()
|
||||||
// Den geschöpften Wert auf o setzen
|
// Den geschöpften Wert auf o setzen
|
||||||
this.value = 0
|
this.value = 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
searchModeratorData() {
|
||||||
|
this.$apollo
|
||||||
|
.query({ query: verifyLogin })
|
||||||
|
.then((result) => {
|
||||||
|
this.$store.commit('moderator', result.data.verifyLogin)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$store.commit('moderator', { id: 0, name: 'Test Moderator' })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.searchModeratorData()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -3,11 +3,19 @@ import NavBar from './NavBar.vue'
|
|||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const storeDispatchMock = jest.fn()
|
||||||
|
const routerPushMock = jest.fn()
|
||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$store: {
|
$store: {
|
||||||
state: {
|
state: {
|
||||||
openCreations: 1,
|
openCreations: 1,
|
||||||
|
token: 'valid-token',
|
||||||
},
|
},
|
||||||
|
dispatch: storeDispatchMock,
|
||||||
|
},
|
||||||
|
$router: {
|
||||||
|
push: routerPushMock,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,4 +35,34 @@ describe('NavBar', () => {
|
|||||||
expect(wrapper.find('.component-nabvar').exists()).toBeTruthy()
|
expect(wrapper.find('.component-nabvar').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('wallet', () => {
|
||||||
|
const assignLocationSpy = jest.fn()
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.findAll('a').at(5).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it.skip('changes widnow location to wallet', () => {
|
||||||
|
expect(assignLocationSpy).toBeCalledWith('valid-token')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dispatches logout to store', () => {
|
||||||
|
expect(storeDispatchMock).toBeCalledWith('logout')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('logout', () => {
|
||||||
|
// const assignLocationSpy = jest.fn()
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.findAll('a').at(6).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('redirects to /logout', () => {
|
||||||
|
expect(routerPushMock).toBeCalledWith('/logout')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dispatches logout to store', () => {
|
||||||
|
expect(storeDispatchMock).toBeCalledWith('logout')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="component-nabvar">
|
<div class="component-nabvar">
|
||||||
<b-navbar toggleable="sm" type="dark" variant="success">
|
<b-navbar toggleable="sm" type="dark" variant="success">
|
||||||
<b-navbar-brand to="/">Adminbereich</b-navbar-brand>
|
<b-navbar-brand to="/">
|
||||||
|
<img src="img/brand/green.png" class="navbar-brand-img" alt="..." />
|
||||||
|
</b-navbar-brand>
|
||||||
|
|
||||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||||
|
|
||||||
<b-collapse id="nav-collapse" is-nav>
|
<b-collapse id="nav-collapse" is-nav>
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
|
<b-nav-item to="/">Übersicht |</b-nav-item>
|
||||||
<b-nav-item to="/user">Usersuche |</b-nav-item>
|
<b-nav-item to="/user">Usersuche |</b-nav-item>
|
||||||
<b-nav-item to="/creation">Mehrfachschöpfung</b-nav-item>
|
<b-nav-item to="/creation">Mehrfachschöpfung</b-nav-item>
|
||||||
<b-nav-item
|
<b-nav-item
|
||||||
@ -31,21 +34,6 @@ export default {
|
|||||||
name: 'navbar',
|
name: 'navbar',
|
||||||
methods: {
|
methods: {
|
||||||
logout() {
|
logout() {
|
||||||
// TODO
|
|
||||||
// this.$emit('logout')
|
|
||||||
/* this.$apollo
|
|
||||||
.query({
|
|
||||||
query: logout,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$store.dispatch('logout')
|
|
||||||
this.$router.push('/logout')
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.$store.dispatch('logout')
|
|
||||||
if (this.$router.currentRoute.path !== '/logout') this.$router.push('/logout')
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
this.$store.dispatch('logout')
|
this.$store.dispatch('logout')
|
||||||
this.$router.push('/logout')
|
this.$router.push('/logout')
|
||||||
},
|
},
|
||||||
@ -56,3 +44,8 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.navbar-brand-img {
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -14,11 +14,11 @@
|
|||||||
{{ overlayText.text2 }}
|
{{ overlayText.text2 }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<b-button size="lg" variant="danger" class="m-3" @click="overlayCancel">
|
<b-button size="md" variant="danger" class="m-3" @click="overlayCancel">
|
||||||
{{ overlayText.button_cancel }}
|
{{ overlayText.button_cancel }}
|
||||||
</b-button>
|
</b-button>
|
||||||
<b-button
|
<b-button
|
||||||
size="lg"
|
size="md"
|
||||||
variant="success"
|
variant="success"
|
||||||
class="m-3 text-right"
|
class="m-3 text-right"
|
||||||
@click="overlayOK(overlayBookmarkType, overlayItem)"
|
@click="overlayOK(overlayBookmarkType, overlayItem)"
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<template #cell(edit_creation)="row">
|
<template #cell(edit_creation)="row">
|
||||||
<b-button
|
<b-button
|
||||||
variant="info"
|
variant="info"
|
||||||
size="lg"
|
size="md"
|
||||||
@click="editCreationUserTable(row, row.item)"
|
@click="editCreationUserTable(row, row.item)"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #cell(show_details)="row">
|
<template #cell(show_details)="row">
|
||||||
<b-button variant="info" size="lg" @click="row.toggleDetails" class="mr-2">
|
<b-button variant="info" size="md" @click="row.toggleDetails" class="mr-2">
|
||||||
<b-icon v-if="row.detailsShowing" icon="eye-slash-fill" aria-label="Help"></b-icon>
|
<b-icon v-if="row.detailsShowing" icon="eye-slash-fill" aria-label="Help"></b-icon>
|
||||||
<b-icon v-else icon="eye-fill" aria-label="Help"></b-icon>
|
<b-icon v-else icon="eye-fill" aria-label="Help"></b-icon>
|
||||||
</b-button>
|
</b-button>
|
||||||
@ -68,6 +68,7 @@
|
|||||||
:item="row.item"
|
:item="row.item"
|
||||||
:creationUserData="creationData"
|
:creationUserData="creationData"
|
||||||
@update-creation-data="updateCreationData"
|
@update-creation-data="updateCreationData"
|
||||||
|
@update-user-data="updateUserData"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<b-button size="sm" @click="row.toggleDetails">
|
<b-button size="sm" @click="row.toggleDetails">
|
||||||
@ -93,7 +94,7 @@
|
|||||||
<b-button
|
<b-button
|
||||||
variant="danger"
|
variant="danger"
|
||||||
v-show="type === 'UserListMassCreation' || type === 'PageCreationConfirm'"
|
v-show="type === 'UserListMassCreation' || type === 'PageCreationConfirm'"
|
||||||
size="lg"
|
size="md"
|
||||||
@click="overlayShow('remove', row.item)"
|
@click="overlayShow('remove', row.item)"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
@ -105,7 +106,7 @@
|
|||||||
<b-button
|
<b-button
|
||||||
variant="success"
|
variant="success"
|
||||||
v-show="type === 'PageCreationConfirm'"
|
v-show="type === 'PageCreationConfirm'"
|
||||||
size="lg"
|
size="md"
|
||||||
@click="overlayShow('confirm', row.item)"
|
@click="overlayShow('confirm', row.item)"
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
>
|
>
|
||||||
@ -232,6 +233,9 @@ export default {
|
|||||||
...data,
|
...data,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateUserData(rowItem, newCreation) {
|
||||||
|
rowItem.creation = newCreation
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
19
admin/src/graphql/createPendingCreation.js
Normal file
19
admin/src/graphql/createPendingCreation.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const createPendingCreation = gql`
|
||||||
|
mutation (
|
||||||
|
$email: String!
|
||||||
|
$amount: Int!
|
||||||
|
$memo: String!
|
||||||
|
$creationDate: String!
|
||||||
|
$moderator: Int!
|
||||||
|
) {
|
||||||
|
createPendingCreation(
|
||||||
|
email: $email
|
||||||
|
amount: $amount
|
||||||
|
memo: $memo
|
||||||
|
creationDate: $creationDate
|
||||||
|
moderator: $moderator
|
||||||
|
)
|
||||||
|
}
|
||||||
|
`
|
||||||
11
admin/src/graphql/verifyLogin.js
Normal file
11
admin/src/graphql/verifyLogin.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const verifyLogin = gql`
|
||||||
|
query {
|
||||||
|
verifyLogin {
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -21,6 +21,7 @@ import 'bootstrap/dist/css/bootstrap.css'
|
|||||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||||
|
|
||||||
import moment from 'vue-moment'
|
import moment from 'vue-moment'
|
||||||
|
import Toasted from 'vue-toasted'
|
||||||
|
|
||||||
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
|
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
|
||||||
|
|
||||||
@ -62,6 +63,18 @@ Vue.use(moment)
|
|||||||
|
|
||||||
Vue.use(VueApollo)
|
Vue.use(VueApollo)
|
||||||
|
|
||||||
|
Vue.use(Toasted, {
|
||||||
|
position: 'top-center',
|
||||||
|
duration: 5000,
|
||||||
|
fullWidth: true,
|
||||||
|
action: {
|
||||||
|
text: 'x',
|
||||||
|
onClick: (e, toastObject) => {
|
||||||
|
toastObject.goAway(0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
addNavigationGuards(router, store)
|
addNavigationGuards(router, store)
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|||||||
@ -3,12 +3,14 @@ import './main'
|
|||||||
import CONFIG from './config'
|
import CONFIG from './config'
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import VueApollo from 'vue-apollo'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import VueI18n from 'vue-i18n'
|
import VueI18n from 'vue-i18n'
|
||||||
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||||
import moment from 'vue-moment'
|
import moment from 'vue-moment'
|
||||||
|
|
||||||
jest.mock('vue')
|
jest.mock('vue')
|
||||||
|
jest.mock('vue-apollo')
|
||||||
jest.mock('vuex')
|
jest.mock('vuex')
|
||||||
jest.mock('vue-i18n')
|
jest.mock('vue-i18n')
|
||||||
jest.mock('vue-moment')
|
jest.mock('vue-moment')
|
||||||
@ -55,6 +57,10 @@ describe('main', () => {
|
|||||||
expect(InMemoryCache).toBeCalled()
|
expect(InMemoryCache).toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('calls the VueApollo', () => {
|
||||||
|
expect(VueApollo).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('calls Vue', () => {
|
it('calls Vue', () => {
|
||||||
expect(Vue).toBeCalled()
|
expect(Vue).toBeCalled()
|
||||||
})
|
})
|
||||||
@ -63,16 +69,16 @@ describe('main', () => {
|
|||||||
expect(VueI18n).toBeCalled()
|
expect(VueI18n).toBeCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('calls BootstrapVue', () => {
|
it('calls BootstrapVue', () => {
|
||||||
expect(BootstrapVue).toBeCalled()
|
expect(Vue.use).toBeCalledWith(BootstrapVue)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('calls IconsPlugin', () => {
|
it('calls IconsPlugin', () => {
|
||||||
expect(IconsPlugin).toBeCalled()
|
expect(Vue.use).toBeCalledWith(IconsPlugin)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('calls Moment', () => {
|
it('calls Moment', () => {
|
||||||
expect(moment).toBeCalled()
|
expect(Vue.use).toBeCalledWith(moment)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('creates a store', () => {
|
it.skip('creates a store', () => {
|
||||||
|
|||||||
@ -32,7 +32,13 @@ export default {
|
|||||||
{ key: 'email', label: 'Email' },
|
{ key: 'email', label: 'Email' },
|
||||||
{ key: 'firstName', label: 'Firstname' },
|
{ key: 'firstName', label: 'Firstname' },
|
||||||
{ key: 'lastName', label: 'Lastname' },
|
{ key: 'lastName', label: 'Lastname' },
|
||||||
{ key: 'creation', label: 'Creation' },
|
{
|
||||||
|
key: 'creation',
|
||||||
|
label: 'Creation',
|
||||||
|
formatter: (value, key, item) => {
|
||||||
|
return String(value)
|
||||||
|
},
|
||||||
|
},
|
||||||
{ key: 'show_details', label: 'Details' },
|
{ key: 'show_details', label: 'Details' },
|
||||||
],
|
],
|
||||||
searchResult: [],
|
searchResult: [],
|
||||||
|
|||||||
@ -18,6 +18,9 @@ export const mutations = {
|
|||||||
token: (state, token) => {
|
token: (state, token) => {
|
||||||
state.token = token
|
state.token = token
|
||||||
},
|
},
|
||||||
|
moderator: (state, moderator) => {
|
||||||
|
state.moderator = moderator
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
@ -35,7 +38,7 @@ const store = new Vuex.Store({
|
|||||||
],
|
],
|
||||||
state: {
|
state: {
|
||||||
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
|
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
|
||||||
moderator: 'Dertest Moderator',
|
moderator: { name: 'Dertest Moderator', id: 0 },
|
||||||
openCreations: 0,
|
openCreations: 0,
|
||||||
},
|
},
|
||||||
// Syncronous mutation of the state
|
// Syncronous mutation of the state
|
||||||
|
|||||||
@ -12524,6 +12524,11 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||||
|
|
||||||
|
vue-toasted@^1.1.28:
|
||||||
|
version "1.1.28"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-toasted/-/vue-toasted-1.1.28.tgz#dbabb83acc89f7a9e8765815e491d79f0dc65c26"
|
||||||
|
integrity sha512-UUzr5LX51UbbiROSGZ49GOgSzFxaMHK6L00JV8fir/CYNJCpIIvNZ5YmS4Qc8Y2+Z/4VVYRpeQL2UO0G800Raw==
|
||||||
|
|
||||||
vue@^2.6.11:
|
vue@^2.6.11:
|
||||||
version "2.6.14"
|
version "2.6.14"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"jest": "^27.2.4",
|
"jest": "^27.2.4",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"module-alias": "^2.2.2",
|
"module-alias": "^2.2.2",
|
||||||
|
"moment": "^2.29.1",
|
||||||
"mysql2": "^2.3.0",
|
"mysql2": "^2.3.0",
|
||||||
"nodemailer": "^6.6.5",
|
"nodemailer": "^6.6.5",
|
||||||
"random-bigint": "^0.0.1",
|
"random-bigint": "^0.0.1",
|
||||||
|
|||||||
19
backend/src/graphql/arg/CreatePendingCreationArgs.ts
Normal file
19
backend/src/graphql/arg/CreatePendingCreationArgs.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { ArgsType, Field, Int } from 'type-graphql'
|
||||||
|
|
||||||
|
@ArgsType()
|
||||||
|
export default class CreatePendingCreationArgs {
|
||||||
|
@Field(() => String)
|
||||||
|
email: string
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
amount: number
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
memo: string
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
creationDate: string
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
moderator: number
|
||||||
|
}
|
||||||
0
backend/src/graphql/model/CreatePendingCreation.ts
Normal file
0
backend/src/graphql/model/CreatePendingCreation.ts
Normal file
@ -12,6 +12,7 @@ export class User {
|
|||||||
*/
|
*/
|
||||||
constructor(json?: any) {
|
constructor(json?: any) {
|
||||||
if (json) {
|
if (json) {
|
||||||
|
this.id = json.id
|
||||||
this.email = json.email
|
this.email = json.email
|
||||||
this.firstName = json.first_name
|
this.firstName = json.first_name
|
||||||
this.lastName = json.last_name
|
this.lastName = json.last_name
|
||||||
@ -24,6 +25,9 @@ export class User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Field(() => Number)
|
||||||
|
id: number
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
email: string
|
email: string
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,13 @@
|
|||||||
import { Resolver, Query, Arg, Authorized } from 'type-graphql'
|
import { Resolver, Query, Arg, Args, Authorized, Mutation } from 'type-graphql'
|
||||||
import { getCustomRepository } from 'typeorm'
|
import { getCustomRepository, Raw } from 'typeorm'
|
||||||
import { UserAdmin } from '../model/UserAdmin'
|
import { UserAdmin } from '../model/UserAdmin'
|
||||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
||||||
import { RIGHTS } from '../../auth/RIGHTS'
|
import { RIGHTS } from '../../auth/RIGHTS'
|
||||||
|
import { TransactionCreationRepository } from '../../typeorm/repository/TransactionCreation'
|
||||||
|
import { PendingCreationRepository } from '../../typeorm/repository/PendingCreation'
|
||||||
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
|
import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class AdminResolver {
|
export class AdminResolver {
|
||||||
@ -11,18 +16,161 @@ export class AdminResolver {
|
|||||||
async searchUsers(@Arg('searchText') searchText: string): Promise<UserAdmin[]> {
|
async searchUsers(@Arg('searchText') searchText: string): Promise<UserAdmin[]> {
|
||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||||
const loginUsers = await loginUserRepository.findBySearchCriteria(searchText)
|
const loginUsers = await loginUserRepository.findBySearchCriteria(searchText)
|
||||||
const users = loginUsers.map((loginUser) => {
|
const users = await Promise.all(
|
||||||
|
loginUsers.map(async (loginUser) => {
|
||||||
const user = new UserAdmin()
|
const user = new UserAdmin()
|
||||||
user.firstName = loginUser.firstName
|
user.firstName = loginUser.firstName
|
||||||
user.lastName = loginUser.lastName
|
user.lastName = loginUser.lastName
|
||||||
user.email = loginUser.email
|
user.email = loginUser.email
|
||||||
user.creation = [
|
user.creation = await getUserCreations(loginUser.id)
|
||||||
(Math.floor(Math.random() * 50) + 1) * 20,
|
|
||||||
(Math.floor(Math.random() * 50) + 1) * 20,
|
|
||||||
(Math.floor(Math.random() * 50) + 1) * 20,
|
|
||||||
]
|
|
||||||
return user
|
return user
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mutation(() => [Number])
|
||||||
|
async createPendingCreation(
|
||||||
|
@Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs,
|
||||||
|
): Promise<number[]> {
|
||||||
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
|
const user = await userRepository.findByEmail(email)
|
||||||
|
|
||||||
|
const creations = await getUserCreations(user.id)
|
||||||
|
const creationDateObj = new Date(creationDate)
|
||||||
|
if (isCreationValid(creations, amount, creationDateObj)) {
|
||||||
|
const pendingCreationRepository = getCustomRepository(PendingCreationRepository)
|
||||||
|
const loginPendingTaskAdmin = pendingCreationRepository.create()
|
||||||
|
loginPendingTaskAdmin.userId = user.id
|
||||||
|
loginPendingTaskAdmin.amount = BigInt(amount * 10000)
|
||||||
|
loginPendingTaskAdmin.created = new Date()
|
||||||
|
loginPendingTaskAdmin.date = creationDateObj
|
||||||
|
loginPendingTaskAdmin.memo = memo
|
||||||
|
loginPendingTaskAdmin.moderator = moderator
|
||||||
|
|
||||||
|
pendingCreationRepository.save(loginPendingTaskAdmin)
|
||||||
|
}
|
||||||
|
return await getUserCreations(user.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserCreations(id: number): Promise<number[]> {
|
||||||
|
const dateNextMonth = moment().add(1, 'month').format('YYYY-MM') + '-01'
|
||||||
|
const dateMonth = moment().format('YYYY-MM') + '-01'
|
||||||
|
const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM') + '-01'
|
||||||
|
const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM') + '-01'
|
||||||
|
|
||||||
|
const transactionCreationRepository = getCustomRepository(TransactionCreationRepository)
|
||||||
|
const createdAmountBeforeLastMonth = await transactionCreationRepository
|
||||||
|
.createQueryBuilder('transaction_creations')
|
||||||
|
.select('SUM(transaction_creations.amount)', 'sum')
|
||||||
|
.where('transaction_creations.state_user_id = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateBeforeLastMonth,
|
||||||
|
enddate: dateLastMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
const createdAmountLastMonth = await transactionCreationRepository
|
||||||
|
.createQueryBuilder('transaction_creations')
|
||||||
|
.select('SUM(transaction_creations.amount)', 'sum')
|
||||||
|
.where('transaction_creations.state_user_id = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateLastMonth,
|
||||||
|
enddate: dateMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
const createdAmountMonth = await transactionCreationRepository
|
||||||
|
.createQueryBuilder('transaction_creations')
|
||||||
|
.select('SUM(transaction_creations.amount)', 'sum')
|
||||||
|
.where('transaction_creations.state_user_id = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateMonth,
|
||||||
|
enddate: dateNextMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
const pendingCreationRepository = getCustomRepository(PendingCreationRepository)
|
||||||
|
const pendingAmountMounth = await pendingCreationRepository
|
||||||
|
.createQueryBuilder('login_pending_tasks_admin')
|
||||||
|
.select('SUM(login_pending_tasks_admin.amount)', 'sum')
|
||||||
|
.where('login_pending_tasks_admin.userId = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateMonth,
|
||||||
|
enddate: dateNextMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
const pendingAmountLastMounth = await pendingCreationRepository
|
||||||
|
.createQueryBuilder('login_pending_tasks_admin')
|
||||||
|
.select('SUM(login_pending_tasks_admin.amount)', 'sum')
|
||||||
|
.where('login_pending_tasks_admin.userId = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateLastMonth,
|
||||||
|
enddate: dateMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
const pendingAmountBeforeLastMounth = await pendingCreationRepository
|
||||||
|
.createQueryBuilder('login_pending_tasks_admin')
|
||||||
|
.select('SUM(login_pending_tasks_admin.amount)', 'sum')
|
||||||
|
.where('login_pending_tasks_admin.userId = :id', { id })
|
||||||
|
.andWhere({
|
||||||
|
date: Raw((alias) => `${alias} >= :date and ${alias} < :enddate`, {
|
||||||
|
date: dateBeforeLastMonth,
|
||||||
|
enddate: dateLastMonth,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.getRawOne()
|
||||||
|
|
||||||
|
// COUNT amount from 2 tables
|
||||||
|
const usedCreationBeforeLastMonth =
|
||||||
|
(Number(createdAmountBeforeLastMonth.sum) + Number(pendingAmountBeforeLastMounth.sum)) / 10000
|
||||||
|
const usedCreationLastMonth =
|
||||||
|
(Number(createdAmountLastMonth.sum) + Number(pendingAmountLastMounth.sum)) / 10000
|
||||||
|
const usedCreationMonth =
|
||||||
|
(Number(createdAmountMonth.sum) + Number(pendingAmountMounth.sum)) / 10000
|
||||||
|
return [
|
||||||
|
1000 - usedCreationBeforeLastMonth,
|
||||||
|
1000 - usedCreationLastMonth,
|
||||||
|
1000 - usedCreationMonth,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCreationValid(creations: number[], amount: number, creationDate: Date) {
|
||||||
|
const dateMonth = moment().format('YYYY-MM')
|
||||||
|
const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM')
|
||||||
|
const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM')
|
||||||
|
const creationDateMonth = moment(creationDate).format('YYYY-MM')
|
||||||
|
|
||||||
|
let openCreation
|
||||||
|
switch (creationDateMonth) {
|
||||||
|
case dateMonth:
|
||||||
|
openCreation = creations[2]
|
||||||
|
break
|
||||||
|
case dateLastMonth:
|
||||||
|
openCreation = creations[1]
|
||||||
|
break
|
||||||
|
case dateBeforeLastMonth:
|
||||||
|
openCreation = creations[0]
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new Error('CreationDate is not in last three months')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openCreation < amount) {
|
||||||
|
throw new Error(`Open creation (${openCreation}) is less than amount (${amount})`)
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -207,6 +207,7 @@ export class UserResolver {
|
|||||||
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
const loginUserRepository = getCustomRepository(LoginUserRepository)
|
||||||
const loginUser = await loginUserRepository.findByEmail(userEntity.email)
|
const loginUser = await loginUserRepository.findByEmail(userEntity.email)
|
||||||
const user = new User()
|
const user = new User()
|
||||||
|
user.id = userEntity.id
|
||||||
user.email = userEntity.email
|
user.email = userEntity.email
|
||||||
user.firstName = userEntity.firstName
|
user.firstName = userEntity.firstName
|
||||||
user.lastName = userEntity.lastName
|
user.lastName = userEntity.lastName
|
||||||
@ -276,6 +277,7 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const user = new User()
|
const user = new User()
|
||||||
|
user.id = userEntity.id
|
||||||
user.email = email
|
user.email = email
|
||||||
user.firstName = loginUser.firstName
|
user.firstName = loginUser.firstName
|
||||||
user.lastName = loginUser.lastName
|
user.lastName = loginUser.lastName
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import { elopageWebhook } from '../webhook/elopage'
|
|||||||
// TODO implement
|
// TODO implement
|
||||||
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
||||||
|
|
||||||
const DB_VERSION = '0004-login_server_data'
|
const DB_VERSION = '0005-admin_tables'
|
||||||
|
|
||||||
const createServer = async (context: any = serverContext): Promise<any> => {
|
const createServer = async (context: any = serverContext): Promise<any> => {
|
||||||
// open mysql connection
|
// open mysql connection
|
||||||
|
|||||||
5
backend/src/typeorm/repository/PendingCreation.ts
Normal file
5
backend/src/typeorm/repository/PendingCreation.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
|
import { LoginPendingTasksAdmin } from '@entity/LoginPendingTasksAdmin'
|
||||||
|
|
||||||
|
@EntityRepository(LoginPendingTasksAdmin)
|
||||||
|
export class PendingCreationRepository extends Repository<LoginPendingTasksAdmin> {}
|
||||||
5
backend/src/typeorm/repository/TransactionCreation.ts
Normal file
5
backend/src/typeorm/repository/TransactionCreation.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { EntityRepository, Repository } from 'typeorm'
|
||||||
|
import { TransactionCreation } from '@entity/TransactionCreation'
|
||||||
|
|
||||||
|
@EntityRepository(TransactionCreation)
|
||||||
|
export class TransactionCreationRepository extends Repository<TransactionCreation> {}
|
||||||
@ -4139,6 +4139,11 @@ module-alias@^2.2.2:
|
|||||||
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
|
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
|
||||||
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
|
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
|
||||||
|
|
||||||
|
moment@^2.29.1:
|
||||||
|
version "2.29.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||||
|
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
|||||||
25
database/entity/0005-admin_tables/LoginPendingTasksAdmin.ts
Normal file
25
database/entity/0005-admin_tables/LoginPendingTasksAdmin.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity('login_pending_tasks_admin')
|
||||||
|
export class LoginPendingTasksAdmin extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ unsigned: true, nullable: false })
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
|
||||||
|
created: Date
|
||||||
|
|
||||||
|
@Column({ type: 'datetime', nullable: false })
|
||||||
|
date: Date
|
||||||
|
|
||||||
|
@Column({ length: 256, nullable: true, default: null })
|
||||||
|
memo: string
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', nullable: false })
|
||||||
|
amount: BigInt
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
moderator: number
|
||||||
|
}
|
||||||
1
database/entity/LoginPendingTasksAdmin.ts
Normal file
1
database/entity/LoginPendingTasksAdmin.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { LoginPendingTasksAdmin } from './0005-admin_tables/LoginPendingTasksAdmin'
|
||||||
@ -12,6 +12,7 @@ import { TransactionSendCoin } from './TransactionSendCoin'
|
|||||||
import { User } from './User'
|
import { User } from './User'
|
||||||
import { UserSetting } from './UserSetting'
|
import { UserSetting } from './UserSetting'
|
||||||
import { UserTransaction } from './UserTransaction'
|
import { UserTransaction } from './UserTransaction'
|
||||||
|
import { LoginPendingTasksAdmin } from './LoginPendingTasksAdmin'
|
||||||
|
|
||||||
export const entities = [
|
export const entities = [
|
||||||
Balance,
|
Balance,
|
||||||
@ -28,4 +29,5 @@ export const entities = [
|
|||||||
User,
|
User,
|
||||||
UserSetting,
|
UserSetting,
|
||||||
UserTransaction,
|
UserTransaction,
|
||||||
|
LoginPendingTasksAdmin,
|
||||||
]
|
]
|
||||||
|
|||||||
29
database/migrations/0005-admin_tables.ts
Normal file
29
database/migrations/0005-admin_tables.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* MIGRATION FOR ADMIN INTERFACE
|
||||||
|
*
|
||||||
|
* This migration is special since it takes into account that
|
||||||
|
* the database can be setup already but also may not be.
|
||||||
|
* Therefore you will find all `CREATE TABLE` statements with
|
||||||
|
* a `IF NOT EXISTS`, all `INSERT` with an `IGNORE` and in the
|
||||||
|
* downgrade function all `DROP TABLE` with a `IF EXISTS`.
|
||||||
|
* This ensures compatibility for existing or non-existing
|
||||||
|
* databases.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`
|
||||||
|
CREATE TABLE \`login_pending_tasks_admin\` (
|
||||||
|
\`id\` int UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
\`userId\` int UNSIGNED DEFAULT 0,
|
||||||
|
\`created\` datetime NOT NULL,
|
||||||
|
\`date\` datetime NOT NULL,
|
||||||
|
\`memo\` text DEFAULT NULL,
|
||||||
|
\`amount\` bigint(20) NOT NULL,
|
||||||
|
\`moderator\` int UNSIGNED DEFAULT 0,
|
||||||
|
PRIMARY KEY (\`id\`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`DROP TABLE \`login_pending_tasks_admin\`;`)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user