mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into combine_transaction_tables2
This commit is contained in:
commit
33ce8a0799
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -422,7 +422,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: 94
|
min_coverage: 95
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
30
CHANGELOG.md
30
CHANGELOG.md
@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
|
|
||||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||||
|
|
||||||
|
#### [1.6.6](https://github.com/gradido/gradido/compare/1.6.5...1.6.6)
|
||||||
|
|
||||||
|
- Fix: Upper case email on register breaks account [`#1542`](https://github.com/gradido/gradido/pull/1542)
|
||||||
|
- 1106 first transaction cannot be expanded [`#1432`](https://github.com/gradido/gradido/pull/1432)
|
||||||
|
- added missing bootstrap scss. bootstrap/scss/bootstrap, plus more mis… [`#1540`](https://github.com/gradido/gradido/pull/1540)
|
||||||
|
- feat: Seed Deleted User [`#1533`](https://github.com/gradido/gradido/pull/1533)
|
||||||
|
- fix: No Creations for Deleted Users [`#1534`](https://github.com/gradido/gradido/pull/1534)
|
||||||
|
- fix: Wrong Key Name for Recover User [`#1535`](https://github.com/gradido/gradido/pull/1535)
|
||||||
|
- [Feature] : user deleted and undeleted functions for adminarea [`#1520`](https://github.com/gradido/gradido/pull/1520)
|
||||||
|
- fix: Possible SQL Exception in User Search [`#1530`](https://github.com/gradido/gradido/pull/1530)
|
||||||
|
- Feature: Make lint warnings unwanted [`#1529`](https://github.com/gradido/gradido/pull/1529)
|
||||||
|
- 1459 list data again on confirm creation [`#1467`](https://github.com/gradido/gradido/pull/1467)
|
||||||
|
- fix: Return Empty Array When No Pending Creations Are Present [`#1526`](https://github.com/gradido/gradido/pull/1526)
|
||||||
|
- Fix: Correct path of index.js in production [`#1525`](https://github.com/gradido/gradido/pull/1525)
|
||||||
|
- refactor: Get Open Creations by One Query [`#1524`](https://github.com/gradido/gradido/pull/1524)
|
||||||
|
- Admin: Langsame Benutzer-Suche [`#1472`](https://github.com/gradido/gradido/pull/1472)
|
||||||
|
- fix: Backend Unit Tests Running Again [`#1513`](https://github.com/gradido/gradido/pull/1513)
|
||||||
|
- Refactor: Combine transaction tables [`#1523`](https://github.com/gradido/gradido/pull/1523)
|
||||||
|
- Refactor: User resolver [`#1522`](https://github.com/gradido/gradido/pull/1522)
|
||||||
|
- feature: Soft-Delete for users (backend) [`#1521`](https://github.com/gradido/gradido/pull/1521)
|
||||||
|
- feature: Soft-Delete for users (database only) [`#1516`](https://github.com/gradido/gradido/pull/1516)
|
||||||
|
- refactor: Improve Decay Display [`#1517`](https://github.com/gradido/gradido/pull/1517)
|
||||||
|
- 404 page needs back to login button [`#1515`](https://github.com/gradido/gradido/pull/1515)
|
||||||
|
- feature: show current version in admin footer [`#1514`](https://github.com/gradido/gradido/pull/1514)
|
||||||
|
- fix: Never Sent Email Text [`#1512`](https://github.com/gradido/gradido/pull/1512)
|
||||||
|
- refactor: static decay block [`#1405`](https://github.com/gradido/gradido/pull/1405)
|
||||||
|
- refactor: Use Bootstrap Vue Toast [`#1499`](https://github.com/gradido/gradido/pull/1499)
|
||||||
|
- fix: Catch GDT Server Errors [`#1479`](https://github.com/gradido/gradido/pull/1479)
|
||||||
|
- Fix: Autochangelog - no commits [`#1498`](https://github.com/gradido/gradido/pull/1498)
|
||||||
|
|
||||||
#### [1.6.5](https://github.com/gradido/gradido/compare/1.6.4...1.6.5)
|
#### [1.6.5](https://github.com/gradido/gradido/compare/1.6.4...1.6.5)
|
||||||
|
|
||||||
> 15 February 2022
|
> 15 February 2022
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"description": "Administraion Interface for Gradido",
|
"description": "Administraion Interface for Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"author": "Moriz Wahl",
|
"author": "Moriz Wahl",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
BIN
admin/public/img/elopage_favicon.png
Normal file
BIN
admin/public/img/elopage_favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
262
admin/src/components/DeletedUserFormular.spec.js
Normal file
262
admin/src/components/DeletedUserFormular.spec.js
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import DeletedUserFormular from './DeletedUserFormular.vue'
|
||||||
|
import { deleteUser } from '../graphql/deleteUser'
|
||||||
|
import { unDeleteUser } from '../graphql/unDeleteUser'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const date = new Date()
|
||||||
|
|
||||||
|
const apolloMutateMock = jest.fn().mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
deleteUser: date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const toastedErrorMock = jest.fn()
|
||||||
|
const toastedSuccessMock = jest.fn()
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$apollo: {
|
||||||
|
mutate: apolloMutateMock,
|
||||||
|
},
|
||||||
|
$store: {
|
||||||
|
state: {
|
||||||
|
moderator: {
|
||||||
|
id: 0,
|
||||||
|
name: 'test moderator',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$toasted: {
|
||||||
|
error: toastedErrorMock,
|
||||||
|
success: toastedSuccessMock,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
item: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DeletedUserFormular', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(DeletedUserFormular, { localVue, mocks, propsData })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a DIV element with the class.delete-user-formular', () => {
|
||||||
|
expect(wrapper.find('.deleted-user-formular').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('delete self', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({
|
||||||
|
item: {
|
||||||
|
userId: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows a text that you cannot delete yourself', () => {
|
||||||
|
expect(wrapper.text()).toBe('removeNotSelf')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('delete other user', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({
|
||||||
|
item: {
|
||||||
|
userId: 1,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a checkbox', () => {
|
||||||
|
expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the text "delete_user"', () => {
|
||||||
|
expect(wrapper.text()).toBe('delete_user')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on checkbox', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('input[type="checkbox"]').setChecked()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a confirmation button', () => {
|
||||||
|
expect(wrapper.find('button').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the button text "delete_user"', () => {
|
||||||
|
expect(wrapper.find('button').text()).toBe('delete_user')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('confirm delete with success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('button').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API', () => {
|
||||||
|
expect(apolloMutateMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
mutation: deleteUser,
|
||||||
|
variables: {
|
||||||
|
userId: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts a success message', () => {
|
||||||
|
expect(toastedSuccessMock).toBeCalledWith('user_deleted')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits update deleted At', () => {
|
||||||
|
expect(wrapper.emitted('updateDeletedAt')).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.arrayContaining([
|
||||||
|
{
|
||||||
|
userId: 1,
|
||||||
|
deletedAt: date,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('unchecks the checkbox', () => {
|
||||||
|
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('confirm delete with error', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
|
||||||
|
await wrapper.find('button').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an error message', () => {
|
||||||
|
expect(toastedErrorMock).toBeCalledWith('Oh no!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on checkbox again', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('input[type="checkbox"]').setChecked(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has no confirmation button anymore', () => {
|
||||||
|
expect(wrapper.find('button').exists()).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('recover user', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({
|
||||||
|
item: {
|
||||||
|
userId: 1,
|
||||||
|
deletedAt: date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a checkbox', () => {
|
||||||
|
expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows the text "undelete_user"', () => {
|
||||||
|
expect(wrapper.text()).toBe('undelete_user')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on checkbox', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
apolloMutateMock.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
unDeleteUser: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wrapper.find('input[type="checkbox"]').setChecked()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a confirmation button', () => {
|
||||||
|
expect(wrapper.find('button').exists()).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has the button text "undelete_user"', () => {
|
||||||
|
expect(wrapper.find('button').text()).toBe('undelete_user')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('confirm recover with success', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('button').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls the API', () => {
|
||||||
|
expect(apolloMutateMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
mutation: unDeleteUser,
|
||||||
|
variables: {
|
||||||
|
userId: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts a success message', () => {
|
||||||
|
expect(toastedSuccessMock).toBeCalledWith('user_recovered')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits update deleted At', () => {
|
||||||
|
expect(wrapper.emitted('updateDeletedAt')).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.arrayContaining([
|
||||||
|
{
|
||||||
|
userId: 1,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('unchecks the checkbox', () => {
|
||||||
|
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('confirm recover with error', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
|
||||||
|
await wrapper.find('button').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toasts an error message', () => {
|
||||||
|
expect(toastedErrorMock).toBeCalledWith('Oh no!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('click on checkbox again', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('input[type="checkbox"]').setChecked(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has no confirmation button anymore', () => {
|
||||||
|
expect(wrapper.find('button').exists()).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
86
admin/src/components/DeletedUserFormular.vue
Normal file
86
admin/src/components/DeletedUserFormular.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div class="deleted-user-formular">
|
||||||
|
<div v-if="item.userId === $store.state.moderator.id" class="mt-5 mb-5">
|
||||||
|
{{ $t('removeNotSelf') }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="mt-5">
|
||||||
|
<b-form-checkbox switch size="lg" v-model="checked">
|
||||||
|
<div>{{ item.deletedAt ? $t('undelete_user') : $t('delete_user') }}</div>
|
||||||
|
</b-form-checkbox>
|
||||||
|
|
||||||
|
<div class="mt-3 mb-5">
|
||||||
|
<b-button v-if="checked && item.deletedAt === null" variant="danger" @click="deleteUser">
|
||||||
|
{{ $t('delete_user') }}
|
||||||
|
</b-button>
|
||||||
|
<b-button v-if="checked && item.deletedAt !== null" variant="success" @click="unDeleteUser">
|
||||||
|
{{ $t('undelete_user') }}
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { deleteUser } from '../graphql/deleteUser'
|
||||||
|
import { unDeleteUser } from '../graphql/unDeleteUser'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DeletedUser',
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
checked: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
deleteUser() {
|
||||||
|
this.$apollo
|
||||||
|
.mutate({
|
||||||
|
mutation: deleteUser,
|
||||||
|
variables: {
|
||||||
|
userId: this.item.userId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.$toasted.success(this.$t('user_deleted'))
|
||||||
|
this.$emit('updateDeletedAt', {
|
||||||
|
userId: this.item.userId,
|
||||||
|
deletedAt: result.data.deleteUser,
|
||||||
|
})
|
||||||
|
this.checked = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$toasted.error(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unDeleteUser() {
|
||||||
|
this.$apollo
|
||||||
|
.mutate({
|
||||||
|
mutation: unDeleteUser,
|
||||||
|
variables: {
|
||||||
|
userId: this.item.userId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
this.$toasted.success(this.$t('user_recovered'))
|
||||||
|
this.$emit('updateDeletedAt', {
|
||||||
|
userId: this.item.userId,
|
||||||
|
deletedAt: result.data.unDeleteUser,
|
||||||
|
})
|
||||||
|
this.checked = false
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$toasted.error(error.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.input-group-text {
|
||||||
|
background-color: rgb(255, 252, 205);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<b-card class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
<b-card class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||||
<b-row class="mb-2">
|
|
||||||
<b-col></b-col>
|
|
||||||
</b-row>
|
|
||||||
<slot :name="slotName" />
|
<slot :name="slotName" />
|
||||||
<b-button size="sm" @click="$emit('row-toogle-details', row, index)">
|
<b-button size="sm" @click="$emit('row-toggle-details', row, index)">
|
||||||
<b-icon
|
<b-icon
|
||||||
:icon="type === 'PageCreationConfirm' ? 'x' : 'eye-slash-fill'"
|
:icon="type === 'PageCreationConfirm' ? 'x' : 'eye-slash-fill'"
|
||||||
aria-label="Help"
|
aria-label="Help"
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(edit_creation)="row">
|
<template #cell(edit_creation)="row">
|
||||||
<b-button variant="info" size="md" @click="rowToogleDetails(row, 0)" class="mr-2">
|
<b-button variant="info" size="md" @click="rowToggleDetails(row, 0)" class="mr-2">
|
||||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||||
</b-button>
|
</b-button>
|
||||||
</template>
|
</template>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
type="show-creation"
|
type="show-creation"
|
||||||
slotName="show-creation"
|
slotName="show-creation"
|
||||||
:index="0"
|
:index="0"
|
||||||
@row-toogle-details="rowToogleDetails"
|
@row-toggle-details="rowToggleDetails"
|
||||||
>
|
>
|
||||||
<template #show-creation>
|
<template #show-creation>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
129
admin/src/components/Tables/SearchUserTable.spec.js
Normal file
129
admin/src/components/Tables/SearchUserTable.spec.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import SearchUserTable from './SearchUserTable.vue'
|
||||||
|
|
||||||
|
const date = new Date()
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
const apolloMutateMock = jest.fn().mockResolvedValue({})
|
||||||
|
const apolloQueryMock = jest.fn().mockResolvedValue({})
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
userId: 1,
|
||||||
|
firstName: 'Bibi',
|
||||||
|
lastName: 'Bloxberg',
|
||||||
|
email: 'bibi@bloxberg.de',
|
||||||
|
creation: [200, 400, 600],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 2,
|
||||||
|
firstName: 'Benjamin',
|
||||||
|
lastName: 'Blümchen',
|
||||||
|
email: 'benjamin@bluemchen.de',
|
||||||
|
creation: [1000, 1000, 1000],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 3,
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
creation: [0, 0, 0],
|
||||||
|
emailChecked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: 4,
|
||||||
|
firstName: 'New',
|
||||||
|
lastName: 'User',
|
||||||
|
email: 'new@user.ch',
|
||||||
|
creation: [1000, 1000, 1000],
|
||||||
|
emailChecked: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{ key: 'email', label: 'e_mail' },
|
||||||
|
{ key: 'firstName', label: 'firstname' },
|
||||||
|
{ key: 'lastName', label: 'lastname' },
|
||||||
|
{
|
||||||
|
key: 'creation',
|
||||||
|
label: 'creationLabel',
|
||||||
|
formatter: (value, key, item) => {
|
||||||
|
return value.join(' | ')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ key: 'status', label: 'status' },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((d) => d),
|
||||||
|
$apollo: {
|
||||||
|
mutate: apolloMutateMock,
|
||||||
|
query: apolloQueryMock,
|
||||||
|
},
|
||||||
|
$store: {
|
||||||
|
state: {
|
||||||
|
moderator: {
|
||||||
|
id: 0,
|
||||||
|
name: 'test moderator',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
$toasted: {
|
||||||
|
error: jest.fn(),
|
||||||
|
success: jest.fn(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('SearchUserTable', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(SearchUserTable, { localVue, mocks, propsData })
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has a table with four rows', () => {
|
||||||
|
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('show row details', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.findAll('tbody > tr').at(1).trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('deleted at', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('div.deleted-user-formular').vm.$emit('updateDeletedAt', {
|
||||||
|
userId: 1,
|
||||||
|
deletedAt: date,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits updateDeletedAt', () => {
|
||||||
|
expect(wrapper.emitted('updateDeletedAt')).toEqual([[1, date]])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateUserData', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper
|
||||||
|
.find('div.component-creation-formular')
|
||||||
|
.vm.$emit('update-user-data', propsData.items[1], [250, 500, 750])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates the item', () => {
|
||||||
|
expect(wrapper.vm.items[1].creation).toEqual([250, 500, 750])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,100 +1,97 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="search-user-table">
|
<div class="search-user-table">
|
||||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
<b-table
|
||||||
|
tbody-tr-class="pointer"
|
||||||
|
:items="myItems"
|
||||||
|
:fields="fields"
|
||||||
|
caption-top
|
||||||
|
striped
|
||||||
|
hover
|
||||||
|
stacked="md"
|
||||||
|
select-mode="single"
|
||||||
|
selectableonRowSelected
|
||||||
|
@row-clicked="onRowClicked"
|
||||||
|
>
|
||||||
<template #cell(creation)="data">
|
<template #cell(creation)="data">
|
||||||
<div v-html="data.value"></div>
|
<div v-html="data.value"></div>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(show_details)="row">
|
|
||||||
<b-button
|
<template #cell(status)="row">
|
||||||
variant="info"
|
<div class="text-right">
|
||||||
size="md"
|
<b-avatar v-if="row.item.deletedAt" class="mr-3" variant="light">
|
||||||
v-if="row.item.emailChecked"
|
<b-iconstack font-scale="2">
|
||||||
@click="rowToogleDetails(row, 0)"
|
<b-icon stacked icon="person" variant="info" scale="0.75"></b-icon>
|
||||||
class="mr-2"
|
<b-icon stacked icon="slash-circle" variant="danger"></b-icon>
|
||||||
>
|
</b-iconstack>
|
||||||
<b-icon :icon="row.detailsShowing ? 'eye-slash-fill' : 'eye-fill'"></b-icon>
|
</b-avatar>
|
||||||
</b-button>
|
<span v-if="!row.item.deletedAt">
|
||||||
</template>
|
<b-avatar
|
||||||
<template #cell(confirm_mail)="row">
|
v-if="!row.item.emailChecked"
|
||||||
<b-button
|
icon="envelope"
|
||||||
:variant="row.item.emailChecked ? 'success' : 'danger'"
|
class="align-center mr-3"
|
||||||
size="md"
|
variant="danger"
|
||||||
@click="rowToogleDetails(row, 1)"
|
></b-avatar>
|
||||||
class="mr-2"
|
|
||||||
>
|
<b-avatar
|
||||||
|
v-if="!row.item.hasElopage"
|
||||||
|
variant="danger"
|
||||||
|
class="mr-3"
|
||||||
|
src="img/elopage_favicon.png"
|
||||||
|
></b-avatar>
|
||||||
|
</span>
|
||||||
<b-icon
|
<b-icon
|
||||||
:icon="row.item.emailChecked ? 'envelope-open' : 'envelope'"
|
variant="dark"
|
||||||
aria-label="Help"
|
:icon="row.detailsShowing ? 'caret-up-fill' : 'caret-down'"
|
||||||
|
:title="row.item.enabled ? $t('enabled') : $t('deleted')"
|
||||||
></b-icon>
|
></b-icon>
|
||||||
</b-button>
|
</div>
|
||||||
</template>
|
|
||||||
<template #cell(has_elopage)="row">
|
|
||||||
<b-icon
|
|
||||||
:variant="row.item.hasElopage ? 'success' : 'danger'"
|
|
||||||
:icon="row.item.hasElopage ? 'check-circle' : 'x-circle'"
|
|
||||||
></b-icon>
|
|
||||||
</template>
|
|
||||||
<template #cell(transactions_list)="row">
|
|
||||||
<b-button variant="warning" size="md" @click="rowToogleDetails(row, 2)" class="mr-2">
|
|
||||||
<b-icon icon="list"></b-icon>
|
|
||||||
</b-button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #row-details="row">
|
<template #row-details="row">
|
||||||
<row-details
|
<b-card ref="rowDetails" class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||||
:row="row"
|
<creation-formular
|
||||||
type="singleCreation"
|
v-if="!row.item.deletedAt"
|
||||||
:slotName="slotName"
|
type="singleCreation"
|
||||||
:index="slotIndex"
|
pagetype="singleCreation"
|
||||||
@row-toogle-details="rowToogleDetails"
|
:creation="row.item.creation"
|
||||||
>
|
:item="row.item"
|
||||||
<template #show-creation>
|
:creationUserData="creationUserData"
|
||||||
<div>
|
@update-user-data="updateUserData"
|
||||||
<creation-formular
|
/>
|
||||||
type="singleCreation"
|
<div v-else>{{ $t('userIsDeleted') }}</div>
|
||||||
pagetype="singleCreation"
|
<confirm-register-mail-formular
|
||||||
:creation="row.item.creation"
|
v-if="!row.item.deletedAt"
|
||||||
:item="row.item"
|
:checked="row.item.emailChecked"
|
||||||
:creationUserData="creationUserData"
|
:email="row.item.email"
|
||||||
@update-user-data="updateUserData"
|
:dateLastSend="
|
||||||
/>
|
row.item.emailConfirmationSend
|
||||||
</div>
|
? $d(new Date(row.item.emailConfirmationSend), 'long')
|
||||||
</template>
|
: ''
|
||||||
<template #show-register-mail>
|
"
|
||||||
<confirm-register-mail-formular
|
/>
|
||||||
:checked="row.item.emailChecked"
|
<creation-transaction-list-formular
|
||||||
:email="row.item.email"
|
v-if="!row.item.deletedAt"
|
||||||
:dateLastSend="
|
:userId="row.item.userId"
|
||||||
row.item.emailConfirmationSend
|
/>
|
||||||
? $d(new Date(row.item.emailConfirmationSend), 'long')
|
<deleted-user-formular :item="row.item" @updateDeletedAt="updateDeletedAt" />
|
||||||
: ''
|
</b-card>
|
||||||
"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #show-transaction-list>
|
|
||||||
<creation-transaction-list-formular :userId="row.item.userId" />
|
|
||||||
</template>
|
|
||||||
</row-details>
|
|
||||||
</template>
|
</template>
|
||||||
</b-table-lite>
|
</b-table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import CreationFormular from '../CreationFormular.vue'
|
import CreationFormular from '../CreationFormular.vue'
|
||||||
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue'
|
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue'
|
||||||
import RowDetails from '../RowDetails.vue'
|
|
||||||
import CreationTransactionListFormular from '../CreationTransactionListFormular.vue'
|
import CreationTransactionListFormular from '../CreationTransactionListFormular.vue'
|
||||||
import { toggleRowDetails } from '../../mixins/toggleRowDetails'
|
import DeletedUserFormular from '../DeletedUserFormular.vue'
|
||||||
|
|
||||||
const slotNames = ['show-creation', 'show-register-mail', 'show-transaction-list']
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SearchUserTable',
|
name: 'SearchUserTable',
|
||||||
mixins: [toggleRowDetails],
|
|
||||||
components: {
|
components: {
|
||||||
CreationFormular,
|
CreationFormular,
|
||||||
ConfirmRegisterMailFormular,
|
ConfirmRegisterMailFormular,
|
||||||
CreationTransactionListFormular,
|
CreationTransactionListFormular,
|
||||||
RowDetails,
|
DeletedUserFormular,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
items: {
|
items: {
|
||||||
@ -115,10 +112,29 @@ export default {
|
|||||||
updateUserData(rowItem, newCreation) {
|
updateUserData(rowItem, newCreation) {
|
||||||
rowItem.creation = newCreation
|
rowItem.creation = newCreation
|
||||||
},
|
},
|
||||||
|
updateDeletedAt({ userId, deletedAt }) {
|
||||||
|
this.$emit('updateDeletedAt', userId, deletedAt)
|
||||||
|
},
|
||||||
|
async onRowClicked(item) {
|
||||||
|
const status = this.myItems.find((obj) => obj === item)._showDetails
|
||||||
|
this.myItems.forEach((obj) => {
|
||||||
|
if (obj === item) {
|
||||||
|
obj._showDetails = !status
|
||||||
|
} else {
|
||||||
|
obj._showDetails = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await this.$nextTick()
|
||||||
|
if (!status && this.$refs.rowDetails) {
|
||||||
|
this.$refs.rowDetails.focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
slotName() {
|
myItems() {
|
||||||
return slotNames[this.slotIndex]
|
return this.items.map((item) => {
|
||||||
|
return { ...item, _showDetails: false }
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
7
admin/src/graphql/deleteUser.js
Normal file
7
admin/src/graphql/deleteUser.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const deleteUser = gql`
|
||||||
|
mutation ($userId: Float!) {
|
||||||
|
deleteUser(userId: $userId)
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -1,12 +1,19 @@
|
|||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
export const searchUsers = gql`
|
export const searchUsers = gql`
|
||||||
query ($searchText: String!, $currentPage: Int, $pageSize: Int, $notActivated: Boolean) {
|
query (
|
||||||
|
$searchText: String!
|
||||||
|
$currentPage: Int
|
||||||
|
$pageSize: Int
|
||||||
|
$notActivated: Boolean
|
||||||
|
$isDeleted: Boolean
|
||||||
|
) {
|
||||||
searchUsers(
|
searchUsers(
|
||||||
searchText: $searchText
|
searchText: $searchText
|
||||||
currentPage: $currentPage
|
currentPage: $currentPage
|
||||||
pageSize: $pageSize
|
pageSize: $pageSize
|
||||||
notActivated: $notActivated
|
notActivated: $notActivated
|
||||||
|
isDeleted: $isDeleted
|
||||||
) {
|
) {
|
||||||
userCount
|
userCount
|
||||||
userList {
|
userList {
|
||||||
@ -18,6 +25,7 @@ export const searchUsers = gql`
|
|||||||
emailChecked
|
emailChecked
|
||||||
hasElopage
|
hasElopage
|
||||||
emailConfirmationSend
|
emailConfirmationSend
|
||||||
|
deletedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
admin/src/graphql/unDeleteUser.js
Normal file
7
admin/src/graphql/unDeleteUser.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const unDeleteUser = gql`
|
||||||
|
mutation ($userId: Float!) {
|
||||||
|
unDeleteUser(userId: $userId)
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -23,12 +23,15 @@
|
|||||||
"creation_for_month": "Schöpfung für Monat",
|
"creation_for_month": "Schöpfung für Monat",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
|
"deleted": "gelöscht",
|
||||||
|
"deleted_user": "Alle gelöschten Nutzer",
|
||||||
|
"delete_user": "Nutzer löschen",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"e_mail": "E-Mail",
|
"e_mail": "E-Mail",
|
||||||
"firstname": "Vorname",
|
"firstname": "Vorname",
|
||||||
"gradido_admin_footer": "Gradido Akademie Adminkonsole",
|
"gradido_admin_footer": "Gradido Akademie Adminkonsole",
|
||||||
"hide_details": "Details verbergen von",
|
"hide_details": "Details verbergen",
|
||||||
"lastname": "Nachname",
|
"lastname": "Nachname",
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
||||||
@ -61,8 +64,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
|
"removeNotSelf": "Als Admin / Moderator kannst du dich nicht selber löschen.",
|
||||||
"remove_all": "alle Nutzer entfernen",
|
"remove_all": "alle Nutzer entfernen",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
|
"status": "Status",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"transaction": "Transaktion",
|
"transaction": "Transaktion",
|
||||||
"transactionlist": {
|
"transactionlist": {
|
||||||
@ -73,6 +78,7 @@
|
|||||||
"memo": "Nachricht",
|
"memo": "Nachricht",
|
||||||
"title": "Alle geschöpften Transaktionen für den Nutzer"
|
"title": "Alle geschöpften Transaktionen für den Nutzer"
|
||||||
},
|
},
|
||||||
|
"undelete_user": "Nutzer wiederherstellen",
|
||||||
"unregistered_emails": "Nur unregistrierte Nutzer",
|
"unregistered_emails": "Nur unregistrierte Nutzer",
|
||||||
"unregister_mail": {
|
"unregister_mail": {
|
||||||
"button": "Registrierungs-Email bestätigen, jetzt senden",
|
"button": "Registrierungs-Email bestätigen, jetzt senden",
|
||||||
@ -83,5 +89,8 @@
|
|||||||
"text_false": " Die letzte Email wurde am {date} Uhr an das Mitglied ({email}) gesendet.",
|
"text_false": " Die letzte Email wurde am {date} Uhr an das Mitglied ({email}) gesendet.",
|
||||||
"text_true": " Die Email wurde bestätigt."
|
"text_true": " Die Email wurde bestätigt."
|
||||||
},
|
},
|
||||||
|
"userIsDeleted": "Der Nutzer ist gelöscht. Es können keine GDD mehr geschöpft werden.",
|
||||||
|
"user_deleted": "Nutzer ist gelöscht.",
|
||||||
|
"user_recovered": "Nutzer ist wiederhergestellt.",
|
||||||
"user_search": "Nutzer-Suche"
|
"user_search": "Nutzer-Suche"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,12 +23,15 @@
|
|||||||
"creation_for_month": "Creation for month",
|
"creation_for_month": "Creation for month",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"deleted": "deleted",
|
||||||
|
"deleted_user": "All deleted user",
|
||||||
|
"delete_user": "Delete user",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"e_mail": "E-mail",
|
"e_mail": "E-mail",
|
||||||
"firstname": "Firstname",
|
"firstname": "Firstname",
|
||||||
"gradido_admin_footer": "Gradido Academy Admin Console",
|
"gradido_admin_footer": "Gradido Academy Admin Console",
|
||||||
"hide_details": "Hide details from",
|
"hide_details": "Hide details",
|
||||||
"lastname": "Lastname",
|
"lastname": "Lastname",
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
||||||
@ -61,8 +64,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
|
"removeNotSelf": "As admin / moderator you cannot delete yourself.",
|
||||||
"remove_all": "Remove all users",
|
"remove_all": "Remove all users",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
|
"status": "Status",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"transaction": "Transaction",
|
"transaction": "Transaction",
|
||||||
"transactionlist": {
|
"transactionlist": {
|
||||||
@ -73,6 +78,7 @@
|
|||||||
"memo": "Message",
|
"memo": "Message",
|
||||||
"title": "All creation-transactions for the user"
|
"title": "All creation-transactions for the user"
|
||||||
},
|
},
|
||||||
|
"undelete_user": "Undelete User",
|
||||||
"unregistered_emails": "Only unregistered users",
|
"unregistered_emails": "Only unregistered users",
|
||||||
"unregister_mail": {
|
"unregister_mail": {
|
||||||
"button": "Confirm registration email, send now",
|
"button": "Confirm registration email, send now",
|
||||||
@ -83,5 +89,8 @@
|
|||||||
"text_false": "The last email was sent to the member ({email}) on {date}.",
|
"text_false": "The last email was sent to the member ({email}) on {date}.",
|
||||||
"text_true": "The email was confirmed."
|
"text_true": "The email was confirmed."
|
||||||
},
|
},
|
||||||
|
"userIsDeleted": "The user is deleted. No more GDD can be created.",
|
||||||
|
"user_deleted": "User is deleted.",
|
||||||
|
"user_recovered": "User is recovered.",
|
||||||
"user_search": "User search"
|
"user_search": "User search"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const toggleRowDetails = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
rowToogleDetails(row, index) {
|
rowToggleDetails(row, index) {
|
||||||
if (this.openRow) {
|
if (this.openRow) {
|
||||||
if (this.openRow.index === row.index) {
|
if (this.openRow.index === row.index) {
|
||||||
if (index === this.slotIndex) {
|
if (index === this.slotIndex) {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ describe('toggleRowDetails', () => {
|
|||||||
|
|
||||||
describe('no open row', () => {
|
describe('no open row', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.vm.rowToogleDetails(row, 2)
|
wrapper.vm.rowToggleDetails(row, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls toggleDetails', () => {
|
it('calls toggleDetails', () => {
|
||||||
@ -70,7 +70,7 @@ describe('toggleRowDetails', () => {
|
|||||||
describe('row index is open row index', () => {
|
describe('row index is open row index', () => {
|
||||||
describe('index is slot index', () => {
|
describe('index is slot index', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.vm.rowToogleDetails(row, 0)
|
wrapper.vm.rowToggleDetails(row, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls toggleDetails', () => {
|
it('calls toggleDetails', () => {
|
||||||
@ -84,7 +84,7 @@ describe('toggleRowDetails', () => {
|
|||||||
|
|
||||||
describe('index is not slot index', () => {
|
describe('index is not slot index', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.vm.rowToogleDetails(row, 2)
|
wrapper.vm.rowToggleDetails(row, 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not call toggleDetails', () => {
|
it('does not call toggleDetails', () => {
|
||||||
@ -99,7 +99,7 @@ describe('toggleRowDetails', () => {
|
|||||||
|
|
||||||
describe('row index is not open row index', () => {
|
describe('row index is not open row index', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.vm.rowToogleDetails(
|
wrapper.vm.rowToggleDetails(
|
||||||
{
|
{
|
||||||
toggleDetails: secondToggleDetailsMock,
|
toggleDetails: secondToggleDetailsMock,
|
||||||
index: 2,
|
index: 2,
|
||||||
|
|||||||
@ -83,6 +83,7 @@ describe('UserSearch', () => {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
notActivated: false,
|
notActivated: false,
|
||||||
|
isDeleted: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -90,7 +91,7 @@ describe('UserSearch', () => {
|
|||||||
|
|
||||||
describe('unconfirmed emails', () => {
|
describe('unconfirmed emails', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.find('button.btn-block').trigger('click')
|
await wrapper.find('button.unconfirmedRegisterMails').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls API with filter', () => {
|
it('calls API with filter', () => {
|
||||||
@ -101,6 +102,27 @@ describe('UserSearch', () => {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
notActivated: true,
|
notActivated: true,
|
||||||
|
isDeleted: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('deleted Users', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await wrapper.find('button.deletedUserSearch').trigger('click')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls API with filter', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
searchText: '',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 25,
|
||||||
|
notActivated: false,
|
||||||
|
isDeleted: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -120,6 +142,7 @@ describe('UserSearch', () => {
|
|||||||
currentPage: 2,
|
currentPage: 2,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
notActivated: false,
|
notActivated: false,
|
||||||
|
isDeleted: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -139,6 +162,7 @@ describe('UserSearch', () => {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
notActivated: false,
|
notActivated: false,
|
||||||
|
isDeleted: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -155,6 +179,7 @@ describe('UserSearch', () => {
|
|||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
notActivated: false,
|
notActivated: false,
|
||||||
|
isDeleted: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user-search">
|
<div class="user-search">
|
||||||
<div style="text-align: right">
|
<div style="text-align: right">
|
||||||
<b-button block variant="danger" @click="unconfirmedRegisterMails">
|
<b-button class="unconfirmedRegisterMails" variant="light" @click="unconfirmedRegisterMails">
|
||||||
<b-icon icon="envelope" variant="light"></b-icon>
|
<b-icon icon="envelope" variant="danger"></b-icon>
|
||||||
{{ filterCheckedEmails ? $t('all_emails') : $t('unregistered_emails') }}
|
{{ filterCheckedEmails ? $t('all_emails') : $t('unregistered_emails') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
|
<b-button class="deletedUserSearch" variant="light" @click="deletedUserSearch">
|
||||||
|
<b-icon icon="x-circle" variant="danger"></b-icon>
|
||||||
|
{{ filterDeletedUser ? $t('all_emails') : $t('deleted_user') }}
|
||||||
|
</b-button>
|
||||||
</div>
|
</div>
|
||||||
<label>{{ $t('user_search') }}</label>
|
<label>{{ $t('user_search') }}</label>
|
||||||
<div>
|
<div>
|
||||||
@ -22,7 +26,12 @@
|
|||||||
</b-input-group-append>
|
</b-input-group-append>
|
||||||
</b-input-group>
|
</b-input-group>
|
||||||
</div>
|
</div>
|
||||||
<search-user-table type="PageUserSearch" :items="searchResult" :fields="fields" />
|
<search-user-table
|
||||||
|
type="PageUserSearch"
|
||||||
|
:items="searchResult"
|
||||||
|
:fields="fields"
|
||||||
|
@updateDeletedAt="updateDeletedAt"
|
||||||
|
/>
|
||||||
<b-pagination
|
<b-pagination
|
||||||
pills
|
pills
|
||||||
size="lg"
|
size="lg"
|
||||||
@ -52,6 +61,7 @@ export default {
|
|||||||
massCreation: [],
|
massCreation: [],
|
||||||
criteria: '',
|
criteria: '',
|
||||||
filterCheckedEmails: false,
|
filterCheckedEmails: false,
|
||||||
|
filterDeletedUser: false,
|
||||||
rows: 0,
|
rows: 0,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
perPage: 25,
|
perPage: 25,
|
||||||
@ -63,6 +73,10 @@ export default {
|
|||||||
this.filterCheckedEmails = !this.filterCheckedEmails
|
this.filterCheckedEmails = !this.filterCheckedEmails
|
||||||
this.getUsers()
|
this.getUsers()
|
||||||
},
|
},
|
||||||
|
deletedUserSearch() {
|
||||||
|
this.filterDeletedUser = !this.filterDeletedUser
|
||||||
|
this.getUsers()
|
||||||
|
},
|
||||||
getUsers() {
|
getUsers() {
|
||||||
this.$apollo
|
this.$apollo
|
||||||
.query({
|
.query({
|
||||||
@ -72,6 +86,7 @@ export default {
|
|||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.perPage,
|
pageSize: this.perPage,
|
||||||
notActivated: this.filterCheckedEmails,
|
notActivated: this.filterCheckedEmails,
|
||||||
|
isDeleted: this.filterDeletedUser,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -82,6 +97,9 @@ export default {
|
|||||||
this.$toasted.error(error.message)
|
this.$toasted.error(error.message)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
updateDeletedAt(userId, deletedAt) {
|
||||||
|
this.searchResult.find((obj) => obj.userId === userId).deletedAt = deletedAt
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentPage() {
|
currentPage() {
|
||||||
@ -104,10 +122,11 @@ export default {
|
|||||||
return value.join(' | ')
|
return value.join(' | ')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ key: 'show_details', label: this.$t('details') },
|
// { key: 'show_details', label: this.$t('details') },
|
||||||
{ key: 'confirm_mail', label: this.$t('confirmed') },
|
// { key: 'confirm_mail', label: this.$t('confirmed') },
|
||||||
{ key: 'has_elopage', label: 'elopage' },
|
// { key: 'has_elopage', label: 'elopage' },
|
||||||
{ key: 'transactions_list', label: this.$t('transaction') },
|
// { key: 'transactions_list', label: this.$t('transaction') },
|
||||||
|
{ key: 'status', label: this.$t('status') },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-backend",
|
"name": "gradido-backend",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/backend",
|
"repository": "https://github.com/gradido/gradido/backend",
|
||||||
|
|||||||
@ -26,4 +26,6 @@ export enum RIGHTS {
|
|||||||
DELETE_PENDING_CREATION = 'DELETE_PENDING_CREATION',
|
DELETE_PENDING_CREATION = 'DELETE_PENDING_CREATION',
|
||||||
CONFIRM_PENDING_CREATION = 'CONFIRM_PENDING_CREATION',
|
CONFIRM_PENDING_CREATION = 'CONFIRM_PENDING_CREATION',
|
||||||
SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL',
|
SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL',
|
||||||
|
DELETE_USER = 'DELETE_USER',
|
||||||
|
UNDELETE_USER = 'UNDELETE_USER',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import dotenv from 'dotenv'
|
|||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0024-combine_transaction_tables',
|
DB_VERSION: '0025-emails_to_lower',
|
||||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
|
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,4 +13,7 @@ export default class SearchUsersArgs {
|
|||||||
|
|
||||||
@Field(() => Boolean, { nullable: true })
|
@Field(() => Boolean, { nullable: true })
|
||||||
notActivated?: boolean
|
notActivated?: boolean
|
||||||
|
|
||||||
|
@Field(() => Boolean, { nullable: true })
|
||||||
|
isDeleted?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class Transaction {
|
|||||||
this.balance = 0
|
this.balance = 0
|
||||||
this.totalBalance = 0
|
this.totalBalance = 0
|
||||||
this.memo = ''
|
this.memo = ''
|
||||||
|
this.firstTransaction = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
@ -52,4 +53,7 @@ export class Transaction {
|
|||||||
|
|
||||||
@Field({ nullable: true })
|
@Field({ nullable: true })
|
||||||
decay?: Decay
|
decay?: Decay
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
firstTransaction: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,20 @@
|
|||||||
|
import { User } from '@entity/User'
|
||||||
import { ObjectType, Field, Int } from 'type-graphql'
|
import { ObjectType, Field, Int } from 'type-graphql'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class UserAdmin {
|
export class UserAdmin {
|
||||||
|
constructor(user: User, creation: number[], hasElopage: boolean, emailConfirmationSend: string) {
|
||||||
|
this.userId = user.id
|
||||||
|
this.email = user.email
|
||||||
|
this.firstName = user.firstName
|
||||||
|
this.lastName = user.lastName
|
||||||
|
this.creation = creation
|
||||||
|
this.emailChecked = user.emailChecked
|
||||||
|
this.hasElopage = hasElopage
|
||||||
|
this.deletedAt = user.deletedAt
|
||||||
|
this.emailConfirmationSend = emailConfirmationSend
|
||||||
|
}
|
||||||
|
|
||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
userId: number
|
userId: number
|
||||||
|
|
||||||
@ -23,6 +36,9 @@ export class UserAdmin {
|
|||||||
@Field(() => Boolean)
|
@Field(() => Boolean)
|
||||||
hasElopage: boolean
|
hasElopage: boolean
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
deletedAt?: Date | null
|
||||||
|
|
||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
emailConfirmationSend?: string
|
emailConfirmationSend?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,14 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql'
|
import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql'
|
||||||
import { getCustomRepository, ObjectLiteral, getConnection, In } from '@dbTools/typeorm'
|
import {
|
||||||
|
getCustomRepository,
|
||||||
|
IsNull,
|
||||||
|
Not,
|
||||||
|
ObjectLiteral,
|
||||||
|
getConnection,
|
||||||
|
In,
|
||||||
|
} from '@dbTools/typeorm'
|
||||||
import { UserAdmin, SearchUsersResult } from '../model/UserAdmin'
|
import { UserAdmin, SearchUsersResult } from '../model/UserAdmin'
|
||||||
import { PendingCreation } from '../model/PendingCreation'
|
import { PendingCreation } from '../model/PendingCreation'
|
||||||
import { CreatePendingCreations } from '../model/CreatePendingCreations'
|
import { CreatePendingCreations } from '../model/CreatePendingCreations'
|
||||||
@ -31,7 +38,14 @@ export class AdminResolver {
|
|||||||
@Authorized([RIGHTS.SEARCH_USERS])
|
@Authorized([RIGHTS.SEARCH_USERS])
|
||||||
@Query(() => SearchUsersResult)
|
@Query(() => SearchUsersResult)
|
||||||
async searchUsers(
|
async searchUsers(
|
||||||
@Args() { searchText, currentPage = 1, pageSize = 25, notActivated = false }: SearchUsersArgs,
|
@Args()
|
||||||
|
{
|
||||||
|
searchText,
|
||||||
|
currentPage = 1,
|
||||||
|
pageSize = 25,
|
||||||
|
notActivated = false,
|
||||||
|
isDeleted = false,
|
||||||
|
}: SearchUsersArgs,
|
||||||
): Promise<SearchUsersResult> {
|
): Promise<SearchUsersResult> {
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
|
|
||||||
@ -40,7 +54,11 @@ export class AdminResolver {
|
|||||||
filterCriteria.push({ emailChecked: false })
|
filterCriteria.push({ emailChecked: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
const userFields = ['id', 'firstName', 'lastName', 'email', 'emailChecked']
|
if (isDeleted) {
|
||||||
|
filterCriteria.push({ deletedAt: Not(IsNull()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
const userFields = ['id', 'firstName', 'lastName', 'email', 'emailChecked', 'deletedAt']
|
||||||
const [users, count] = await userRepository.findBySearchCriteriaPagedFiltered(
|
const [users, count] = await userRepository.findBySearchCriteriaPagedFiltered(
|
||||||
userFields.map((fieldName) => {
|
userFields.map((fieldName) => {
|
||||||
return 'user.' + fieldName
|
return 'user.' + fieldName
|
||||||
@ -51,19 +69,18 @@ export class AdminResolver {
|
|||||||
pageSize,
|
pageSize,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (users.length === 0) {
|
||||||
|
return {
|
||||||
|
userCount: 0,
|
||||||
|
userList: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const creations = await getUserCreations(users.map((u) => u.id))
|
const creations = await getUserCreations(users.map((u) => u.id))
|
||||||
|
|
||||||
const adminUsers = await Promise.all(
|
const adminUsers = await Promise.all(
|
||||||
users.map(async (user) => {
|
users.map(async (user) => {
|
||||||
const adminUser = new UserAdmin()
|
let emailConfirmationSend = ''
|
||||||
adminUser.userId = user.id
|
|
||||||
adminUser.firstName = user.firstName
|
|
||||||
adminUser.lastName = user.lastName
|
|
||||||
adminUser.email = user.email
|
|
||||||
const userCreations = creations.find((c) => c.id === user.id)
|
|
||||||
adminUser.creation = userCreations ? userCreations.creations : [1000, 1000, 1000]
|
|
||||||
adminUser.emailChecked = user.emailChecked
|
|
||||||
adminUser.hasElopage = await hasElopageBuys(user.email)
|
|
||||||
if (!user.emailChecked) {
|
if (!user.emailChecked) {
|
||||||
const emailOptIn = await LoginEmailOptIn.findOne(
|
const emailOptIn = await LoginEmailOptIn.findOne(
|
||||||
{
|
{
|
||||||
@ -79,12 +96,19 @@ export class AdminResolver {
|
|||||||
)
|
)
|
||||||
if (emailOptIn) {
|
if (emailOptIn) {
|
||||||
if (emailOptIn.updatedAt) {
|
if (emailOptIn.updatedAt) {
|
||||||
adminUser.emailConfirmationSend = emailOptIn.updatedAt.toISOString()
|
emailConfirmationSend = emailOptIn.updatedAt.toISOString()
|
||||||
} else {
|
} else {
|
||||||
adminUser.emailConfirmationSend = emailOptIn.createdAt.toISOString()
|
emailConfirmationSend = emailOptIn.createdAt.toISOString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const userCreations = creations.find((c) => c.id === user.id)
|
||||||
|
const adminUser = new UserAdmin(
|
||||||
|
user,
|
||||||
|
userCreations ? userCreations.creations : [1000, 1000, 1000],
|
||||||
|
await hasElopageBuys(user.email),
|
||||||
|
emailConfirmationSend,
|
||||||
|
)
|
||||||
return adminUser
|
return adminUser
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -94,6 +118,39 @@ export class AdminResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.DELETE_USER])
|
||||||
|
@Mutation(() => Date, { nullable: true })
|
||||||
|
async deleteUser(@Arg('userId') userId: number, @Ctx() context: any): Promise<Date | null> {
|
||||||
|
const user = await User.findOne({ id: userId })
|
||||||
|
// user exists ?
|
||||||
|
if (!user) {
|
||||||
|
throw new Error(`Could not find user with userId: ${userId}`)
|
||||||
|
}
|
||||||
|
// moderator user disabled own account?
|
||||||
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
|
const moderatorUser = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
|
if (moderatorUser.id === userId) {
|
||||||
|
throw new Error('Moderator can not delete his own account!')
|
||||||
|
}
|
||||||
|
// soft-delete user
|
||||||
|
await user.softRemove()
|
||||||
|
const newUser = await User.findOne({ id: userId }, { withDeleted: true })
|
||||||
|
return newUser ? newUser.deletedAt : null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.UNDELETE_USER])
|
||||||
|
@Mutation(() => Date, { nullable: true })
|
||||||
|
async unDeleteUser(@Arg('userId') userId: number): Promise<Date | null> {
|
||||||
|
const user = await User.findOne({ id: userId }, { withDeleted: true })
|
||||||
|
// user exists ?
|
||||||
|
if (!user) {
|
||||||
|
throw new Error(`Could not find user with userId: ${userId}`)
|
||||||
|
}
|
||||||
|
// recover user account
|
||||||
|
await user.recover()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.CREATE_PENDING_CREATION])
|
@Authorized([RIGHTS.CREATE_PENDING_CREATION])
|
||||||
@Mutation(() => [Number])
|
@Mutation(() => [Number])
|
||||||
async createPendingCreation(
|
async createPendingCreation(
|
||||||
@ -206,7 +263,7 @@ export class AdminResolver {
|
|||||||
|
|
||||||
const userIds = pendingCreations.map((p) => p.userId)
|
const userIds = pendingCreations.map((p) => p.userId)
|
||||||
const userCreations = await getUserCreations(userIds)
|
const userCreations = await getUserCreations(userIds)
|
||||||
const users = await User.find({ id: In(userIds) })
|
const users = await User.find({ where: { id: In(userIds) }, withDeleted: true })
|
||||||
|
|
||||||
return pendingCreations.map((pendingCreation) => {
|
return pendingCreations.map((pendingCreation) => {
|
||||||
const user = users.find((u) => u.id === pendingCreation.userId)
|
const user = users.find((u) => u.id === pendingCreation.userId)
|
||||||
@ -240,6 +297,9 @@ export class AdminResolver {
|
|||||||
if (moderatorUser.id === pendingCreation.userId)
|
if (moderatorUser.id === pendingCreation.userId)
|
||||||
throw new Error('Moderator can not confirm own pending creation')
|
throw new Error('Moderator can not confirm own pending creation')
|
||||||
|
|
||||||
|
const user = await User.findOneOrFail({ id: pendingCreation.userId }, { withDeleted: true })
|
||||||
|
if (user.deletedAt) throw new Error('This user was deleted. Cannot confirm a creation.')
|
||||||
|
|
||||||
const creations = await getUserCreation(pendingCreation.userId, false)
|
const creations = await getUserCreation(pendingCreation.userId, false)
|
||||||
if (!isCreationValid(creations, Number(pendingCreation.amount) / 10000, pendingCreation.date)) {
|
if (!isCreationValid(creations, Number(pendingCreation.amount) / 10000, pendingCreation.date)) {
|
||||||
throw new Error('Creation is not valid!!')
|
throw new Error('Creation is not valid!!')
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable new-cap */
|
/* eslint-disable new-cap */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
|
|
||||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||||
import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm'
|
import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm'
|
||||||
|
|||||||
@ -252,7 +252,7 @@ export class UserResolver {
|
|||||||
throw new Error('No user with this credentials')
|
throw new Error('No user with this credentials')
|
||||||
})
|
})
|
||||||
if (dbUser.deletedAt) {
|
if (dbUser.deletedAt) {
|
||||||
throw new Error('This user was permanently disabled. Contact support for questions.')
|
throw new Error('This user was permanently deleted. Contact support for questions.')
|
||||||
}
|
}
|
||||||
if (!dbUser.emailChecked) {
|
if (!dbUser.emailChecked) {
|
||||||
throw new Error('User email not validated')
|
throw new Error('User email not validated')
|
||||||
@ -335,7 +335,7 @@ export class UserResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate email unique
|
// Validate email unique
|
||||||
// TODO: i can register an email in upper/lower case twice
|
email = email.trim().toLowerCase()
|
||||||
// TODO we cannot use repository.count(), since it does not allow to specify if you want to include the soft deletes
|
// TODO we cannot use repository.count(), since it does not allow to specify if you want to include the soft deletes
|
||||||
const userFound = await DbUser.findOne({ email }, { withDeleted: true })
|
const userFound = await DbUser.findOne({ email }, { withDeleted: true })
|
||||||
if (userFound) {
|
if (userFound) {
|
||||||
@ -408,6 +408,7 @@ export class UserResolver {
|
|||||||
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
||||||
@Mutation(() => Boolean)
|
@Mutation(() => Boolean)
|
||||||
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
|
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
|
||||||
|
email = email.trim().toLowerCase()
|
||||||
const user = await DbUser.findOneOrFail({ email: email })
|
const user = await DbUser.findOneOrFail({ email: email })
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
@ -448,7 +449,7 @@ export class UserResolver {
|
|||||||
@Query(() => Boolean)
|
@Query(() => Boolean)
|
||||||
async sendResetPasswordEmail(@Arg('email') email: string): Promise<boolean> {
|
async sendResetPasswordEmail(@Arg('email') email: string): Promise<boolean> {
|
||||||
// TODO: this has duplicate code with createUser
|
// TODO: this has duplicate code with createUser
|
||||||
|
email = email.trim().toLowerCase()
|
||||||
const user = await DbUser.findOneOrFail({ email })
|
const user = await DbUser.findOneOrFail({ email })
|
||||||
|
|
||||||
const optInCode = await getOptInCode(user.id)
|
const optInCode = await getOptInCode(user.id)
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export class UserRepository extends Repository<User> {
|
|||||||
currentPage: number,
|
currentPage: number,
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
): Promise<[User[], number]> {
|
): Promise<[User[], number]> {
|
||||||
return await this.createQueryBuilder('user')
|
const query = await this.createQueryBuilder('user')
|
||||||
.select(select)
|
.select(select)
|
||||||
.withDeleted()
|
.withDeleted()
|
||||||
.where(
|
.where(
|
||||||
@ -39,7 +39,10 @@ export class UserRepository extends Repository<User> {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.andWhere(filterCriteria)
|
filterCriteria.forEach((filter) => {
|
||||||
|
query.andWhere(filter)
|
||||||
|
})
|
||||||
|
return query
|
||||||
.take(pageSize)
|
.take(pageSize)
|
||||||
.skip((currentPage - 1) * pageSize)
|
.skip((currentPage - 1) * pageSize)
|
||||||
.getManyAndCount()
|
.getManyAndCount()
|
||||||
|
|||||||
17
database/migrations/0025-emails_to_lower.ts
Normal file
17
database/migrations/0025-emails_to_lower.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* MIGRATION TO MAKE ALL EMAILS LOWERCASE
|
||||||
|
*
|
||||||
|
* Make all `email` values in `users` lowercase.
|
||||||
|
* This allows safe queries without any modificators
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn('UPDATE `users` SET `email` = LOWER(`email`);')
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
// This migration cannot be revered
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido-database",
|
"name": "gradido-database",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"description": "Gradido Database Tool to execute database migrations",
|
"description": "Gradido Database Tool to execute database migrations",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": "https://github.com/gradido/gradido/database",
|
"repository": "https://github.com/gradido/gradido/database",
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { CreatePeterLustigSeed } from './seeds/users/peter-lustig.admin.seed'
|
|||||||
import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed'
|
import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed'
|
||||||
import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed'
|
import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed'
|
||||||
import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed'
|
import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed'
|
||||||
|
import { CreateStephenHawkingSeed } from './seeds/users/stephen-hawking.seed'
|
||||||
import { CreateGarrickOllivanderSeed } from './seeds/users/garrick-ollivander.seed'
|
import { CreateGarrickOllivanderSeed } from './seeds/users/garrick-ollivander.seed'
|
||||||
import { CreateUserSeed } from './seeds/create-user.seed'
|
import { CreateUserSeed } from './seeds/create-user.seed'
|
||||||
import { resetDB, pool, migration } from './helpers'
|
import { resetDB, pool, migration } from './helpers'
|
||||||
@ -44,6 +45,7 @@ const run = async (command: string) => {
|
|||||||
await runSeeder(CreateBibiBloxbergSeed)
|
await runSeeder(CreateBibiBloxbergSeed)
|
||||||
await runSeeder(CreateRaeuberHotzenplotzSeed)
|
await runSeeder(CreateRaeuberHotzenplotzSeed)
|
||||||
await runSeeder(CreateBobBaumeisterSeed)
|
await runSeeder(CreateBobBaumeisterSeed)
|
||||||
|
await runSeeder(CreateStephenHawkingSeed)
|
||||||
// eslint-disable-next-line prefer-spread
|
// eslint-disable-next-line prefer-spread
|
||||||
Array.apply(null, Array(96)).forEach(async () => {
|
Array.apply(null, Array(96)).forEach(async () => {
|
||||||
await runSeeder(CreateUserSeed)
|
await runSeeder(CreateUserSeed)
|
||||||
|
|||||||
9
database/src/seeds/users/stephen-hawking.seed.ts
Normal file
9
database/src/seeds/users/stephen-hawking.seed.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Factory, Seeder } from 'typeorm-seeding'
|
||||||
|
import { stephenHawking } from './stephen-hawking'
|
||||||
|
import { userSeeder } from '../helpers/user-helpers'
|
||||||
|
|
||||||
|
export class CreateStephenHawkingSeed implements Seeder {
|
||||||
|
public async run(factory: Factory): Promise<void> {
|
||||||
|
await userSeeder(factory, stephenHawking)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
database/src/seeds/users/stephen-hawking.ts
Normal file
23
database/src/seeds/users/stephen-hawking.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const stephenHawking: UserInterface = {
|
||||||
|
email: 'stephen@hawking.uk',
|
||||||
|
firstName: 'Stephen',
|
||||||
|
lastName: 'Hawking',
|
||||||
|
// description: 'A Brief History of Time',
|
||||||
|
password: BigInt('18075098469449931746'),
|
||||||
|
pubKey: Buffer.from('19576a7aab8cd4ce683ed6735bba937d6bdbd08016568f730a385b6481241213', 'hex'),
|
||||||
|
privKey: Buffer.from(
|
||||||
|
'1d8ce9b5df086a713fee9eb562adc127073f3211a6214a54e53eb22c1249d49e1e94580ab00f25fd4b38808c1e31b41624ef627f277d21ef5d5717d4b81958f13dc2b257759caba07c6fdbc72f86ab0f',
|
||||||
|
'hex',
|
||||||
|
),
|
||||||
|
emailHash: Buffer.from('71d4ed7a25d2130d445d6451135eefbbdd96c3105dd297783590ced0bf3116fd', 'hex'),
|
||||||
|
createdAt: new Date('1942-01-08T09:17:52'),
|
||||||
|
deletedAt: new Date('2018-03-14T09:17:52'),
|
||||||
|
emailChecked: true,
|
||||||
|
language: 'en',
|
||||||
|
passphrase:
|
||||||
|
'demise tree praise funny ignore despair vessel shop sorry try day peanut tongue toddler bone december inch chicken clump sheriff weasel rally check suggest ',
|
||||||
|
isAdmin: false,
|
||||||
|
addBalance: false,
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bootstrap-vue-gradido-wallet",
|
"name": "bootstrap-vue-gradido-wallet",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node run/server.js",
|
"start": "node run/server.js",
|
||||||
|
|||||||
@ -40,67 +40,3 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
|
||||||
.gdd-toaster-title {
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
.gdd-toaster-body {
|
|
||||||
color: #ffffff !important;
|
|
||||||
}
|
|
||||||
.gdd-toaster {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
.btn-primary pim {
|
|
||||||
background-color: #5a7b02;
|
|
||||||
border-color: #5e72e4;
|
|
||||||
}
|
|
||||||
a,
|
|
||||||
.copyright {
|
|
||||||
color: #5a7b02;
|
|
||||||
}
|
|
||||||
.font1_2em {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
.font2em {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
.gradido-global-color-text {
|
|
||||||
color: #3d443b;
|
|
||||||
}
|
|
||||||
.gradido-global-color-accent {
|
|
||||||
color: #047006;
|
|
||||||
}
|
|
||||||
.gradido-global-color-6e0a9c9e {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
.gradido-global-color-2d0fb154 {
|
|
||||||
color: #047006;
|
|
||||||
}
|
|
||||||
.gradido-global-color-16efe88c {
|
|
||||||
color: #7ebc55;
|
|
||||||
}
|
|
||||||
.gradido-global-color-1939326 {
|
|
||||||
color: #f6fff6;
|
|
||||||
}
|
|
||||||
.gradido-global-color-9d79fc1 {
|
|
||||||
color: #047006;
|
|
||||||
}
|
|
||||||
.gradido-global-color-6347f4d {
|
|
||||||
color: #5a7b02;
|
|
||||||
}
|
|
||||||
.gradido-global-color-4fbc19a {
|
|
||||||
color: #014034;
|
|
||||||
}
|
|
||||||
.gradido-global-color-d341874 {
|
|
||||||
color: #b6d939;
|
|
||||||
}
|
|
||||||
.gradido-global-color-619d338 {
|
|
||||||
color: #8ebfb1;
|
|
||||||
}
|
|
||||||
.gradido-global-color-44819a9 {
|
|
||||||
color: #026873;
|
|
||||||
}
|
|
||||||
.gradido-global-color-gray {
|
|
||||||
color: #858383;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -3,13 +3,11 @@
|
|||||||
|
|
||||||
@import "~bootstrap/scss/functions";
|
@import "~bootstrap/scss/functions";
|
||||||
|
|
||||||
|
|
||||||
@import "custom/variables";
|
@import "custom/variables";
|
||||||
// @import "~bootstrap/scss/variables"; wird am
|
// @import "~bootstrap/scss/variables"; wird am
|
||||||
// ende der custom/variables angehangen
|
// ende der custom/variables angehangen
|
||||||
|
|
||||||
// Bootstrap (4.5.3) mixins
|
// Bootstrap (4.5.3) mixins
|
||||||
|
|
||||||
@import "~bootstrap/scss/mixins";
|
@import "~bootstrap/scss/mixins";
|
||||||
|
|
||||||
// Bootstrap (4.5.3) components
|
// Bootstrap (4.5.3) components
|
||||||
@ -20,11 +18,18 @@
|
|||||||
@import "~bootstrap/scss/button-group";
|
@import "~bootstrap/scss/button-group";
|
||||||
@import "~bootstrap/scss/buttons";
|
@import "~bootstrap/scss/buttons";
|
||||||
@import "~bootstrap/scss/card";
|
@import "~bootstrap/scss/card";
|
||||||
|
@import "~bootstrap/scss/carousel";
|
||||||
|
@import "~bootstrap/scss/close";
|
||||||
|
@import "~bootstrap/scss/code";
|
||||||
@import "~bootstrap/scss/custom-forms";
|
@import "~bootstrap/scss/custom-forms";
|
||||||
|
@import "~bootstrap/scss/dropdown";
|
||||||
@import "~bootstrap/scss/forms";
|
@import "~bootstrap/scss/forms";
|
||||||
|
@import "~bootstrap/scss/functions";
|
||||||
@import "~bootstrap/scss/grid";
|
@import "~bootstrap/scss/grid";
|
||||||
@import "~bootstrap/scss/input-group";
|
@import "~bootstrap/scss/input-group";
|
||||||
@import "~bootstrap/scss/list-group";
|
@import "~bootstrap/scss/list-group";
|
||||||
|
@import "~bootstrap/scss/mixins";
|
||||||
|
@import "~bootstrap/scss/modal";
|
||||||
@import "~bootstrap/scss/nav";
|
@import "~bootstrap/scss/nav";
|
||||||
@import "~bootstrap/scss/navbar";
|
@import "~bootstrap/scss/navbar";
|
||||||
@import "~bootstrap/scss/pagination";
|
@import "~bootstrap/scss/pagination";
|
||||||
@ -38,7 +43,99 @@
|
|||||||
@import "~bootstrap/scss/type";
|
@import "~bootstrap/scss/type";
|
||||||
@import "~bootstrap/scss/utilities";
|
@import "~bootstrap/scss/utilities";
|
||||||
@import "~bootstrap/scss/variables";
|
@import "~bootstrap/scss/variables";
|
||||||
|
@import "~bootstrap/scss/bootstrap-grid";
|
||||||
|
@import "~bootstrap/scss/bootstrap-reboot";
|
||||||
|
@import "~bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
// Bootstrap-vue (2.21.1) scss
|
// Bootstrap-vue (2.21.1) scss
|
||||||
@import '~bootstrap-vue/src/index.scss';
|
@import '~bootstrap-vue/src/index.scss';
|
||||||
|
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
color: #155724;
|
||||||
|
background-color: #d4edda;
|
||||||
|
border-color: #c3e6cb;
|
||||||
|
}
|
||||||
|
.alert-danger {
|
||||||
|
color: #721c24;
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border-color: #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.b-toast-danger .toast .toast-header {
|
||||||
|
color: #721c24;
|
||||||
|
background-color: rgba(248,215,218,.85);
|
||||||
|
border-bottom-color: rgba(245,198,203,.85);
|
||||||
|
}
|
||||||
|
.b-toast-danger .toast .toast-body{
|
||||||
|
background-color: rgba(252,237,238,.85);
|
||||||
|
border-color: rgba(245,198,203,.85);
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-toast-success .toast .toast-header {
|
||||||
|
color: #155724;
|
||||||
|
background-color: rgba(212,237,218,.58);
|
||||||
|
border-bottom-color: rgba(195,230,203,.85);
|
||||||
|
}
|
||||||
|
.b-toast-success .toast .toast-body{
|
||||||
|
color: #155724;
|
||||||
|
background-color: rgba(212,237,218,.85);
|
||||||
|
border-bottom-color: rgba(195,230,203,.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-primary pim {
|
||||||
|
background-color: #5a7b02;
|
||||||
|
border-color: #5e72e4;
|
||||||
|
}
|
||||||
|
a,
|
||||||
|
.copyright {
|
||||||
|
color: #5a7b02;
|
||||||
|
}
|
||||||
|
.font1_2em {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
.font2em {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
.gradido-global-color-text {
|
||||||
|
color: #3d443b;
|
||||||
|
}
|
||||||
|
.gradido-global-color-accent {
|
||||||
|
color: #047006;
|
||||||
|
}
|
||||||
|
.gradido-global-color-6e0a9c9e {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.gradido-global-color-2d0fb154 {
|
||||||
|
color: #047006;
|
||||||
|
}
|
||||||
|
.gradido-global-color-16efe88c {
|
||||||
|
color: #7ebc55;
|
||||||
|
}
|
||||||
|
.gradido-global-color-1939326 {
|
||||||
|
color: #f6fff6;
|
||||||
|
}
|
||||||
|
.gradido-global-color-9d79fc1 {
|
||||||
|
color: #047006;
|
||||||
|
}
|
||||||
|
.gradido-global-color-6347f4d {
|
||||||
|
color: #5a7b02;
|
||||||
|
}
|
||||||
|
.gradido-global-color-4fbc19a {
|
||||||
|
color: #014034;
|
||||||
|
}
|
||||||
|
.gradido-global-color-d341874 {
|
||||||
|
color: #b6d939;
|
||||||
|
}
|
||||||
|
.gradido-global-color-619d338 {
|
||||||
|
color: #8ebfb1;
|
||||||
|
}
|
||||||
|
.gradido-global-color-44819a9 {
|
||||||
|
color: #026873;
|
||||||
|
}
|
||||||
|
.gradido-global-color-gray {
|
||||||
|
color: #858383;
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
{{ decay ? ' − ' + $n(decay.balance, 'decimal') : '' }}
|
{{ decay ? ' − ' + $n(decay.balance, 'decimal') : '' }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div v-if="decaytyp === 'new'">
|
<div v-if="decaytyp === 'new' || decaytyp === 'decayLastTransaction'">
|
||||||
<div class="d-flex" v-if="!decay.decayStartBlock">
|
<div class="d-flex" v-if="!decay.decayStartBlock">
|
||||||
<div style="width: 100%" class="text-center pb-3">
|
<div style="width: 100%" class="text-center pb-3">
|
||||||
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
||||||
@ -56,17 +56,22 @@
|
|||||||
<div>{{ $t('decay.decay') }}</div>
|
<div>{{ $t('decay.decay') }}</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="6">
|
<b-col cols="6">
|
||||||
<div>− {{ $n(decay.balance, 'decimal') }}</div>
|
<div v-if="decaytyp === 'new'">- {{ $n(decay.balance, 'decimal') }}</div>
|
||||||
|
<div v-if="decaytyp === 'decayLastTransaction'">
|
||||||
|
{{ $n(decay.balance + gddbalance, 'decimal') }} GDD -
|
||||||
|
{{ $n(decay.balance, 'decimal') }} GDD =
|
||||||
|
<b>{{ $n(gddbalance, 'decimal') }} GDD</b>
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<hr class="mt-2 mb-2" />
|
<hr class="mt-2 mb-2" />
|
||||||
<b-row>
|
<b-row v-if="decaytyp === 'new'">
|
||||||
<b-col class="text-center pt-3 pb-2">
|
<b-col class="text-center pt-3 pb-2">
|
||||||
<b>{{ $t('decay.calculation_total') }}</b>
|
<b>{{ $t('decay.calculation_total') }}</b>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<!-- Type-->
|
<!-- Type-->
|
||||||
<b-row>
|
<b-row v-if="decaytyp === 'new'">
|
||||||
<b-col cols="6" class="text-right">
|
<b-col cols="6" class="text-right">
|
||||||
<div v-if="type === 'send'">{{ $t('decay.sent') }}</div>
|
<div v-if="type === 'send'">{{ $t('decay.sent') }}</div>
|
||||||
<div v-if="type === 'receive'">{{ $t('decay.received') }}</div>
|
<div v-if="type === 'receive'">{{ $t('decay.received') }}</div>
|
||||||
@ -77,7 +82,7 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<!-- Decay-->
|
<!-- Decay-->
|
||||||
<b-row>
|
<b-row v-if="decaytyp === 'new'">
|
||||||
<b-col cols="6" class="text-right">
|
<b-col cols="6" class="text-right">
|
||||||
<div>{{ $t('decay.decay') }}</div>
|
<div>{{ $t('decay.decay') }}</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
@ -86,7 +91,7 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<!-- Total-->
|
<!-- Total-->
|
||||||
<b-row>
|
<b-row v-if="decaytyp === 'new'">
|
||||||
<b-col cols="6" class="text-right">
|
<b-col cols="6" class="text-right">
|
||||||
<div>{{ $t('decay.total') }}</div>
|
<div>{{ $t('decay.total') }}</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
@ -109,6 +114,7 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'DecayInformation',
|
name: 'DecayInformation',
|
||||||
props: {
|
props: {
|
||||||
|
gddbalance: { type: Number },
|
||||||
balance: { type: Number },
|
balance: { type: Number },
|
||||||
type: { type: String, default: '' },
|
type: { type: String, default: '' },
|
||||||
decay: {
|
decay: {
|
||||||
|
|||||||
@ -31,8 +31,13 @@ describe('Transaction', () => {
|
|||||||
expect(wrapper.find('div.gdt-transaction-list-item').exists()).toBeTruthy()
|
expect(wrapper.find('div.gdt-transaction-list-item').exists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a collapse button', () => {
|
it('has a collapse icon bi-caret-down-square', () => {
|
||||||
expect(wrapper.find('button[type="button"].btn-secondary').text()).toBe('i')
|
expect(wrapper.find('div.gdt-transaction-list-item').findAll('svg').at(1).classes()).toEqual([
|
||||||
|
'bi-caret-down-square',
|
||||||
|
'b-icon',
|
||||||
|
'bi',
|
||||||
|
'text-muted',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('no valid GDT entry type', () => {
|
describe('no valid GDT entry type', () => {
|
||||||
|
|||||||
@ -9,9 +9,10 @@
|
|||||||
|
|
||||||
<!-- collaps Button -->
|
<!-- collaps Button -->
|
||||||
<div class="text-right" style="width: 96%; position: absolute">
|
<div class="text-right" style="width: 96%; position: absolute">
|
||||||
<b-button class="btn-sm">
|
<b-icon
|
||||||
<b>i</b>
|
:icon="getCollapseState(id) ? 'caret-up-square' : 'caret-down-square'"
|
||||||
</b-button>
|
:class="getCollapseState(id) ? 'text-black' : 'text-muted'"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- type -->
|
<!-- type -->
|
||||||
@ -85,6 +86,16 @@ export default {
|
|||||||
gdt: { type: Number },
|
gdt: { type: Number },
|
||||||
id: { type: Number },
|
id: { type: Number },
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
collapseStatus: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCollapseState(id) {
|
||||||
|
return this.collapseStatus.includes('gdt-collapse-' + id)
|
||||||
|
},
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
collapseId() {
|
collapseId() {
|
||||||
return 'gdt-collapse-' + String(this.id)
|
return 'gdt-collapse-' + String(this.id)
|
||||||
@ -130,5 +141,14 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$root.$on('bv::collapse::state', (collapseId, isJustShown) => {
|
||||||
|
if (isJustShown) {
|
||||||
|
this.collapseStatus.push(collapseId)
|
||||||
|
} else {
|
||||||
|
this.collapseStatus = this.collapseStatus.filter((id) => id !== collapseId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -78,6 +78,7 @@ export const transactionsQuery = gql`
|
|||||||
decayDuration
|
decayDuration
|
||||||
decayStartBlock
|
decayStartBlock
|
||||||
}
|
}
|
||||||
|
firstTransaction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"switch-to-this-community": "zu dieser Gemeinschaft wechseln"
|
"switch-to-this-community": "zu dieser Gemeinschaft wechseln"
|
||||||
},
|
},
|
||||||
"decay": {
|
"decay": {
|
||||||
|
"befor_startblock_transaction": "Diese Transaktion beinhaltet keine Vergänglichkeit.",
|
||||||
"calculation_decay": "Berechnung der Vergänglichkeit",
|
"calculation_decay": "Berechnung der Vergänglichkeit",
|
||||||
"calculation_total": "Berechnung der Gesamtsumme",
|
"calculation_total": "Berechnung der Gesamtsumme",
|
||||||
"created": "Geschöpft",
|
"created": "Geschöpft",
|
||||||
@ -29,6 +30,7 @@
|
|||||||
"decayStart": " - Startblock für Vergänglichkeit am: ",
|
"decayStart": " - Startblock für Vergänglichkeit am: ",
|
||||||
"decay_introduced": "Die Vergänglichkeit wurde eingeführt am ",
|
"decay_introduced": "Die Vergänglichkeit wurde eingeführt am ",
|
||||||
"decay_since_last_transaction": "Vergänglichkeit seit der letzten Transaktion",
|
"decay_since_last_transaction": "Vergänglichkeit seit der letzten Transaktion",
|
||||||
|
"first_transaction": "Die erste Transaktion beinhaltet keine Vergänglichkeit.",
|
||||||
"hours": "Stunden",
|
"hours": "Stunden",
|
||||||
"last_transaction": "Letzte Transaktion",
|
"last_transaction": "Letzte Transaktion",
|
||||||
"minutes": "Minuten",
|
"minutes": "Minuten",
|
||||||
@ -215,6 +217,7 @@
|
|||||||
"gdt-text": "GradidoTransform Transaktionen",
|
"gdt-text": "GradidoTransform Transaktionen",
|
||||||
"more": "mehr",
|
"more": "mehr",
|
||||||
"nullTransactions": "Du hast noch keine Transaktionen auf deinem Konto.",
|
"nullTransactions": "Du hast noch keine Transaktionen auf deinem Konto.",
|
||||||
|
"receiverDeleted": "Das Empfängerkonto wurde gelöscht",
|
||||||
"receiverNotFound": "Empfänger nicht gefunden",
|
"receiverNotFound": "Empfänger nicht gefunden",
|
||||||
"show_all": "Alle <strong>{count}</strong> Transaktionen ansehen"
|
"show_all": "Alle <strong>{count}</strong> Transaktionen ansehen"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"switch-to-this-community": "Switch to this community"
|
"switch-to-this-community": "Switch to this community"
|
||||||
},
|
},
|
||||||
"decay": {
|
"decay": {
|
||||||
|
"befor_startblock_transaction": "This transaction does not include decay.",
|
||||||
"calculation_decay": "Calculation of Decay",
|
"calculation_decay": "Calculation of Decay",
|
||||||
"calculation_total": "Calculation of the total Amount",
|
"calculation_total": "Calculation of the total Amount",
|
||||||
"created": "Created",
|
"created": "Created",
|
||||||
@ -29,6 +30,7 @@
|
|||||||
"decayStart": " - Starting block for decay at: ",
|
"decayStart": " - Starting block for decay at: ",
|
||||||
"decay_introduced": "Decay was introduced on",
|
"decay_introduced": "Decay was introduced on",
|
||||||
"decay_since_last_transaction": "Decay since the last transaction",
|
"decay_since_last_transaction": "Decay since the last transaction",
|
||||||
|
"first_transaction": "The first transaction does not include decay.",
|
||||||
"hours": "Hours",
|
"hours": "Hours",
|
||||||
"last_transaction": "Last transaction:",
|
"last_transaction": "Last transaction:",
|
||||||
"minutes": "Minutes",
|
"minutes": "Minutes",
|
||||||
@ -215,6 +217,7 @@
|
|||||||
"gdt-text": "GradidoTransform Transactions",
|
"gdt-text": "GradidoTransform Transactions",
|
||||||
"more": "more",
|
"more": "more",
|
||||||
"nullTransactions": "You don't have any transactions on your account yet.",
|
"nullTransactions": "You don't have any transactions on your account yet.",
|
||||||
|
"receiverDeleted": "The recipient account was deleted",
|
||||||
"receiverNotFound": "Recipient not found",
|
"receiverNotFound": "Recipient not found",
|
||||||
"show_all": "View all <strong>{count}</strong> transactions."
|
"show_all": "View all <strong>{count}</strong> transactions."
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export const toasters = {
|
|||||||
message = message.replace(/^GraphQL error: /, '')
|
message = message.replace(/^GraphQL error: /, '')
|
||||||
this.$bvToast.toast(message, {
|
this.$bvToast.toast(message, {
|
||||||
autoHideDelay: 5000,
|
autoHideDelay: 5000,
|
||||||
appendToast: false,
|
appendToast: true,
|
||||||
solid: true,
|
solid: true,
|
||||||
toaster: 'b-toaster-top-right',
|
toaster: 'b-toaster-top-right',
|
||||||
headerClass: 'gdd-toaster-title',
|
headerClass: 'gdd-toaster-title',
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<gdd-transaction-list
|
<gdd-transaction-list
|
||||||
|
:gddbalance="balance"
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
:pageSize="5"
|
:pageSize="5"
|
||||||
:timestamp="timestamp"
|
:timestamp="timestamp"
|
||||||
|
|||||||
@ -117,7 +117,7 @@ describe('GddTransactionList', () => {
|
|||||||
decay: { balance: '1.5' },
|
decay: { balance: '1.5' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
balance: '1.07',
|
balance: 1.07,
|
||||||
type: 'decay',
|
type: 'decay',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -135,12 +135,31 @@ describe('GddTransactionList', () => {
|
|||||||
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(0)
|
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has a bi-caret-down-square icon', () => {
|
||||||
|
expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
'bi-caret-down-square',
|
||||||
|
'b-icon',
|
||||||
|
'bi',
|
||||||
|
'text-muted',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// it('transaction is clicked', async () => {
|
||||||
|
// await transaction.trigger('click')
|
||||||
|
// expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
// 'bi-caret-up-square',
|
||||||
|
// 'b-icon',
|
||||||
|
// 'bi',
|
||||||
|
// 'text-muted',
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
|
||||||
it('has a bi-arrow-left-circle icon', () => {
|
it('has a bi-arrow-left-circle icon', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('bi-arrow-left-circle')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('bi-arrow-left-circle')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has text-danger color', () => {
|
it('has text-danger color', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('text-danger')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('text-danger')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a minus operator', () => {
|
it('has a minus operator', () => {
|
||||||
@ -186,12 +205,33 @@ describe('GddTransactionList', () => {
|
|||||||
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(1)
|
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has a bi-caret-down-square icon', () => {
|
||||||
|
expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
'bi-caret-down-square',
|
||||||
|
'b-icon',
|
||||||
|
'bi',
|
||||||
|
'text-muted',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// it('transaction is clicked', async () => {
|
||||||
|
// await transaction.trigger('click')
|
||||||
|
// expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
// 'bi-caret-up-square',
|
||||||
|
// 'b-icon',
|
||||||
|
// 'bi',
|
||||||
|
// 'text-muted',
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
|
||||||
it('has a bi-gift icon', () => {
|
it('has a bi-gift icon', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('bi-gift')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('bi-gift')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has gradido-global-color-accent color', () => {
|
it('has gradido-global-color-accent color', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('gradido-global-color-accent')
|
expect(transaction.findAll('svg').at(1).classes()).toContain(
|
||||||
|
'gradido-global-color-accent',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a plus operator', () => {
|
it('has a plus operator', () => {
|
||||||
@ -225,12 +265,33 @@ describe('GddTransactionList', () => {
|
|||||||
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(2)
|
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has a bi-caret-down-square icon', () => {
|
||||||
|
expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
'bi-caret-down-square',
|
||||||
|
'b-icon',
|
||||||
|
'bi',
|
||||||
|
'text-muted',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// it('transaction is clicked', async () => {
|
||||||
|
// await transaction.trigger('click')
|
||||||
|
// expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
// 'bi-caret-up-square',
|
||||||
|
// 'b-icon',
|
||||||
|
// 'bi',
|
||||||
|
// 'text-muted',
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
|
||||||
it('has a bi-arrow-right-circle icon', () => {
|
it('has a bi-arrow-right-circle icon', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('bi-arrow-right-circle')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('bi-arrow-right-circle')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has gradido-global-color-accent color', () => {
|
it('has gradido-global-color-accent color', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('gradido-global-color-accent')
|
expect(transaction.findAll('svg').at(1).classes()).toContain(
|
||||||
|
'gradido-global-color-accent',
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a plus operator', () => {
|
it('has a plus operator', () => {
|
||||||
@ -276,12 +337,31 @@ describe('GddTransactionList', () => {
|
|||||||
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(3)
|
transaction = wrapper.findAll('div.gdd-transaction-list-item').at(3)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('has a bi-caret-down-square icon', () => {
|
||||||
|
expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
'bi-caret-down-square',
|
||||||
|
'b-icon',
|
||||||
|
'bi',
|
||||||
|
'text-muted',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
// it('transaction is clicked', async () => {
|
||||||
|
// await transaction.trigger('click')
|
||||||
|
// expect(transaction.findAll('svg').at(0).classes()).toEqual([
|
||||||
|
// 'bi-caret-up-square',
|
||||||
|
// 'b-icon',
|
||||||
|
// 'bi',
|
||||||
|
// 'text-muted',
|
||||||
|
// ])
|
||||||
|
// })
|
||||||
|
|
||||||
it('has a bi-droplet-half icon', () => {
|
it('has a bi-droplet-half icon', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('bi-droplet-half')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('bi-droplet-half')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has gradido-global-color-gray color', () => {
|
it('has gradido-global-color-gray color', () => {
|
||||||
expect(transaction.find('svg').classes()).toContain('gradido-global-color-gray')
|
expect(transaction.findAll('svg').at(1).classes()).toContain('gradido-global-color-gray')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a minus operator', () => {
|
it('has a minus operator', () => {
|
||||||
|
|||||||
@ -2,35 +2,44 @@
|
|||||||
<div class="gdd-transaction-list">
|
<div class="gdd-transaction-list">
|
||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
<div v-if="!transactions" class="test-no-transactionlist text-right">
|
<div v-if="!transactions" class="test-no-transactionlist text-right">
|
||||||
<b-icon icon="exclamation-triangle" class="mr-2" style="color: red"></b-icon>
|
<b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>
|
||||||
<small>
|
<small>
|
||||||
{{ $t('error.no-transactionlist') }}
|
{{ $t('error.no-transactionlist') }}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="transactionCount < 0" class="test-empty-transactionlist text-right">
|
<div v-if="transactionCount < 0" class="test-empty-transactionlist text-right">
|
||||||
<b-icon icon="exclamation-triangle" class="mr-2" style="color: red"></b-icon>
|
<b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>
|
||||||
<small>{{ $t('error.empty-transactionlist') }}</small>
|
<small>{{ $t('error.empty-transactionlist') }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-for="{ decay, transactionId, type, date, balance, name, memo } in transactions"
|
v-for="{
|
||||||
|
decay,
|
||||||
|
transactionId,
|
||||||
|
type,
|
||||||
|
date,
|
||||||
|
balance,
|
||||||
|
name,
|
||||||
|
memo,
|
||||||
|
firstTransaction,
|
||||||
|
decayDuration,
|
||||||
|
decayEnd,
|
||||||
|
decayStart,
|
||||||
|
} in transactions"
|
||||||
:key="transactionId"
|
:key="transactionId"
|
||||||
:style="type === 'decay' ? 'background-color:#f1e0ae3d' : ''"
|
:style="type === 'decay' ? 'background-color:#f1e0ae3d' : ''"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="list-group-item gdd-transaction-list-item"
|
class="list-group-item gdd-transaction-list-item"
|
||||||
|
:class="getCollapseState(transactionId) ? 'bg-secondary' : ''"
|
||||||
v-b-toggle="'decay-' + transactionId"
|
v-b-toggle="'decay-' + transactionId"
|
||||||
>
|
>
|
||||||
<!-- Collaps Button -->
|
<!-- Collaps Button -->
|
||||||
<div
|
<div class="text-right" style="width: 95%; position: absolute">
|
||||||
v-if="type != 'decay' && decay"
|
<b-icon
|
||||||
class="text-right"
|
:icon="getCollapseState(transactionId) ? 'caret-up-square' : 'caret-down-square'"
|
||||||
style="width: 95%; position: absolute"
|
:class="getCollapseState(transactionId) ? 'text-black' : 'text-muted'"
|
||||||
>
|
/>
|
||||||
<b-button class="btn-sm">
|
|
||||||
<b>i</b>
|
|
||||||
</b-button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<b-row>
|
<b-row>
|
||||||
<!-- ICON -->
|
<!-- ICON -->
|
||||||
@ -112,9 +121,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Collaps Start -->
|
<!-- Collaps Start -->
|
||||||
|
<!-- v-if="
|
||||||
<b-collapse v-if="type != 'decay' && decay" class="pb-4" :id="'decay-' + transactionId">
|
(type != 'decay' && decay) ||
|
||||||
<div style="border: 0px; background-color: #f1f1f1" class="p-2 pb-4 mb-4">
|
firstTransaction ||
|
||||||
|
(!firstTransaction && decay === null)
|
||||||
|
" -->
|
||||||
|
<b-collapse class="pb-4" :id="'decay-' + transactionId">
|
||||||
|
<div class="pt-4 pb-4 bg-white border border-muted">
|
||||||
<decay-information
|
<decay-information
|
||||||
v-if="decay"
|
v-if="decay"
|
||||||
decaytyp="new"
|
decaytyp="new"
|
||||||
@ -122,6 +135,30 @@
|
|||||||
:decay="decay"
|
:decay="decay"
|
||||||
:type="type"
|
:type="type"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="firstTransaction" class="mt-3 mb-3 text-center">
|
||||||
|
<b>{{ $t('decay.first_transaction') }}</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="type !== 'decay' && !firstTransaction && decay === null"
|
||||||
|
class="mt-3 mb-3 text-center"
|
||||||
|
>
|
||||||
|
<b>{{ $t('decay.befor_startblock_transaction') }}</b>
|
||||||
|
</div>
|
||||||
|
<div v-if="type === 'decay'" class="mt-3 mb-3">
|
||||||
|
<decay-information
|
||||||
|
decaytyp="decayLastTransaction"
|
||||||
|
:gddbalance="gddbalance"
|
||||||
|
:balance="balance"
|
||||||
|
:decay="{
|
||||||
|
balance: balance,
|
||||||
|
decayStart: parseInt(decayStart),
|
||||||
|
decayEnd: parseInt(decayEnd),
|
||||||
|
decayDuration: parseInt(decayDuration),
|
||||||
|
}"
|
||||||
|
:type="type"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-collapse>
|
</b-collapse>
|
||||||
|
|
||||||
@ -161,9 +198,11 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
collapseStatus: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
gddbalance: { type: Number },
|
||||||
transactions: { default: () => [] },
|
transactions: { default: () => [] },
|
||||||
pageSize: { type: Number, default: 25 },
|
pageSize: { type: Number, default: 25 },
|
||||||
timestamp: { type: Number, default: 0 },
|
timestamp: { type: Number, default: 0 },
|
||||||
@ -191,6 +230,18 @@ export default {
|
|||||||
throwError(msg) {
|
throwError(msg) {
|
||||||
throw new Error(msg)
|
throw new Error(msg)
|
||||||
},
|
},
|
||||||
|
getCollapseState(transactionId) {
|
||||||
|
return this.collapseStatus.includes('decay-' + transactionId)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$root.$on('bv::collapse::state', (collapseId, isJustShown) => {
|
||||||
|
if (isJustShown) {
|
||||||
|
this.collapseStatus.push(collapseId)
|
||||||
|
} else {
|
||||||
|
this.collapseStatus = this.collapseStatus.filter((id) => id !== collapseId)
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentPage() {
|
currentPage() {
|
||||||
|
|||||||
@ -108,7 +108,7 @@
|
|||||||
v-if="showError"
|
v-if="showError"
|
||||||
show
|
show
|
||||||
dismissible
|
dismissible
|
||||||
variant="warning"
|
variant="danger"
|
||||||
@dismissed="closeAlert"
|
@dismissed="closeAlert"
|
||||||
>
|
>
|
||||||
<span class="alert-icon"><i class="ni ni-point"></i></span>
|
<span class="alert-icon"><i class="ni ni-point"></i></span>
|
||||||
|
|||||||
@ -13,6 +13,12 @@
|
|||||||
<div class="test-receiver-not-found" v-if="errorResult === 'recipient not known'">
|
<div class="test-receiver-not-found" v-if="errorResult === 'recipient not known'">
|
||||||
{{ $t('transaction.receiverNotFound') }}
|
{{ $t('transaction.receiverNotFound') }}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="test-receiver-not-found"
|
||||||
|
v-if="errorResult === 'GraphQL error: The recipient account was deleted'"
|
||||||
|
>
|
||||||
|
{{ $t('transaction.receiverDeleted') }}
|
||||||
|
</div>
|
||||||
<div v-else>({{ errorResult }})</div>
|
<div v-else>({{ errorResult }})</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center mt-3">
|
<p class="text-center mt-3">
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
<p class="tab-tex">{{ $t('transaction.gdd-text') }}</p>
|
<p class="tab-tex">{{ $t('transaction.gdd-text') }}</p>
|
||||||
|
|
||||||
<gdd-transaction-list
|
<gdd-transaction-list
|
||||||
|
:gddbalance="balance"
|
||||||
:timestamp="timestamp"
|
:timestamp="timestamp"
|
||||||
:transactionCount="transactionCount"
|
:transactionCount="transactionCount"
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradido",
|
"name": "gradido",
|
||||||
"version": "1.6.5",
|
"version": "1.6.6",
|
||||||
"description": "Gradido",
|
"description": "Gradido",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "git@github.com:gradido/gradido.git",
|
"repository": "git@github.com:gradido/gradido.git",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user