mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into e2e-test-setup
This commit is contained in:
commit
d29832260a
@ -26,5 +26,5 @@ module.exports = {
|
||||
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
|
||||
// snapshotSerializers: ['jest-serializer-vue'],
|
||||
transformIgnorePatterns: ['<rootDir>/node_modules/(?!vee-validate/dist/rules)'],
|
||||
testEnvironment: 'jest-environment-jsdom-sixteen',
|
||||
testEnvironment: 'jest-environment-jsdom-sixteen', // why this is still needed? should not be needed anymore since jest@26, see: https://www.npmjs.com/package/jest-environment-jsdom-sixteen
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "26.6.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom-sixteen": "^2.0.0",
|
||||
"portal-vue": "^2.1.7",
|
||||
"qrcanvas-vue": "2.1.1",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
@ -70,7 +71,6 @@
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-vue": "^7.20.0",
|
||||
"jest-environment-jsdom-sixteen": "^2.0.0",
|
||||
"postcss": "^8.4.8",
|
||||
"postcss-html": "^1.3.0",
|
||||
"postcss-scss": "^4.0.3",
|
||||
|
||||
39
admin/src/components/CommunityStatistic.spec.js
Normal file
39
admin/src/components/CommunityStatistic.spec.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import CommunityStatistic from './CommunityStatistic'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$n: jest.fn((n) => n),
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
value: {
|
||||
totalUsers: '123',
|
||||
activeUsers: '100',
|
||||
deletedUsers: '5',
|
||||
totalGradidoCreated: '2500',
|
||||
totalGradidoDecayed: '200',
|
||||
totalGradidoAvailable: '500',
|
||||
totalGradidoUnbookedDecayed: '111',
|
||||
},
|
||||
}
|
||||
|
||||
describe('CommunityStatistic', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(CommunityStatistic, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the Div Element ".community-statistic"', () => {
|
||||
expect(wrapper.find('div.community-statistic').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
59
admin/src/components/CommunityStatistic.vue
Normal file
59
admin/src/components/CommunityStatistic.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="community-statistic">
|
||||
<div>
|
||||
<b-jumbotron bg-variant="info" text-variant="white" border-variant="dark">
|
||||
<template #header>{{ $t('statistic.name') }}</template>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<div>
|
||||
{{ $t('statistic.totalUsers') }}{{ $t('math.colon') }}
|
||||
<b>{{ value.totalUsers }}</b>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.activeUsers') }}{{ $t('math.colon') }}
|
||||
<b>{{ value.activeUsers }}</b>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.deletedUsers') }}{{ $t('math.colon') }}
|
||||
<b>{{ value.deletedUsers }}</b>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoCreated') }}{{ $t('math.colon') }}
|
||||
<b>{{ $n(value.totalGradidoCreated, 'decimal') }} {{ $t('GDD') }}</b>
|
||||
<small class="ml-5">{{ value.totalGradidoCreated }}</small>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoDecayed') }}{{ $t('math.colon') }}
|
||||
<b>{{ $n(value.totalGradidoDecayed, 'decimal') }} {{ $t('GDD') }}</b>
|
||||
<small class="ml-5">{{ value.totalGradidoDecayed }}</small>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoAvailable') }}{{ $t('math.colon') }}
|
||||
<b>{{ $n(value.totalGradidoAvailable, 'decimal') }} {{ $t('GDD') }}</b>
|
||||
<small class="ml-5">{{ value.totalGradidoAvailable }}</small>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoUnbookedDecayed') }}{{ $t('math.colon') }}
|
||||
<b>{{ $n(value.totalGradidoUnbookedDecayed, 'decimal') }} {{ $t('GDD') }}</b>
|
||||
<small class="ml-5">{{ value.totalGradidoUnbookedDecayed }}</small>
|
||||
</div>
|
||||
</b-jumbotron>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CONFIG from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'CommunityStatistic',
|
||||
props: {
|
||||
value: { type: Object },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
CONFIG,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
29
admin/src/components/ContentFooter.spec.js
Normal file
29
admin/src/components/ContentFooter.spec.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ContentFooter from './ContentFooter'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$i18n: {
|
||||
locale: jest.fn(() => 'en'),
|
||||
},
|
||||
}
|
||||
|
||||
describe('ContentFooter', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(ContentFooter, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the div element ".content-footer"', () => {
|
||||
expect(wrapper.find('div.content-footer').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="content-footer">
|
||||
<hr />
|
||||
<b-row align-v="center" class="mt-4 justify-content-lg-between">
|
||||
<b-col>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ContributionLinkForm from './ContributionLinkForm.vue'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
@ -8,9 +9,13 @@ global.alert = jest.fn()
|
||||
const propsData = {
|
||||
contributionLinkData: {},
|
||||
}
|
||||
const apolloMutateMock = jest.fn().mockResolvedValue()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
}
|
||||
|
||||
// const mockAPIcall = jest.fn()
|
||||
@ -99,4 +104,16 @@ describe('ContributionLinkForm', () => {
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
describe('send createContributionLink with error', () => {
|
||||
beforeEach(() => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })
|
||||
wrapper = Wrapper()
|
||||
wrapper.vm.onSubmit()
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('contributionLink.noStartDate')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
15
admin/src/graphql/communityStatistics.js
Normal file
15
admin/src/graphql/communityStatistics.js
Normal file
@ -0,0 +1,15 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const communityStatistics = gql`
|
||||
query {
|
||||
communityStatistics {
|
||||
totalUsers
|
||||
activeUsers
|
||||
deletedUsers
|
||||
totalGradidoCreated
|
||||
totalGradidoDecayed
|
||||
totalGradidoAvailable
|
||||
totalGradidoUnbookedDecayed
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -73,6 +73,7 @@
|
||||
"hide_details": "Details verbergen",
|
||||
"lastname": "Nachname",
|
||||
"math": {
|
||||
"colon": ":",
|
||||
"exclaim": "!",
|
||||
"pipe": "|",
|
||||
"plus": "+"
|
||||
@ -105,6 +106,16 @@
|
||||
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
||||
"remove_all": "alle Nutzer entfernen",
|
||||
"save": "Speichern",
|
||||
"statistic": {
|
||||
"activeUsers": "Aktive Mitglieder",
|
||||
"deletedUsers": "Gelöschte Mitglieder",
|
||||
"name": "Statistik",
|
||||
"totalGradidoAvailable": "GDD insgesamt im Umlauf",
|
||||
"totalGradidoCreated": "GDD insgesamt geschöpft",
|
||||
"totalGradidoDecayed": "GDD insgesamt verfallen",
|
||||
"totalGradidoUnbookedDecayed": "Ungebuchter GDD Verfall",
|
||||
"totalUsers": "Mitglieder"
|
||||
},
|
||||
"status": "Status",
|
||||
"success": "Erfolg",
|
||||
"text": "Text",
|
||||
|
||||
@ -73,6 +73,7 @@
|
||||
"hide_details": "Hide details",
|
||||
"lastname": "Lastname",
|
||||
"math": {
|
||||
"colon": ":",
|
||||
"exclaim": "!",
|
||||
"pipe": "|",
|
||||
"plus": "+"
|
||||
@ -105,6 +106,16 @@
|
||||
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
||||
"remove_all": "Remove all users",
|
||||
"save": "Speichern",
|
||||
"statistic": {
|
||||
"activeUsers": "Active members",
|
||||
"deletedUsers": "Deleted members",
|
||||
"name": "Statistic",
|
||||
"totalGradidoAvailable": "Total GDD in circulation",
|
||||
"totalGradidoCreated": "Total created GDD",
|
||||
"totalGradidoDecayed": "Total GDD decay",
|
||||
"totalGradidoUnbookedDecayed": "Unbooked GDD decay",
|
||||
"totalUsers": "Members"
|
||||
},
|
||||
"status": "Status",
|
||||
"success": "Success",
|
||||
"text": "Text",
|
||||
|
||||
@ -1,28 +1,83 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Overview from './Overview.vue'
|
||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||
import { communityStatistics } from '@/graphql/communityStatistics.js'
|
||||
import { listUnconfirmedContributions } from '@/graphql/listUnconfirmedContributions.js'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
listUnconfirmedContributions: [
|
||||
{
|
||||
pending: true,
|
||||
const apolloQueryMock = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
listUnconfirmedContributions: [
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
communityStatistics: {
|
||||
totalUsers: 3113,
|
||||
activeUsers: 1057,
|
||||
deletedUsers: 35,
|
||||
totalGradidoCreated: '4083774.05000000000000000000',
|
||||
totalGradidoDecayed: '-1062639.13634129622923372197',
|
||||
totalGradidoAvailable: '2513565.869444365732411569',
|
||||
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
listContributionLinks: {
|
||||
links: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Meditation',
|
||||
memo: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l',
|
||||
amount: '200',
|
||||
validFrom: '2022-04-01',
|
||||
validTo: '2022-08-01',
|
||||
cycle: 'täglich',
|
||||
maxPerCycle: '3',
|
||||
maxAmountPerMonth: 0,
|
||||
link: 'https://localhost/redeem/CL-1a2345678',
|
||||
},
|
||||
],
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
.mockResolvedValue({
|
||||
data: {
|
||||
listUnconfirmedContributions: [
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
{
|
||||
pending: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const storeCommitMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$n: jest.fn((n) => n),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
@ -47,10 +102,30 @@ describe('Overview', () => {
|
||||
})
|
||||
|
||||
it('calls listUnconfirmedContributions', () => {
|
||||
expect(apolloQueryMock).toBeCalled()
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: listUnconfirmedContributions,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('commts three pending creations to store', () => {
|
||||
it('calls communityStatistics', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: communityStatistics,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('calls listContributionLinks', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: listContributionLinks,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('commits three pending creations to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('setOpenCreations', 3)
|
||||
})
|
||||
|
||||
|
||||
@ -29,26 +29,39 @@
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
<contribution-link :items="items" :count="count" />
|
||||
<community-statistic class="mt-5" v-model="statistics" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||
import { communityStatistics } from '@/graphql/communityStatistics.js'
|
||||
import ContributionLink from '../components/ContributionLink.vue'
|
||||
import { listUnconfirmedContributions } from '../graphql/listUnconfirmedContributions'
|
||||
import CommunityStatistic from '../components/CommunityStatistic.vue'
|
||||
import { listUnconfirmedContributions } from '@/graphql/listUnconfirmedContributions.js'
|
||||
|
||||
export default {
|
||||
name: 'overview',
|
||||
components: {
|
||||
ContributionLink,
|
||||
CommunityStatistic,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
count: 0,
|
||||
statistics: {
|
||||
totalUsers: null,
|
||||
activeUsers: null,
|
||||
deletedUsers: null,
|
||||
totalGradidoCreated: null,
|
||||
totalGradidoDecayed: null,
|
||||
totalGradidoAvailable: null,
|
||||
totalGradidoUnbookedDecayed: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getPendingCreations() {
|
||||
getPendingCreations() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listUnconfirmedContributions,
|
||||
@ -58,7 +71,7 @@ export default {
|
||||
this.$store.commit('setOpenCreations', result.data.listUnconfirmedContributions.length)
|
||||
})
|
||||
},
|
||||
async getContributionLinks() {
|
||||
getContributionLinks() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listContributionLinks,
|
||||
@ -72,9 +85,31 @@ export default {
|
||||
this.toastError('listContributionLinks has no result, use default data')
|
||||
})
|
||||
},
|
||||
getCommunityStatistics() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: communityStatistics,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.statistics.totalUsers = result.data.communityStatistics.totalUsers
|
||||
this.statistics.activeUsers = result.data.communityStatistics.activeUsers
|
||||
this.statistics.deletedUsers = result.data.communityStatistics.deletedUsers
|
||||
this.statistics.totalGradidoCreated = result.data.communityStatistics.totalGradidoCreated
|
||||
this.statistics.totalGradidoDecayed = result.data.communityStatistics.totalGradidoDecayed
|
||||
this.statistics.totalGradidoAvailable =
|
||||
result.data.communityStatistics.totalGradidoAvailable
|
||||
this.statistics.totalGradidoUnbookedDecayed =
|
||||
result.data.communityStatistics.totalGradidoUnbookedDecayed
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('communityStatistics has no result, use default data')
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getPendingCreations()
|
||||
this.getCommunityStatistics()
|
||||
this.getContributionLinks()
|
||||
},
|
||||
}
|
||||
|
||||
@ -25,6 +25,14 @@
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
},
|
||||
"klicktipp":
|
||||
{
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/backend/klicktipp.log",
|
||||
"pattern": "%d{ISO8601} %p %c %X{user} %f:%l %m",
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_"
|
||||
},
|
||||
"errorFile":
|
||||
{
|
||||
"type": "dateFile",
|
||||
@ -90,6 +98,17 @@
|
||||
"level": "debug",
|
||||
"enableCallStack": true
|
||||
},
|
||||
"klicktipp":
|
||||
{
|
||||
"appenders":
|
||||
[
|
||||
"klicktipp",
|
||||
"out",
|
||||
"errors"
|
||||
],
|
||||
"level": "debug",
|
||||
"enableCallStack": true
|
||||
},
|
||||
"http":
|
||||
{
|
||||
"appenders":
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
"dependencies": {
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"apollo-server-testing": "^2.25.2",
|
||||
"axios": "^0.21.1",
|
||||
@ -39,7 +40,8 @@
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sodium-native": "^3.3.0",
|
||||
"ts-jest": "^27.0.5",
|
||||
"type-graphql": "^1.1.1"
|
||||
"type-graphql": "^1.1.1",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
|
||||
@ -30,6 +30,9 @@ export enum RIGHTS {
|
||||
LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS',
|
||||
LIST_ALL_CONTRIBUTIONS = 'LIST_ALL_CONTRIBUTIONS',
|
||||
UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION',
|
||||
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
||||
COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS',
|
||||
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
||||
// Admin
|
||||
SEARCH_USERS = 'SEARCH_USERS',
|
||||
SET_USER_ROLE = 'SET_USER_ROLE',
|
||||
@ -45,7 +48,6 @@ export enum RIGHTS {
|
||||
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
|
||||
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
|
||||
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
||||
LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS',
|
||||
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
||||
UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK',
|
||||
}
|
||||
|
||||
@ -28,6 +28,9 @@ export const ROLE_USER = new Role('user', [
|
||||
RIGHTS.LIST_CONTRIBUTIONS,
|
||||
RIGHTS.LIST_ALL_CONTRIBUTIONS,
|
||||
RIGHTS.UPDATE_CONTRIBUTION,
|
||||
RIGHTS.SEARCH_ADMIN_USERS,
|
||||
RIGHTS.LIST_CONTRIBUTION_LINKS,
|
||||
RIGHTS.COMMUNITY_STATISTICS,
|
||||
])
|
||||
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ Decimal.set({
|
||||
})
|
||||
|
||||
const constants = {
|
||||
DB_VERSION: '0044-insert_missing_contributions',
|
||||
DB_VERSION: '0047-messages_tables',
|
||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
// default log level on production should be info
|
||||
|
||||
14
backend/src/graphql/enum/ContributionStatus.ts
Normal file
14
backend/src/graphql/enum/ContributionStatus.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum ContributionStatus {
|
||||
PENDING = 'PENDING',
|
||||
DELETED = 'DELETED',
|
||||
IN_PROGRESS = 'IN_PROGRESS',
|
||||
DENIED = 'DENIED',
|
||||
CONFIRMED = 'CONFIRMED',
|
||||
}
|
||||
|
||||
registerEnumType(ContributionStatus, {
|
||||
name: 'ContributionStatus',
|
||||
description: 'Name of the Type of the Contribution Status',
|
||||
})
|
||||
12
backend/src/graphql/enum/ContributionType.ts
Normal file
12
backend/src/graphql/enum/ContributionType.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum ContributionType {
|
||||
ADMIN = 'ADMIN',
|
||||
USER = 'USER',
|
||||
LINK = 'LINK',
|
||||
}
|
||||
|
||||
registerEnumType(ContributionType, {
|
||||
name: 'ContributionType',
|
||||
description: 'Name of the Type of the Contribution',
|
||||
})
|
||||
11
backend/src/graphql/enum/MessageType.ts
Normal file
11
backend/src/graphql/enum/MessageType.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum ContributionMessageType {
|
||||
HISTORY = 'HISTORY',
|
||||
DIALOG = 'DIALOG',
|
||||
}
|
||||
|
||||
registerEnumType(ContributionMessageType, {
|
||||
name: 'ContributionMessageType',
|
||||
description: 'Name of the Type of the ContributionMessage',
|
||||
})
|
||||
25
backend/src/graphql/model/AdminUser.ts
Normal file
25
backend/src/graphql/model/AdminUser.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { User } from '@entity/User'
|
||||
import { Field, Int, ObjectType } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class AdminUser {
|
||||
constructor(user: User) {
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class SearchAdminUsersResult {
|
||||
@Field(() => Int)
|
||||
userCount: number
|
||||
|
||||
@Field(() => [AdminUser])
|
||||
userList: AdminUser[]
|
||||
}
|
||||
26
backend/src/graphql/model/CommunityStatistics.ts
Normal file
26
backend/src/graphql/model/CommunityStatistics.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
@ObjectType()
|
||||
export class CommunityStatistics {
|
||||
@Field(() => Number)
|
||||
totalUsers: number
|
||||
|
||||
@Field(() => Number)
|
||||
activeUsers: number
|
||||
|
||||
@Field(() => Number)
|
||||
deletedUsers: number
|
||||
|
||||
@Field(() => Decimal)
|
||||
totalGradidoCreated: Decimal
|
||||
|
||||
@Field(() => Decimal)
|
||||
totalGradidoDecayed: Decimal
|
||||
|
||||
@Field(() => Decimal)
|
||||
totalGradidoAvailable: Decimal
|
||||
|
||||
@Field(() => Decimal)
|
||||
totalGradidoUnbookedDecayed: Decimal
|
||||
}
|
||||
@ -8,6 +8,8 @@ import { FULL_CREATION_AVAILABLE } from '../resolver/const/const'
|
||||
export class User {
|
||||
constructor(user: dbUser, creation: Decimal[] = FULL_CREATION_AVAILABLE) {
|
||||
this.id = user.id
|
||||
this.gradidoID = user.gradidoID
|
||||
this.alias = user.alias
|
||||
this.email = user.email
|
||||
this.firstName = user.firstName
|
||||
this.lastName = user.lastName
|
||||
@ -28,6 +30,12 @@ export class User {
|
||||
// `public_key` binary(32) DEFAULT NULL,
|
||||
// `privkey` binary(80) DEFAULT NULL,
|
||||
|
||||
@Field(() => String)
|
||||
gradidoID: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
alias: string
|
||||
|
||||
// TODO privacy issue here
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@ -1857,11 +1857,17 @@ describe('AdminResolver', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: Set this test in new location to have datas
|
||||
describe('listContributionLinks', () => {
|
||||
it('returns an error', async () => {
|
||||
it('returns an empty object', async () => {
|
||||
await expect(query({ query: listContributionLinks })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
data: {
|
||||
listContributionLinks: {
|
||||
count: 0,
|
||||
links: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@ -36,6 +36,8 @@ import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||
import { User as dbUser } from '@entity/User'
|
||||
import { User } from '@model/User'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { ContributionType } from '@enum/ContributionType'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { Decay } from '@model/Decay'
|
||||
import Paginated from '@arg/Paginated'
|
||||
@ -260,6 +262,8 @@ export class AdminResolver {
|
||||
contribution.contributionDate = creationDateObj
|
||||
contribution.memo = memo
|
||||
contribution.moderatorId = moderator.id
|
||||
contribution.contributionType = ContributionType.ADMIN
|
||||
contribution.contributionStatus = ContributionStatus.PENDING
|
||||
|
||||
logger.trace('contribution to save', contribution)
|
||||
await Contribution.save(contribution)
|
||||
@ -337,6 +341,7 @@ export class AdminResolver {
|
||||
contributionToUpdate.memo = memo
|
||||
contributionToUpdate.contributionDate = new Date(creationDate)
|
||||
contributionToUpdate.moderatorId = moderator.id
|
||||
contributionToUpdate.contributionStatus = ContributionStatus.PENDING
|
||||
|
||||
await Contribution.save(contributionToUpdate)
|
||||
const result = new AdminUpdateContribution()
|
||||
@ -387,6 +392,8 @@ export class AdminResolver {
|
||||
if (!contribution) {
|
||||
throw new Error('Contribution not found for given id.')
|
||||
}
|
||||
contribution.contributionStatus = ContributionStatus.DELETED
|
||||
await contribution.save()
|
||||
const res = await contribution.softRemove()
|
||||
return !!res
|
||||
}
|
||||
@ -454,6 +461,7 @@ export class AdminResolver {
|
||||
contribution.confirmedAt = receivedCallDate
|
||||
contribution.confirmedBy = moderatorUser.id
|
||||
contribution.transactionId = transaction.id
|
||||
contribution.contributionStatus = ContributionStatus.CONFIRMED
|
||||
await queryRunner.manager.update(Contribution, { id: contribution.id }, contribution)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
@ -501,7 +509,7 @@ export class AdminResolver {
|
||||
order: { updatedAt: 'DESC' },
|
||||
})
|
||||
|
||||
optInCode = await checkOptInCode(optInCode, user.id)
|
||||
optInCode = await checkOptInCode(optInCode, user)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const emailSent = await sendAccountActivationEmail({
|
||||
|
||||
@ -7,6 +7,8 @@ import { FindOperator, IsNull, getConnection } from '@dbTools/typeorm'
|
||||
import ContributionArgs from '@arg/ContributionArgs'
|
||||
import Paginated from '@arg/Paginated'
|
||||
import { Order } from '@enum/Order'
|
||||
import { ContributionType } from '@enum/ContributionType'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { Contribution, ContributionListResult } from '@model/Contribution'
|
||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||
import { User } from '@model/User'
|
||||
@ -43,6 +45,8 @@ export class ContributionResolver {
|
||||
contribution.createdAt = new Date()
|
||||
contribution.contributionDate = creationDateObj
|
||||
contribution.memo = memo
|
||||
contribution.contributionType = ContributionType.USER
|
||||
contribution.contributionStatus = ContributionStatus.PENDING
|
||||
|
||||
logger.trace('contribution to save', contribution)
|
||||
await dbContribution.save(contribution)
|
||||
@ -66,6 +70,8 @@ export class ContributionResolver {
|
||||
if (contribution.confirmedAt) {
|
||||
throw new Error('A confirmed contribution can not be deleted')
|
||||
}
|
||||
contribution.contributionStatus = ContributionStatus.DELETED
|
||||
await contribution.save()
|
||||
const res = await contribution.softRemove()
|
||||
return !!res
|
||||
}
|
||||
@ -164,6 +170,7 @@ export class ContributionResolver {
|
||||
contributionToUpdate.amount = amount
|
||||
contributionToUpdate.memo = memo
|
||||
contributionToUpdate.contributionDate = new Date(creationDate)
|
||||
contributionToUpdate.contributionStatus = ContributionStatus.PENDING
|
||||
dbContribution.save(contributionToUpdate)
|
||||
|
||||
return new UnconfirmedContribution(contributionToUpdate, user, creations)
|
||||
|
||||
77
backend/src/graphql/resolver/StatisticsResolver.ts
Normal file
77
backend/src/graphql/resolver/StatisticsResolver.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { Resolver, Query, Authorized } from 'type-graphql'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { CommunityStatistics } from '@model/CommunityStatistics'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||
import { getConnection } from '@dbTools/typeorm'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
|
||||
@Resolver()
|
||||
export class StatisticsResolver {
|
||||
@Authorized([RIGHTS.COMMUNITY_STATISTICS])
|
||||
@Query(() => CommunityStatistics)
|
||||
async communityStatistics(): Promise<CommunityStatistics> {
|
||||
const allUsers = await DbUser.find({ withDeleted: true })
|
||||
|
||||
let totalUsers = 0
|
||||
let activeUsers = 0
|
||||
let deletedUsers = 0
|
||||
|
||||
let totalGradidoAvailable: Decimal = new Decimal(0)
|
||||
let totalGradidoUnbookedDecayed: Decimal = new Decimal(0)
|
||||
|
||||
const receivedCallDate = new Date()
|
||||
|
||||
for (let i = 0; i < allUsers.length; i++) {
|
||||
if (allUsers[i].deletedAt) {
|
||||
deletedUsers++
|
||||
} else {
|
||||
totalUsers++
|
||||
const lastTransaction = await DbTransaction.findOne({
|
||||
where: { userId: allUsers[i].id },
|
||||
order: { balanceDate: 'DESC' },
|
||||
})
|
||||
if (lastTransaction) {
|
||||
activeUsers++
|
||||
const decay = calculateDecay(
|
||||
lastTransaction.balance,
|
||||
lastTransaction.balanceDate,
|
||||
receivedCallDate,
|
||||
)
|
||||
if (decay) {
|
||||
totalGradidoAvailable = totalGradidoAvailable.plus(decay.balance.toString())
|
||||
totalGradidoUnbookedDecayed = totalGradidoUnbookedDecayed.plus(decay.decay.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const queryRunner = getConnection().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
|
||||
const { totalGradidoCreated } = await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.select('SUM(transaction.amount) AS totalGradidoCreated')
|
||||
.from(DbTransaction, 'transaction')
|
||||
.where('transaction.typeId = 1')
|
||||
.getRawOne()
|
||||
|
||||
const { totalGradidoDecayed } = await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.select('SUM(transaction.decay) AS totalGradidoDecayed')
|
||||
.from(DbTransaction, 'transaction')
|
||||
.where('transaction.decay IS NOT NULL')
|
||||
.getRawOne()
|
||||
|
||||
return {
|
||||
totalUsers,
|
||||
activeUsers,
|
||||
deletedUsers,
|
||||
totalGradidoCreated,
|
||||
totalGradidoDecayed,
|
||||
totalGradidoAvailable,
|
||||
totalGradidoUnbookedDecayed,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,8 @@ import { User } from '@model/User'
|
||||
import { calculateDecay } from '@/util/decay'
|
||||
import { executeTransaction } from './TransactionResolver'
|
||||
import { Order } from '@enum/Order'
|
||||
import { ContributionType } from '@enum/ContributionType'
|
||||
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||
import { getUserCreation, validateContribution } from './util/creations'
|
||||
@ -231,6 +233,9 @@ export class TransactionLinkResolver {
|
||||
contribution.memo = contributionLink.memo
|
||||
contribution.amount = contributionLink.amount
|
||||
contribution.contributionLinkId = contributionLink.id
|
||||
contribution.contributionType = ContributionType.LINK
|
||||
contribution.contributionStatus = ContributionStatus.CONFIRMED
|
||||
|
||||
await queryRunner.manager.insert(DbContribution, contribution)
|
||||
|
||||
const lastTransaction = await queryRunner.manager
|
||||
|
||||
@ -5,7 +5,7 @@ import { testEnvironment, headerPushMock, resetToken, cleanDB, resetEntity } fro
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { createUser, setPassword, forgotPassword, updateUserInfos } from '@/seeds/graphql/mutations'
|
||||
import { login, logout, verifyLogin, queryOptIn } from '@/seeds/graphql/queries'
|
||||
import { login, logout, verifyLogin, queryOptIn, searchAdminUsers } from '@/seeds/graphql/queries'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||
import { User } from '@entity/User'
|
||||
@ -20,6 +20,8 @@ import { ContributionLink } from '@model/ContributionLink'
|
||||
// import { TransactionLink } from '@entity/TransactionLink'
|
||||
|
||||
import { logger } from '@test/testSetup'
|
||||
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
|
||||
// import { klicktippSignIn } from '@/apis/KlicktippController'
|
||||
|
||||
@ -111,6 +113,8 @@ describe('UserResolver', () => {
|
||||
expect(user).toEqual([
|
||||
{
|
||||
id: expect.any(Number),
|
||||
gradidoID: expect.any(String),
|
||||
alias: null,
|
||||
email: 'peter@lustig.de',
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
@ -129,6 +133,10 @@ describe('UserResolver', () => {
|
||||
contributionLinkId: null,
|
||||
},
|
||||
])
|
||||
const valUUID = validateUUID(user[0].gradidoID)
|
||||
const verUUID = versionUUID(user[0].gradidoID)
|
||||
expect(valUUID).toEqual(true)
|
||||
expect(verUUID).toEqual(4)
|
||||
})
|
||||
|
||||
it('creates an email optin', () => {
|
||||
@ -198,7 +206,7 @@ describe('UserResolver', () => {
|
||||
it('sets "de" as default language', async () => {
|
||||
await mutate({
|
||||
mutation: createUser,
|
||||
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' },
|
||||
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'it' },
|
||||
})
|
||||
await expect(User.find()).resolves.toEqual(
|
||||
expect.arrayContaining([
|
||||
@ -871,6 +879,51 @@ bei Gradidio sei dabei!`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('searchAdminUsers', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws an error', async () => {
|
||||
resetToken()
|
||||
await expect(mutate({ mutation: searchAdminUsers })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
variables: {
|
||||
email: 'bibi@bloxberg.de',
|
||||
password: 'Aa12345_',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('finds peter@lustig.de', async () => {
|
||||
await expect(mutate({ mutation: searchAdminUsers })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
searchAdminUsers: {
|
||||
userCount: 1,
|
||||
userList: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
}),
|
||||
]),
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('printTimeDuration', () => {
|
||||
|
||||
@ -3,7 +3,7 @@ import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
import { Context, getUser } from '@/server/context'
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
|
||||
import { getConnection } from '@dbTools/typeorm'
|
||||
import { getConnection, getCustomRepository, IsNull, Not } from '@dbTools/typeorm'
|
||||
import CONFIG from '@/config'
|
||||
import { User } from '@model/User'
|
||||
import { User as DbUser } from '@entity/User'
|
||||
@ -32,6 +32,11 @@ import {
|
||||
EventSendConfirmationEmail,
|
||||
} from '@/event/Event'
|
||||
import { getUserCreation } from './util/creations'
|
||||
import { UserRepository } from '@/typeorm/repository/User'
|
||||
import { SearchAdminUsersResult } from '@model/AdminUser'
|
||||
import Paginated from '@arg/Paginated'
|
||||
import { Order } from '@enum/Order'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sodium = require('sodium-native')
|
||||
@ -43,7 +48,7 @@ const isPassword = (password: string): boolean => {
|
||||
return !!password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9 \\t\\n\\r]).{8,}$/)
|
||||
}
|
||||
|
||||
const LANGUAGES = ['de', 'en']
|
||||
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
|
||||
const DEFAULT_LANGUAGE = 'de'
|
||||
const isLanguage = (language: string): boolean => {
|
||||
return LANGUAGES.includes(language)
|
||||
@ -187,7 +192,7 @@ const newEmailOptIn = (userId: number): LoginEmailOptIn => {
|
||||
// if optIn does not exits, it is created
|
||||
export const checkOptInCode = async (
|
||||
optInCode: LoginEmailOptIn | undefined,
|
||||
userId: number,
|
||||
user: DbUser,
|
||||
optInType: OptInType = OptInType.EMAIL_OPT_IN_REGISTER,
|
||||
): Promise<LoginEmailOptIn> => {
|
||||
logger.info(`checkOptInCode... ${optInCode}`)
|
||||
@ -207,15 +212,18 @@ export const checkOptInCode = async (
|
||||
optInCode.updatedAt = new Date()
|
||||
optInCode.resendCount++
|
||||
} else {
|
||||
logger.trace('create new OptIn for userId=' + userId)
|
||||
optInCode = newEmailOptIn(userId)
|
||||
logger.trace('create new OptIn for userId=' + user.id)
|
||||
optInCode = newEmailOptIn(user.id)
|
||||
}
|
||||
|
||||
if (user.emailChecked) {
|
||||
optInCode.emailOptInTypeId = optInType
|
||||
}
|
||||
optInCode.emailOptInTypeId = optInType
|
||||
await LoginEmailOptIn.save(optInCode).catch(() => {
|
||||
logger.error('Unable to save optin code= ' + optInCode)
|
||||
throw new Error('Unable to save optin code.')
|
||||
})
|
||||
logger.debug(`checkOptInCode...successful: ${optInCode} for userid=${userId}`)
|
||||
logger.debug(`checkOptInCode...successful: ${optInCode} for userid=${user.id}`)
|
||||
return optInCode
|
||||
}
|
||||
|
||||
@ -224,6 +232,19 @@ export const activationLink = (optInCode: LoginEmailOptIn): string => {
|
||||
return CONFIG.EMAIL_LINK_SETPASSWORD.replace(/{optin}/g, optInCode.verificationCode.toString())
|
||||
}
|
||||
|
||||
const newGradidoID = async (): Promise<string> => {
|
||||
let gradidoId: string
|
||||
let countIds: number
|
||||
do {
|
||||
gradidoId = uuidv4()
|
||||
countIds = await DbUser.count({ where: { gradidoID: gradidoId } })
|
||||
if (countIds > 0) {
|
||||
logger.info('Gradido-ID creation conflict...')
|
||||
}
|
||||
} while (countIds > 0)
|
||||
return gradidoId
|
||||
}
|
||||
|
||||
@Resolver()
|
||||
export class UserResolver {
|
||||
@Authorized([RIGHTS.VERIFY_LOGIN])
|
||||
@ -344,11 +365,13 @@ export class UserResolver {
|
||||
logger.info(`DbUser.findOne(email=${email}) = ${userFound}`)
|
||||
|
||||
if (userFound) {
|
||||
logger.info('User already exists with this email=' + email)
|
||||
// ATTENTION: this logger-message will be exactly expected during tests
|
||||
logger.info(`User already exists with this email=${email}`)
|
||||
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
|
||||
|
||||
const user = new User(communityDbUser)
|
||||
user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
|
||||
user.gradidoID = uuidv4()
|
||||
user.email = email
|
||||
user.firstName = firstName
|
||||
user.lastName = lastName
|
||||
@ -378,11 +401,13 @@ export class UserResolver {
|
||||
// const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
|
||||
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
|
||||
const emailHash = getEmailHash(email)
|
||||
const gradidoID = await newGradidoID()
|
||||
|
||||
const eventRegister = new EventRegister()
|
||||
const eventRedeemRegister = new EventRedeemRegister()
|
||||
const eventSendConfirmEmail = new EventSendConfirmationEmail()
|
||||
const dbUser = new DbUser()
|
||||
dbUser.gradidoID = gradidoID
|
||||
dbUser.email = email
|
||||
dbUser.firstName = firstName
|
||||
dbUser.lastName = lastName
|
||||
@ -493,7 +518,7 @@ export class UserResolver {
|
||||
userId: user.id,
|
||||
})
|
||||
|
||||
optInCode = await checkOptInCode(optInCode, user.id, OptInType.EMAIL_OPT_IN_RESET_PASSWORD)
|
||||
optInCode = await checkOptInCode(optInCode, user, OptInType.EMAIL_OPT_IN_RESET_PASSWORD)
|
||||
logger.info(`optInCode for ${email}=${optInCode}`)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const emailSent = await sendResetPasswordEmailMailer({
|
||||
@ -731,6 +756,36 @@ export class UserResolver {
|
||||
logger.debug(`has ElopageBuys = ${elopageBuys}`)
|
||||
return elopageBuys
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.SEARCH_ADMIN_USERS])
|
||||
@Query(() => SearchAdminUsersResult)
|
||||
async searchAdminUsers(
|
||||
@Args()
|
||||
{ currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
|
||||
): Promise<SearchAdminUsersResult> {
|
||||
const userRepository = getCustomRepository(UserRepository)
|
||||
|
||||
const [users, count] = await userRepository.findAndCount({
|
||||
where: {
|
||||
isAdmin: Not(IsNull()),
|
||||
},
|
||||
order: {
|
||||
createdAt: order,
|
||||
},
|
||||
skip: (currentPage - 1) * pageSize,
|
||||
take: pageSize,
|
||||
})
|
||||
|
||||
return {
|
||||
userCount: count,
|
||||
userList: users.map((user) => {
|
||||
return {
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isTimeExpired = (optIn: LoginEmailOptIn, duration: number): boolean => {
|
||||
|
||||
@ -2,6 +2,7 @@ import { MiddlewareFn } from 'type-graphql'
|
||||
import { /* klicktippSignIn, */ getKlickTippUser } from '@/apis/KlicktippController'
|
||||
import { KlickTipp } from '@model/KlickTipp'
|
||||
import CONFIG from '@/config'
|
||||
import { klickTippLogger as logger } from '@/server/logger'
|
||||
|
||||
// export const klicktippRegistrationMiddleware: MiddlewareFn = async (
|
||||
// // Only for demo
|
||||
@ -29,7 +30,9 @@ export const klicktippNewsletterStateMiddleware: MiddlewareFn = async (
|
||||
if (klickTippUser) {
|
||||
klickTipp = new KlickTipp(klickTippUser)
|
||||
}
|
||||
} catch (err) {}
|
||||
} catch (err) {
|
||||
logger.error(`There is no user for (email='${result.email}') ${err}`)
|
||||
}
|
||||
}
|
||||
result.klickTipp = klickTipp
|
||||
return result
|
||||
|
||||
@ -280,3 +280,15 @@ export const listContributionLinks = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const searchAdminUsers = gql`
|
||||
query {
|
||||
searchAdminUsers {
|
||||
userCount
|
||||
userList {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -12,7 +12,8 @@ log4js.configure(options)
|
||||
|
||||
const apolloLogger = log4js.getLogger('apollo')
|
||||
const backendLogger = log4js.getLogger('backend')
|
||||
const klickTippLogger = log4js.getLogger('klicktipp')
|
||||
|
||||
backendLogger.addContext('user', 'unknown')
|
||||
|
||||
export { apolloLogger, backendLogger }
|
||||
export { apolloLogger, backendLogger, klickTippLogger }
|
||||
|
||||
@ -6,6 +6,8 @@ import { User } from '@model/User'
|
||||
|
||||
const communityDbUser: dbUser = {
|
||||
id: -1,
|
||||
gradidoID: '11111111-2222-4333-4444-55555555',
|
||||
alias: '',
|
||||
email: 'support@gradido.net',
|
||||
firstName: 'Gradido',
|
||||
lastName: 'Akademie',
|
||||
|
||||
@ -1000,6 +1000,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/uuid@^8.3.4":
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||
|
||||
"@types/validator@^13.1.3":
|
||||
version "13.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.6.3.tgz#31ca2e997bf13a0fffca30a25747d5b9f7dbb7de"
|
||||
@ -5437,7 +5442,7 @@ uuid@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.0.0:
|
||||
uuid@^8.0.0, uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
DeleteDateColumn,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
} from 'typeorm'
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { User } from '../User'
|
||||
|
||||
@Entity('contributions')
|
||||
export class Contribution extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ unsigned: true, nullable: false, name: 'user_id' })
|
||||
userId: number
|
||||
|
||||
@ManyToOne(() => User, (user) => user.contributions)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User
|
||||
|
||||
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ type: 'datetime', nullable: false, name: 'contribution_date' })
|
||||
contributionDate: Date
|
||||
|
||||
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||
memo: string
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
nullable: false,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
amount: Decimal
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'moderator_id' })
|
||||
moderatorId: number
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'contribution_link_id' })
|
||||
contributionLinkId: number
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'confirmed_by' })
|
||||
confirmedBy: number
|
||||
|
||||
@Column({ nullable: true, name: 'confirmed_at' })
|
||||
confirmedAt: Date
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'denied_by' })
|
||||
deniedBy: number
|
||||
|
||||
@Column({ nullable: true, name: 'denied_at' })
|
||||
deniedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'contribution_type',
|
||||
length: 12,
|
||||
nullable: false,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
contributionType: string
|
||||
|
||||
@Column({
|
||||
name: 'contribution_status',
|
||||
length: 12,
|
||||
nullable: false,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
contributionStatus: string
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@DeleteDateColumn({ name: 'deleted_at' })
|
||||
deletedAt: Date | null
|
||||
}
|
||||
111
database/entity/0046-adapt_users_table_for_gradidoid/User.ts
Normal file
111
database/entity/0046-adapt_users_table_for_gradidoid/User.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
DeleteDateColumn,
|
||||
OneToMany,
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from '../Contribution'
|
||||
|
||||
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({
|
||||
name: 'gradido_id',
|
||||
length: 36,
|
||||
nullable: false,
|
||||
unique: true,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
gradidoID: string
|
||||
|
||||
@Column({
|
||||
name: 'alias',
|
||||
length: 20,
|
||||
nullable: true,
|
||||
unique: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
alias: string
|
||||
|
||||
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
|
||||
pubKey: Buffer
|
||||
|
||||
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
|
||||
privKey: Buffer
|
||||
|
||||
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||
email: string
|
||||
|
||||
@Column({
|
||||
name: 'first_name',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
firstName: string
|
||||
|
||||
@Column({
|
||||
name: 'last_name',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
default: null,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
lastName: string
|
||||
|
||||
@DeleteDateColumn()
|
||||
deletedAt: Date | null
|
||||
|
||||
@Column({ type: 'bigint', default: 0, unsigned: true })
|
||||
password: BigInt
|
||||
|
||||
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
|
||||
emailHash: Buffer
|
||||
|
||||
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
|
||||
emailChecked: boolean
|
||||
|
||||
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
|
||||
language: string
|
||||
|
||||
@Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null })
|
||||
isAdmin: Date | null
|
||||
|
||||
@Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null })
|
||||
referrerId?: number | null
|
||||
|
||||
@Column({
|
||||
name: 'contribution_link_id',
|
||||
type: 'int',
|
||||
unsigned: true,
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
contributionLinkId?: number | null
|
||||
|
||||
@Column({ name: 'publisher_id', default: 0 })
|
||||
publisherId: number
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
name: 'passphrase',
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
passphrase: string
|
||||
|
||||
@OneToMany(() => Contribution, (contribution) => contribution.user)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
contributions?: Contribution[]
|
||||
}
|
||||
89
database/entity/0047-messages_tables/Contribution.ts
Normal file
89
database/entity/0047-messages_tables/Contribution.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import Decimal from 'decimal.js-light'
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
DeleteDateColumn,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
} from 'typeorm'
|
||||
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||
import { User } from '../User'
|
||||
import { ContributionMessage } from '../ContributionMessage'
|
||||
|
||||
@Entity('contributions')
|
||||
export class Contribution extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ unsigned: true, nullable: false, name: 'user_id' })
|
||||
userId: number
|
||||
|
||||
@ManyToOne(() => User, (user) => user.contributions)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User
|
||||
|
||||
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ type: 'datetime', nullable: false, name: 'contribution_date' })
|
||||
contributionDate: Date
|
||||
|
||||
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||
memo: string
|
||||
|
||||
@Column({
|
||||
type: 'decimal',
|
||||
precision: 40,
|
||||
scale: 20,
|
||||
nullable: false,
|
||||
transformer: DecimalTransformer,
|
||||
})
|
||||
amount: Decimal
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'moderator_id' })
|
||||
moderatorId: number
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'contribution_link_id' })
|
||||
contributionLinkId: number
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'confirmed_by' })
|
||||
confirmedBy: number
|
||||
|
||||
@Column({ nullable: true, name: 'confirmed_at' })
|
||||
confirmedAt: Date
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'denied_by' })
|
||||
deniedBy: number
|
||||
|
||||
@Column({ nullable: true, name: 'denied_at' })
|
||||
deniedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'contribution_type',
|
||||
length: 12,
|
||||
nullable: false,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
contributionType: string
|
||||
|
||||
@Column({
|
||||
name: 'contribution_status',
|
||||
length: 12,
|
||||
nullable: false,
|
||||
collation: 'utf8mb4_unicode_ci',
|
||||
})
|
||||
contributionStatus: string
|
||||
|
||||
@Column({ unsigned: true, nullable: true, name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@DeleteDateColumn({ name: 'deleted_at' })
|
||||
deletedAt: Date | null
|
||||
|
||||
@OneToMany(() => ContributionMessage, (message) => message.contribution)
|
||||
@JoinColumn({ name: 'contribution_id' })
|
||||
messages?: ContributionMessage[]
|
||||
}
|
||||
46
database/entity/0047-messages_tables/ContributionMessage.ts
Normal file
46
database/entity/0047-messages_tables/ContributionMessage.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from '../Contribution'
|
||||
|
||||
@Entity('contribution_messages', {
|
||||
engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci',
|
||||
})
|
||||
export class ContributionMessage extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'contribution_id', unsigned: true, nullable: false })
|
||||
contributionId: number
|
||||
|
||||
@ManyToOne(() => Contribution, (contribution) => contribution.messages)
|
||||
@JoinColumn({ name: 'contribution_id' })
|
||||
contribution: Contribution
|
||||
|
||||
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
||||
userId: number
|
||||
|
||||
@Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||
message: string
|
||||
|
||||
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' })
|
||||
createdAt: Date
|
||||
|
||||
@Column({ type: 'datetime', default: null, nullable: true, name: 'updated_at' })
|
||||
updatedAt: Date
|
||||
|
||||
@DeleteDateColumn({ name: 'deleted_at' })
|
||||
deletedAt: Date | null
|
||||
|
||||
@Column({ name: 'deleted_by', default: null, unsigned: true, nullable: true })
|
||||
deletedBy: number
|
||||
|
||||
@Column({ length: 12, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||
type: string
|
||||
}
|
||||
@ -1 +1 @@
|
||||
export { Contribution } from './0039-contributions_table/Contribution'
|
||||
export { Contribution } from './0047-messages_tables/Contribution'
|
||||
|
||||
1
database/entity/ContributionMessage.ts
Normal file
1
database/entity/ContributionMessage.ts
Normal file
@ -0,0 +1 @@
|
||||
export { ContributionMessage } from './0047-messages_tables/ContributionMessage'
|
||||
@ -1 +1 @@
|
||||
export { User } from './0040-add_contribution_link_id_to_user/User'
|
||||
export { User } from './0046-adapt_users_table_for_gradidoid/User'
|
||||
|
||||
@ -7,6 +7,7 @@ import { TransactionLink } from './TransactionLink'
|
||||
import { User } from './User'
|
||||
import { Contribution } from './Contribution'
|
||||
import { EventProtocol } from './EventProtocol'
|
||||
import { ContributionMessage } from './ContributionMessage'
|
||||
|
||||
export const entities = [
|
||||
Contribution,
|
||||
@ -18,4 +19,5 @@ export const entities = [
|
||||
TransactionLink,
|
||||
User,
|
||||
EventProtocol,
|
||||
ContributionMessage,
|
||||
]
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
/* MIGRATION TO ADD denied_by, denied_at, contribution_type and contrinution_status
|
||||
FIELDS TO contributions */
|
||||
|
||||
/* 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(
|
||||
'ALTER TABLE `contributions` ADD COLUMN `denied_at` datetime DEFAULT NULL AFTER `confirmed_at`;',
|
||||
)
|
||||
await queryFn(
|
||||
'ALTER TABLE `contributions` ADD COLUMN `denied_by` int(10) unsigned DEFAULT NULL AFTER `denied_at`;',
|
||||
)
|
||||
await queryFn(
|
||||
'ALTER TABLE `contributions` ADD COLUMN `contribution_type` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "ADMIN" AFTER `denied_by`;',
|
||||
)
|
||||
await queryFn(
|
||||
'ALTER TABLE `contributions` ADD COLUMN `contribution_status` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "PENDING" AFTER `contribution_type`;',
|
||||
)
|
||||
await queryFn(
|
||||
'UPDATE `contributions` SET `contribution_type` = "LINK" WHERE `contribution_link_id` IS NOT NULL;',
|
||||
)
|
||||
await queryFn(
|
||||
'UPDATE `contributions` SET `contribution_type` = "USER" WHERE `contribution_link_id` IS NULL AND `moderator_id` IS NULL;',
|
||||
)
|
||||
await queryFn(
|
||||
'UPDATE `contributions` SET `contribution_status` = "CONFIRMED" WHERE `confirmed_at` IS NOT NULL;',
|
||||
)
|
||||
await queryFn(
|
||||
'UPDATE `contributions` SET `contribution_status` = "DELETED" WHERE `deleted_at` IS NOT NULL;',
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn('ALTER TABLE `contributions` DROP COLUMN `contribution_status`;')
|
||||
await queryFn('ALTER TABLE `contributions` DROP COLUMN `contribution_type`;')
|
||||
await queryFn('ALTER TABLE `contributions` DROP COLUMN `denied_by`;')
|
||||
await queryFn('ALTER TABLE `contributions` DROP COLUMN `denied_at`;')
|
||||
}
|
||||
44
database/migrations/0046-adapt_users_table_for_gradidoid.ts
Normal file
44
database/migrations/0046-adapt_users_table_for_gradidoid.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/* MIGRATION TO ADD GRADIDO_ID
|
||||
*
|
||||
* This migration adds new columns to the table `users` and creates the
|
||||
* new table `user_contacts`
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
// First add gradido_id as nullable column without Default
|
||||
await queryFn('ALTER TABLE `users` ADD COLUMN `gradido_id` CHAR(36) NULL AFTER `id`;')
|
||||
|
||||
// Second update gradido_id with ensured unique uuidv4
|
||||
const usersToUpdate = await queryFn('SELECT `id`, `gradido_id` FROM `users`') // WHERE 'u.gradido_id' is null`,)
|
||||
for (const id in usersToUpdate) {
|
||||
const user = usersToUpdate[id]
|
||||
let gradidoId = null
|
||||
let countIds = null
|
||||
do {
|
||||
gradidoId = uuidv4()
|
||||
countIds = await queryFn(
|
||||
`SELECT COUNT(*) FROM \`users\` WHERE \`gradido_id\` = "${gradidoId}"`,
|
||||
)
|
||||
} while (countIds[0] > 0)
|
||||
await queryFn(
|
||||
`UPDATE \`users\` SET \`gradido_id\` = "${gradidoId}" WHERE \`id\` = "${user.id}"`,
|
||||
)
|
||||
}
|
||||
|
||||
// third modify gradido_id to not nullable and unique
|
||||
await queryFn('ALTER TABLE `users` MODIFY COLUMN `gradido_id` CHAR(36) NOT NULL UNIQUE;')
|
||||
|
||||
await queryFn(
|
||||
'ALTER TABLE `users` ADD COLUMN `alias` varchar(20) NULL UNIQUE AFTER `gradido_id`;',
|
||||
)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn('ALTER TABLE users DROP COLUMN gradido_id;')
|
||||
await queryFn('ALTER TABLE users DROP COLUMN alias;')
|
||||
}
|
||||
30
database/migrations/0047-messages_tables.ts
Normal file
30
database/migrations/0047-messages_tables.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* MIGRATION TO CREATE THE MESSAGES TABLES
|
||||
*
|
||||
* This migration creates the `messages` tables in the `community_server` database (`gradido_community`).
|
||||
* This is done to keep all data in the same place and is to be understood in conjunction with the next migration
|
||||
* `0046-messages_tables` which will fill the tables with the existing data
|
||||
*/
|
||||
/* 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(`
|
||||
CREATE TABLE IF NOT EXISTS \`contribution_messages\` (
|
||||
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
\`contribution_id\` int(10) unsigned NOT NULL,
|
||||
\`user_id\` int(10) unsigned NOT NULL,
|
||||
\`message\` varchar(2000) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
\`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
\`updated_at\` datetime DEFAULT NULL,
|
||||
\`deleted_at\` datetime DEFAULT NULL,
|
||||
\`deleted_by\` int(10) unsigned DEFAULT NULL,
|
||||
\`type\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT "DIALOG",
|
||||
PRIMARY KEY (\`id\`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
`)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(`DROP TABLE IF EXISTS \`contribution_messages\`;`)
|
||||
}
|
||||
@ -37,6 +37,7 @@
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto": "^1.0.1",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
@ -44,6 +45,7 @@
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-mysql-migrate": "^1.0.2",
|
||||
"typeorm": "^0.2.38"
|
||||
"typeorm": "^0.2.38",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,6 +137,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5"
|
||||
integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==
|
||||
|
||||
"@types/uuid@^8.3.4":
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||
|
||||
"@types/zen-observable@0.8.3":
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3"
|
||||
@ -2088,6 +2093,11 @@ util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
v8-compile-cache@^2.0.3:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||
|
||||
BIN
docu/Locales/GRADIDO_register_page_spanish.xlsx
Normal file
BIN
docu/Locales/GRADIDO_register_page_spanish.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -23,5 +23,5 @@ module.exports = {
|
||||
testMatch: ['**/?(*.)+(spec|test).js?(x)'],
|
||||
// snapshotSerializers: ['jest-serializer-vue'],
|
||||
transformIgnorePatterns: ['<rootDir>/node_modules/(?!vee-validate/dist/rules)'],
|
||||
testEnvironment: 'jest-environment-jsdom-sixteen',
|
||||
// testEnvironment: 'jest-environment-jsdom-sixteen', // not needed anymore since jest@26, see: https://www.npmjs.com/package/jest-environment-jsdom-sixteen
|
||||
}
|
||||
|
||||
@ -44,7 +44,6 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom-sixteen": "^2.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"portal-vue": "^2.1.7",
|
||||
"prettier": "^2.2.1",
|
||||
|
||||
@ -46,46 +46,274 @@ describe('ContributionForm', () => {
|
||||
})
|
||||
|
||||
describe('empty form data', () => {
|
||||
describe('buttons', () => {
|
||||
it('has reset enabled', () => {
|
||||
expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy()
|
||||
describe('button', () => {
|
||||
it('has submit disabled', () => {
|
||||
expect(wrapper.find('button[data-test="button-submit"]').attributes('disabled')).toBe(
|
||||
'disabled',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('dates', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({
|
||||
form: {
|
||||
id: null,
|
||||
date: '',
|
||||
memo: '',
|
||||
amount: '',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('actual date', () => {
|
||||
describe('same month', () => {
|
||||
beforeEach(async () => {
|
||||
const now = new Date().toISOString()
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has true', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('has submit disabled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
|
||||
describe('month before', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.findComponent({ name: 'BFormDatepicker' })
|
||||
.vm.$emit('input', wrapper.vm.minimalDate)
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has false', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('date in middle of year', () => {
|
||||
describe('same month', () => {
|
||||
beforeEach(async () => {
|
||||
// jest.useFakeTimers('modern')
|
||||
// jest.setSystemTime(new Date('2020-07-06'))
|
||||
// await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper.setData({
|
||||
maximalDate: new Date(2020, 6, 6),
|
||||
form: { date: new Date(2020, 6, 6) },
|
||||
})
|
||||
})
|
||||
|
||||
describe('minimalDate', () => {
|
||||
it('has "2020-06-01T00:00:00.000Z"', () => {
|
||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has true', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('month before', () => {
|
||||
beforeEach(async () => {
|
||||
// jest.useFakeTimers('modern')
|
||||
// jest.setSystemTime(new Date('2020-07-06'))
|
||||
// console.log('middle of year date – now:', wrapper.vm.minimalDate)
|
||||
// await wrapper
|
||||
// .findComponent({ name: 'BFormDatepicker' })
|
||||
// .vm.$emit('input', wrapper.vm.minimalDate)
|
||||
await wrapper.setData({
|
||||
maximalDate: new Date(2020, 6, 6),
|
||||
form: { date: new Date(2020, 5, 6) },
|
||||
})
|
||||
})
|
||||
|
||||
describe('minimalDate', () => {
|
||||
it('has "2020-06-01T00:00:00.000Z"', () => {
|
||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2020-06-01T00:00:00.000Z')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has false', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('date in january', () => {
|
||||
describe('same month', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({
|
||||
maximalDate: new Date(2020, 0, 6),
|
||||
form: { date: new Date(2020, 0, 6) },
|
||||
})
|
||||
})
|
||||
|
||||
describe('minimalDate', () => {
|
||||
it('has "2019-12-01T00:00:00.000Z"', () => {
|
||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has true', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('month before', () => {
|
||||
beforeEach(async () => {
|
||||
// jest.useFakeTimers('modern')
|
||||
// jest.setSystemTime(new Date('2020-07-06'))
|
||||
// console.log('middle of year date – now:', wrapper.vm.minimalDate)
|
||||
// await wrapper
|
||||
// .findComponent({ name: 'BFormDatepicker' })
|
||||
// .vm.$emit('input', wrapper.vm.minimalDate)
|
||||
await wrapper.setData({
|
||||
maximalDate: new Date(2020, 0, 6),
|
||||
form: { date: new Date(2019, 11, 6) },
|
||||
})
|
||||
})
|
||||
|
||||
describe('minimalDate', () => {
|
||||
it('has "2019-12-01T00:00:00.000Z"', () => {
|
||||
expect(wrapper.vm.minimalDate.toISOString()).toBe('2019-12-01T00:00:00.000Z')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isThisMonth', () => {
|
||||
it('has false', () => {
|
||||
expect(wrapper.vm.isThisMonth).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('set contrubtion', () => {
|
||||
describe('fill in form data', () => {
|
||||
describe('fill in form data with "id === null"', () => {
|
||||
const now = new Date().toISOString()
|
||||
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({
|
||||
form: {
|
||||
id: null,
|
||||
date: now,
|
||||
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
||||
amount: '200',
|
||||
date: '',
|
||||
memo: '',
|
||||
amount: '',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('buttons', () => {
|
||||
it('has reset enabled', () => {
|
||||
expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy()
|
||||
describe('invalid form data', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper.find('#contribution-amount').find('input').setValue('200')
|
||||
})
|
||||
|
||||
it('has submit enabled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy()
|
||||
describe('memo lenght < 5, others are valid', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('#contribution-memo').find('textarea').setValue('1234')
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has disabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBe('disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('memo lenght > 255, others are valid', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.find('#contribution-memo')
|
||||
.find('textarea')
|
||||
.setValue(
|
||||
'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' +
|
||||
'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' +
|
||||
'01234567890123456789012345678901234567890123456789012345',
|
||||
)
|
||||
await wrapper.vm.$nextTick()
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has disabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBe('disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid form data', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper
|
||||
.find('#contribution-memo')
|
||||
.find('textarea')
|
||||
.setValue('Mein Beitrag zur Gemeinschaft für diesen Monat ...')
|
||||
await wrapper.find('#contribution-amount').find('input').setValue('200')
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has enabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBeFalsy()
|
||||
})
|
||||
|
||||
it('has label "contribution.submit"', () => {
|
||||
expect(wrapper.find('button[data-test="button-submit"]').text()).toContain(
|
||||
'contribution.submit',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('on trigger submit', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
it('emits "set-contribution"', () => {
|
||||
expect(wrapper.emitted('set-contribution')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.arrayContaining([
|
||||
{
|
||||
id: null,
|
||||
date: now,
|
||||
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
||||
amount: '200',
|
||||
},
|
||||
]),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('update contrubtion', () => {
|
||||
describe('fill in form data and "id"', () => {
|
||||
describe('fill in form data with set "id"', () => {
|
||||
const now = new Date().toISOString()
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -93,19 +321,104 @@ describe('ContributionForm', () => {
|
||||
form: {
|
||||
id: 2,
|
||||
date: now,
|
||||
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
||||
amount: '200',
|
||||
memo: 'Mein kommerzieller Beitrag für diesen Monat ...',
|
||||
amount: '100',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('buttons', () => {
|
||||
it('has reset enabled', () => {
|
||||
expect(wrapper.find('button[type="reset"]').attributes('disabled')).toBeFalsy()
|
||||
describe('invalid form data', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper.find('#contribution-amount').find('input').setValue('200')
|
||||
})
|
||||
|
||||
it('has submit enabled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBeFalsy()
|
||||
describe('memo lenght < 5, others are valid', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('#contribution-memo').find('textarea').setValue('1234')
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has disabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBe('disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('memo lenght > 255, others are valid', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.find('#contribution-memo')
|
||||
.find('textarea')
|
||||
.setValue(
|
||||
'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' +
|
||||
'0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' +
|
||||
'01234567890123456789012345678901234567890123456789012345',
|
||||
)
|
||||
await wrapper.vm.$nextTick()
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has disabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBe('disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid form data', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper
|
||||
.find('#contribution-memo')
|
||||
.find('textarea')
|
||||
.setValue('Mein Beitrag zur Gemeinschaft für diesen Monat ...')
|
||||
await wrapper.find('#contribution-amount').find('input').setValue('200')
|
||||
})
|
||||
|
||||
describe('button', () => {
|
||||
describe('submit', () => {
|
||||
it('has enabled', () => {
|
||||
expect(
|
||||
wrapper.find('button[data-test="button-submit"]').attributes('disabled'),
|
||||
).toBeFalsy()
|
||||
})
|
||||
|
||||
it('has label "form.change"', () => {
|
||||
expect(wrapper.find('button[data-test="button-submit"]').text()).toContain(
|
||||
'form.change',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('on trigger submit', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
it('emits "update-contribution"', () => {
|
||||
expect(wrapper.emitted('update-contribution')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.arrayContaining([
|
||||
{
|
||||
id: 2,
|
||||
date: now,
|
||||
memo: 'Mein Beitrag zur Gemeinschaft für diesen Monat ...',
|
||||
amount: '200',
|
||||
},
|
||||
]),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
<h3>{{ $t('contribution.formText.yourContribution') }}</h3>
|
||||
{{ $t('contribution.formText.bringYourTalentsTo') }}
|
||||
<ul class="my-3">
|
||||
<li v-html="lastMonthObject"></li>
|
||||
<li v-html="thisMonthObject"></li>
|
||||
<li v-html="textForMonth(new Date(minimalDate), maxGddLastMonth)"></li>
|
||||
<li v-html="textForMonth(new Date(), maxGddThisMonth)"></li>
|
||||
</ul>
|
||||
|
||||
<div class="my-3">
|
||||
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<b-form ref="form" @submit.prevent="submit" class="border p-3">
|
||||
<label>{{ $t('contribution.selectDate') }}</label>
|
||||
<label>{{ $t('contribution.selectDate') }} {{ $t('math.asterisk') }}</label>
|
||||
<b-form-datepicker
|
||||
id="contribution-date"
|
||||
v-model="form.date"
|
||||
@ -31,33 +31,32 @@
|
||||
</b-form-datepicker>
|
||||
<validation-provider
|
||||
:rules="{
|
||||
required: true,
|
||||
min: minlength,
|
||||
max: maxlength,
|
||||
}"
|
||||
:name="$t('form.message')"
|
||||
v-slot="{ errors }"
|
||||
>
|
||||
<label class="mt-3">{{ $t('contribution.activity') }}</label>
|
||||
<label class="mt-3">{{ $t('contribution.activity') }} {{ $t('math.asterisk') }}</label>
|
||||
<b-form-textarea
|
||||
id="contribution-memo"
|
||||
v-model="form.memo"
|
||||
rows="3"
|
||||
max-rows="6"
|
||||
:placeholder="$t('contribution.yourActivity')"
|
||||
required
|
||||
></b-form-textarea>
|
||||
<b-col v-if="errors">
|
||||
<span v-for="error in errors" class="errors" :key="error">{{ error }}</span>
|
||||
</b-col>
|
||||
</validation-provider>
|
||||
<label class="mt-3">{{ $t('form.amount') }}</label>
|
||||
<b-input-group size="lg" prepend="GDD" append=".00">
|
||||
<label class="mt-3">{{ $t('form.amount') }} {{ $t('math.asterisk') }}</label>
|
||||
<b-input-group size="lg" prepend="GDD">
|
||||
<b-form-input
|
||||
id="contribution-amount"
|
||||
v-model="form.amount"
|
||||
type="number"
|
||||
min="1"
|
||||
:max="isThisMonth ? maxGddThisMonth : maxGddLastMonth"
|
||||
type="text"
|
||||
:formatter="numberFormat"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
<div
|
||||
@ -74,17 +73,18 @@
|
||||
</div>
|
||||
<b-row class="mt-3">
|
||||
<b-col>
|
||||
<b-button class="test-cancel" type="reset" variant="secondary" @click="reset">
|
||||
<b-button type="reset" variant="secondary" @click="reset" data-test="button-cancel">
|
||||
{{ $t('form.cancel') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button class="test-submit" type="submit" variant="primary" :disabled="disabled">
|
||||
<b-button type="submit" variant="primary" :disabled="disabled" data-test="button-submit">
|
||||
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
<p class="p-2">{{ $t('math.asterisk') }} {{ $t('form.mandatoryField') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -103,12 +103,13 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
numberFormat(value) {
|
||||
return value.replace(/\D/g, '')
|
||||
},
|
||||
submit() {
|
||||
if (this.form.id) {
|
||||
this.$emit('update-contribution', this.form)
|
||||
} else {
|
||||
this.$emit('set-contribution', this.form)
|
||||
}
|
||||
this.form.amount = this.numberFormat(this.form.amount)
|
||||
// spreading is needed for testing
|
||||
this.$emit(this.form.id ? 'update-contribution' : 'set-contribution', { ...this.form })
|
||||
this.reset()
|
||||
},
|
||||
reset() {
|
||||
@ -118,53 +119,47 @@ export default {
|
||||
this.form.memo = ''
|
||||
this.form.amount = ''
|
||||
},
|
||||
textForMonth(date, availableAmount) {
|
||||
const obj = {
|
||||
monthAndYear: this.$d(date, 'monthAndYear'),
|
||||
creation: availableAmount,
|
||||
}
|
||||
return this.$t('contribution.formText.openAmountForMonth', obj)
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/*
|
||||
* minimalDate() = Sets the date to the 1st of the previous month.
|
||||
*
|
||||
*/
|
||||
minimalDate() {
|
||||
return new Date(this.maximalDate.getFullYear(), this.maximalDate.getMonth() - 1, 1)
|
||||
// sets the date to the 1st of the previous month
|
||||
let date = new Date(this.maximalDate) // has to be a new object, because of 'setMonth' changes the objects date
|
||||
date = new Date(date.setMonth(date.getMonth() - 1))
|
||||
return new Date(date.getFullYear(), date.getMonth(), 1)
|
||||
},
|
||||
disabled() {
|
||||
if (
|
||||
return (
|
||||
this.form.date === '' ||
|
||||
this.form.memo.length < this.minlength ||
|
||||
this.form.amount <= 0 ||
|
||||
this.form.amount > 1000 ||
|
||||
this.form.memo.length > this.maxlength ||
|
||||
parseInt(this.form.amount) <= 0 ||
|
||||
parseInt(this.form.amount) > 1000 ||
|
||||
(this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddThisMonth)) ||
|
||||
(!this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddLastMonth))
|
||||
)
|
||||
return true
|
||||
return false
|
||||
},
|
||||
lastMonthObject() {
|
||||
// new Date().getMonth === 1 If the current month is January, then one year must be gone back in the previous month
|
||||
const obj = {
|
||||
monthAndYear: this.$d(new Date(this.minimalDate), 'monthAndYear'),
|
||||
creation: this.maxGddLastMonth,
|
||||
}
|
||||
return this.$t('contribution.formText.openAmountForMonth', obj)
|
||||
},
|
||||
thisMonthObject() {
|
||||
const obj = {
|
||||
monthAndYear: this.$d(new Date(), 'monthAndYear'),
|
||||
creation: this.maxGddThisMonth,
|
||||
}
|
||||
return this.$t('contribution.formText.openAmountForMonth', obj)
|
||||
},
|
||||
isThisMonth() {
|
||||
return new Date(this.form.date).getMonth() === new Date().getMonth()
|
||||
const formDate = new Date(this.form.date)
|
||||
return (
|
||||
formDate.getFullYear() === this.maximalDate.getFullYear() &&
|
||||
formDate.getMonth() === this.maximalDate.getMonth()
|
||||
)
|
||||
},
|
||||
maxGddLastMonth() {
|
||||
// When edited, the amount is added back on top of the amount
|
||||
// when existing contribution is edited, the amount is added back on top of the amount
|
||||
return this.form.id && !this.isThisMonth
|
||||
? parseInt(this.$store.state.creation[1]) + parseInt(this.updateAmount)
|
||||
: this.$store.state.creation[1]
|
||||
},
|
||||
maxGddThisMonth() {
|
||||
// When edited, the amount is added back on top of the amount
|
||||
// when existing contribution is edited, the amount is added back on top of the amount
|
||||
return this.form.id && this.isThisMonth
|
||||
? parseInt(this.$store.state.creation[2]) + parseInt(this.updateAmount)
|
||||
: this.$store.state.creation[2]
|
||||
|
||||
@ -46,5 +46,17 @@ describe('GddSend confirm', () => {
|
||||
expect(wrapper.findAll('div.confirm-box-link').at(0).exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('has totalBalance under 0', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setProps({
|
||||
balance: 0,
|
||||
})
|
||||
})
|
||||
|
||||
it('has send button disabled', () => {
|
||||
expect(wrapper.find('.send-button').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -42,7 +42,12 @@
|
||||
<b-button @click="$emit('on-reset')">{{ $t('back') }}</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button variant="primary" :disabled="disabled" @click="$emit('send-transaction')">
|
||||
<b-button
|
||||
class="send-button"
|
||||
variant="primary"
|
||||
:disabled="disabled"
|
||||
@click="$emit('send-transaction')"
|
||||
>
|
||||
{{ $t('form.generate_now') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
|
||||
@ -45,7 +45,7 @@ describe('LanguageSwitch', () => {
|
||||
expect(wrapper.find('div.language-switch').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('with locales en and de', () => {
|
||||
describe('with locales en, de and es', () => {
|
||||
describe('empty store', () => {
|
||||
describe('navigator language is "en-US"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
@ -69,11 +69,44 @@ describe('LanguageSwitch', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigator language is "es-ES" (not supported)', () => {
|
||||
describe('navigator language is "es-ES"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
|
||||
it('shows Español as language ', async () => {
|
||||
languageGetter.mockReturnValue('es-ES')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Español - es')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigator language is "fr-FR"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
|
||||
it('shows French as language ', async () => {
|
||||
languageGetter.mockReturnValue('fr-FR')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Français - fr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigator language is "nl-NL"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
|
||||
it('shows Dutch as language ', async () => {
|
||||
languageGetter.mockReturnValue('nl-NL')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigator language is "it-IT" (not supported)', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
|
||||
it('shows English as language ', async () => {
|
||||
languageGetter.mockReturnValue('es-ES')
|
||||
languageGetter.mockReturnValue('it-IT')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('English - en')
|
||||
@ -101,9 +134,36 @@ describe('LanguageSwitch', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('language "es" in store', () => {
|
||||
it('shows Español as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'es'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Español - es')
|
||||
})
|
||||
})
|
||||
|
||||
describe('language "fr" in store', () => {
|
||||
it('shows French as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'fr'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Français - fr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('language "nl" in store', () => {
|
||||
it('shows Dutch as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'nl'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('button.dropdown-toggle').text()).toBe('Holandés - nl')
|
||||
})
|
||||
})
|
||||
|
||||
describe('dropdown menu', () => {
|
||||
it('has English and German as languages to choose', () => {
|
||||
expect(wrapper.findAll('li')).toHaveLength(2)
|
||||
expect(wrapper.findAll('li')).toHaveLength(5)
|
||||
})
|
||||
|
||||
it('has English as first language to choose', () => {
|
||||
@ -113,6 +173,18 @@ describe('LanguageSwitch', () => {
|
||||
it('has German as second language to choose', () => {
|
||||
expect(wrapper.findAll('li').at(1).text()).toBe('Deutsch')
|
||||
})
|
||||
|
||||
it('has Español as second language to choose', () => {
|
||||
expect(wrapper.findAll('li').at(2).text()).toBe('Español')
|
||||
})
|
||||
|
||||
it('has French as second language to choose', () => {
|
||||
expect(wrapper.findAll('li').at(3).text()).toBe('Français')
|
||||
})
|
||||
|
||||
it('has Dutch as second language to choose', () => {
|
||||
expect(wrapper.findAll('li').at(4).text()).toBe('Holandés')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -138,6 +210,39 @@ describe('LanguageSwitch', () => {
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("with locale 'es'", () => {
|
||||
wrapper.findAll('li').at(2).find('a').trigger('click')
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
locale: 'es',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("with locale 'fr'", () => {
|
||||
wrapper.findAll('li').at(3).find('a').trigger('click')
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
locale: 'fr',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("with locale 'nl'", () => {
|
||||
wrapper.findAll('li').at(4).find('a').trigger('click')
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
locale: 'nl',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -66,10 +66,37 @@ describe('LanguageSwitch', () => {
|
||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
||||
})
|
||||
})
|
||||
describe('navigator language is "es-ES" (not supported)', () => {
|
||||
describe('navigator language is "es-ES"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
it('shows Español as language ', async () => {
|
||||
languageGetter.mockReturnValue('es-ES')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Español')
|
||||
})
|
||||
})
|
||||
describe('navigator language is "fr-FR"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
it('shows French as language ', async () => {
|
||||
languageGetter.mockReturnValue('fr-FR')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Français')
|
||||
})
|
||||
})
|
||||
describe('navigator language is "nl-NL"', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
it('shows Dutch as language ', async () => {
|
||||
languageGetter.mockReturnValue('nl-NL')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Holandés')
|
||||
})
|
||||
})
|
||||
describe('navigator language is "it-IT" (not supported)', () => {
|
||||
const languageGetter = jest.spyOn(navigator, 'language', 'get')
|
||||
it('shows English as language ', async () => {
|
||||
languageGetter.mockReturnValue('es-ES')
|
||||
languageGetter.mockReturnValue('it-IT')
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
||||
@ -90,44 +117,62 @@ describe('LanguageSwitch', () => {
|
||||
wrapper.vm.$store.state.language = 'de'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('English')
|
||||
})
|
||||
})
|
||||
describe('language "es" in store', () => {
|
||||
it('shows Español as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'es'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch')
|
||||
})
|
||||
})
|
||||
describe('language "fr" in store', () => {
|
||||
it('shows French as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'fr'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español')
|
||||
})
|
||||
})
|
||||
describe('language "nl" in store', () => {
|
||||
it('shows Dutch as language', async () => {
|
||||
wrapper.vm.$store.state.language = 'nl'
|
||||
wrapper.vm.setCurrentLanguage()
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français')
|
||||
})
|
||||
})
|
||||
describe('language menu', () => {
|
||||
it('has English and German as languages to choose', () => {
|
||||
expect(wrapper.findAll('span.locales')).toHaveLength(2)
|
||||
it('has English, German and Español as languages to choose', () => {
|
||||
expect(wrapper.findAll('span.locales')).toHaveLength(5)
|
||||
})
|
||||
it('has English as first language to choose', () => {
|
||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('English')
|
||||
expect(wrapper.findAll('span.locales').at(0).text()).toBe('Holandés')
|
||||
})
|
||||
it('has German as second language to choose', () => {
|
||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('Deutsch')
|
||||
expect(wrapper.findAll('span.locales').at(1).text()).toBe('English')
|
||||
})
|
||||
it('has Español as third language to choose', () => {
|
||||
expect(wrapper.findAll('span.locales').at(2).text()).toBe('Deutsch')
|
||||
})
|
||||
it('has French as third language to choose', () => {
|
||||
expect(wrapper.findAll('span.locales').at(3).text()).toBe('Español')
|
||||
})
|
||||
it('has Dutch as third language to choose', () => {
|
||||
expect(wrapper.findAll('span.locales').at(4).text()).toBe('Français')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('calls the API', () => {
|
||||
it("with locale 'de'", () => {
|
||||
wrapper.findAll('span.locales').at(1).trigger('click')
|
||||
wrapper.findAll('span.locales').at(2).trigger('click')
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
locale: 'de',
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({ variables: { locale: 'de' } }),
|
||||
)
|
||||
})
|
||||
|
||||
// it("with locale 'en'", () => {
|
||||
// wrapper.findAll('span.locales').at(0).trigger('click')
|
||||
// expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
// expect.objectContaining({
|
||||
// variables: {
|
||||
// locale: 'en',
|
||||
// },
|
||||
// }),
|
||||
// )
|
||||
// })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,15 +1,37 @@
|
||||
<template>
|
||||
<div class="language-switch">
|
||||
<span
|
||||
v-for="(lang, index) in locales"
|
||||
@click.prevent="saveLocale(lang.code)"
|
||||
v-for="lang in locales"
|
||||
:key="lang.code"
|
||||
class="pointer pr-2"
|
||||
class="pointer"
|
||||
:class="$store.state.language === lang.code ? 'c-grey' : 'c-blau'"
|
||||
>
|
||||
<span class="locales">{{ lang.name }}</span>
|
||||
<span class="ml-3">{{ locales.length - 1 > index ? $t('math.pipe') : '' }}</span>
|
||||
<span v-if="lang.code === $store.state.language" class="locales mr-1">{{ lang.name }}</span>
|
||||
</span>
|
||||
<b-icon v-b-toggle.collapse-1 icon="caret-down-fill" aria-hidden="true"></b-icon>
|
||||
<b-collapse id="collapse-1" class="mt-4">
|
||||
<span
|
||||
v-for="(lang, index) in locales"
|
||||
@click.prevent="saveLocale(lang.code)"
|
||||
:key="lang.code"
|
||||
class="pointer"
|
||||
:class="$store.state.language === lang.code ? 'c-grey' : 'c-blau'"
|
||||
>
|
||||
<span v-if="lang.code !== $store.state.language" v-b-toggle.collapse-1 class="locales">
|
||||
{{ lang.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="
|
||||
lang.code !== $store.state.language &&
|
||||
(indexOfSelectedLocale !== indexOfLastLocale ||
|
||||
(indexOfSelectedLocale === indexOfLastLocale && index !== indexOfSecondLastLocale))
|
||||
"
|
||||
class="ml-3 mr-3"
|
||||
>
|
||||
{{ locales.length - 1 > index ? $t('math.pipe') : '' }}
|
||||
</span>
|
||||
</span>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -68,6 +90,17 @@ export default {
|
||||
this.currentLanguage = object
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
indexOfSelectedLocale() {
|
||||
return this.locales.findIndex((element) => element.code === this.$store.state.language)
|
||||
},
|
||||
indexOfSecondLastLocale() {
|
||||
return this.locales.length - 2
|
||||
},
|
||||
indexOfLastLocale() {
|
||||
return this.locales.length - 1
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setCurrentLanguage()
|
||||
},
|
||||
|
||||
@ -16,6 +16,9 @@ export default {
|
||||
options: [
|
||||
{ value: 'de', text: this.$t('settings.language.de') },
|
||||
{ value: 'en', text: this.$t('settings.language.en') },
|
||||
{ value: 'es', text: this.$t('settings.language.es') },
|
||||
{ value: 'fr', text: this.$t('settings.language.fr') },
|
||||
{ value: 'nl', text: this.$t('settings.language.nl') },
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
@ -48,49 +48,57 @@ describe('Navbar', () => {
|
||||
expect(wrapper.find('.navbar-toggler').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has ten b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(11)
|
||||
it('has thirteen b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(13)
|
||||
})
|
||||
|
||||
it('has first nav-item "amount GDD" in navbar', () => {
|
||||
it('has nav-item "amount GDD" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(1).text()).toEqual('1234 GDD')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.overview" in navbar', () => {
|
||||
it('has nav-item "navigation.overview" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(3).text()).toEqual('navigation.overview')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.send" in navbar', () => {
|
||||
it('has nav-item "navigation.send" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(4).text()).toEqual('navigation.send')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.transactions" in navbar', () => {
|
||||
it('has nav-item "navigation.transactions" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.transactions')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.transactions" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.community')
|
||||
it('has nav-item "gdt.gdt" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('gdt.gdt')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.profile" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.profile')
|
||||
it('has nav-item "navigation.community" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.community')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.profile" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.profile')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.info" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.info')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigation Navbar (user has an elopage account)', () => {
|
||||
it('has a link to the members area', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toContain('navigation.members_area')
|
||||
expect(wrapper.findAll('.nav-item').at(8).find('a').attributes('href')).toBe(
|
||||
expect(wrapper.findAll('.nav-item').at(10).text()).toContain('navigation.members_area')
|
||||
expect(wrapper.findAll('.nav-item').at(10).find('a').attributes('href')).toBe(
|
||||
'https://elopage.com',
|
||||
)
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.admin_area')
|
||||
it('has nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(11).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(10).text()).toEqual('navigation.logout')
|
||||
it('has nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(12).text()).toEqual('navigation.logout')
|
||||
})
|
||||
})
|
||||
|
||||
@ -100,12 +108,12 @@ describe('Navbar', () => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.admin_area')
|
||||
it('has nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(10).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.logout')
|
||||
it('has nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(11).text()).toEqual('navigation.logout')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -57,6 +57,10 @@
|
||||
<b-icon icon="layout-text-sidebar-reverse" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.transactions') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/gdt" class="mb-3">
|
||||
<b-icon icon="layout-text-sidebar-reverse" aria-hidden="true"></b-icon>
|
||||
{{ $t('gdt.gdt') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/community" class="mb-3">
|
||||
<b-icon icon="people" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.community') }}
|
||||
@ -65,6 +69,10 @@
|
||||
<b-icon icon="gear" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.profile') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/information" class="mb-3">
|
||||
<b-icon icon="info-circle" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.info') }}
|
||||
</b-nav-item>
|
||||
<br />
|
||||
<b-nav-item v-if="$store.state.hasElopage" :href="elopageUri" class="mb-3" target="_blank">
|
||||
<b-icon icon="link45deg" aria-hidden="true"></b-icon>
|
||||
|
||||
@ -32,44 +32,62 @@ describe('Sidebar', () => {
|
||||
expect(wrapper.find('div#component-sidebar').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('navigation Navbar (general elements)', () => {
|
||||
it('has first nav-item "navigation.overview" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(0).text()).toEqual('navigation.overview')
|
||||
describe('navigation Navbar', () => {
|
||||
it('has ten b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(10)
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.send" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(1).text()).toEqual('navigation.send')
|
||||
describe('navigation Navbar (general elements)', () => {
|
||||
it('has nav-item "navigation.overview" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(0).text()).toEqual('navigation.overview')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.send" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(1).text()).toEqual('navigation.send')
|
||||
})
|
||||
|
||||
it('has nav-item "gdt.gdt" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(3).text()).toEqual('gdt.gdt')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.community" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(4).text()).toContain('navigation.community')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.profile" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.profile')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.info" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.info')
|
||||
})
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.transactions" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(2).text()).toEqual('navigation.transactions')
|
||||
describe('navigation Navbar (user has an elopage account)', () => {
|
||||
it('has ten b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(10)
|
||||
})
|
||||
|
||||
it('has a link to the members area', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.members_area')
|
||||
expect(wrapper.findAll('.nav-item').at(7).find('a').attributes('href')).toBe('#')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.logout')
|
||||
})
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.community" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(3).text()).toContain('navigation.community')
|
||||
it('has nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.profile" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(4).text()).toEqual('navigation.profile')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigation Navbar (user has an elopage account)', () => {
|
||||
it('has eight b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(8)
|
||||
})
|
||||
|
||||
it('has a link to the members area', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.members_area')
|
||||
expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('#')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.logout')
|
||||
it('has nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(9).text()).toEqual('navigation.logout')
|
||||
})
|
||||
})
|
||||
|
||||
@ -79,16 +97,16 @@ describe('Sidebar', () => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has seven b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(7)
|
||||
it('has nine b-nav-item in the navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item')).toHaveLength(9)
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(5).text()).toEqual('navigation.admin_area')
|
||||
it('has nav-item "navigation.admin_area" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(7).text()).toEqual('navigation.admin_area')
|
||||
})
|
||||
|
||||
it('has first nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(6).text()).toEqual('navigation.logout')
|
||||
it('has nav-item "navigation.logout" in navbar', () => {
|
||||
expect(wrapper.findAll('.nav-item').at(8).text()).toEqual('navigation.logout')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -16,6 +16,10 @@
|
||||
<b-icon icon="layout-text-sidebar-reverse" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.transactions') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/gdt" class="mb-3">
|
||||
<b-icon icon="layout-text-sidebar-reverse" aria-hidden="true"></b-icon>
|
||||
{{ $t('gdt.gdt') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/community" class="mb-3">
|
||||
<b-icon icon="people" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.community') }}
|
||||
@ -24,6 +28,10 @@
|
||||
<b-icon icon="gear" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.profile') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/information" class="mb-3">
|
||||
<b-icon icon="info-circle" aria-hidden="true"></b-icon>
|
||||
{{ $t('navigation.info') }}
|
||||
</b-nav-item>
|
||||
</b-nav>
|
||||
<hr />
|
||||
<b-nav vertical class="w-100">
|
||||
|
||||
@ -163,6 +163,26 @@ export const listTransactionLinks = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const listContributionLinks = gql`
|
||||
query($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
|
||||
listContributionLinks(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
links {
|
||||
id
|
||||
amount
|
||||
name
|
||||
memo
|
||||
createdAt
|
||||
validFrom
|
||||
validTo
|
||||
maxAmountPerMonth
|
||||
cycle
|
||||
maxPerCycle
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const listContributions = gql`
|
||||
query(
|
||||
$currentPage: Int = 1
|
||||
@ -209,3 +229,29 @@ export const listAllContributions = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const communityStatistics = gql`
|
||||
query {
|
||||
communityStatistics {
|
||||
totalUsers
|
||||
activeUsers
|
||||
deletedUsers
|
||||
totalGradidoCreated
|
||||
totalGradidoDecayed
|
||||
totalGradidoAvailable
|
||||
totalGradidoUnbookedDecayed
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const searchAdminUsers = gql`
|
||||
query {
|
||||
searchAdminUsers {
|
||||
userCount
|
||||
userList {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
||||
import en from 'vee-validate/dist/locale/en'
|
||||
import de from 'vee-validate/dist/locale/de'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
function loadLocaleMessages() {
|
||||
@ -13,18 +10,9 @@ function loadLocaleMessages() {
|
||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
|
||||
if (matched && matched.length > 1) {
|
||||
const locale = matched[1]
|
||||
messages[locale] = locales(key)
|
||||
if (locale === 'de') {
|
||||
messages[locale] = {
|
||||
validations: de,
|
||||
...messages[locale],
|
||||
}
|
||||
}
|
||||
if (locale === 'en') {
|
||||
messages[locale] = {
|
||||
validations: en,
|
||||
...messages[locale],
|
||||
}
|
||||
messages[locale] = {
|
||||
validations: require(`vee-validate/dist/locale/${locale}`),
|
||||
...locales(key),
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -58,6 +46,45 @@ const numberFormats = {
|
||||
useGrouping: false,
|
||||
},
|
||||
},
|
||||
es: {
|
||||
decimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
},
|
||||
ungroupedDecimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
useGrouping: false,
|
||||
},
|
||||
},
|
||||
fr: {
|
||||
decimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
},
|
||||
ungroupedDecimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
useGrouping: false,
|
||||
},
|
||||
},
|
||||
nl: {
|
||||
decimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
},
|
||||
ungroupedDecimal: {
|
||||
style: 'decimal',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
useGrouping: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const dateTimeFormats = {
|
||||
@ -117,6 +144,90 @@ const dateTimeFormats = {
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
es: {
|
||||
short: {
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
year: 'numeric',
|
||||
},
|
||||
long: {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
},
|
||||
monthShort: {
|
||||
month: 'short',
|
||||
},
|
||||
month: {
|
||||
month: 'long',
|
||||
},
|
||||
year: {
|
||||
year: 'numeric',
|
||||
},
|
||||
monthAndYear: {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
fr: {
|
||||
short: {
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
year: 'numeric',
|
||||
},
|
||||
long: {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
},
|
||||
monthShort: {
|
||||
month: 'short',
|
||||
},
|
||||
month: {
|
||||
month: 'long',
|
||||
},
|
||||
year: {
|
||||
year: 'numeric',
|
||||
},
|
||||
monthAndYear: {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
nl: {
|
||||
short: {
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
year: 'numeric',
|
||||
},
|
||||
long: {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
},
|
||||
monthShort: {
|
||||
month: 'short',
|
||||
},
|
||||
month: {
|
||||
month: 'long',
|
||||
},
|
||||
year: {
|
||||
year: 'numeric',
|
||||
},
|
||||
monthAndYear: {
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default new VueI18n({
|
||||
|
||||
@ -26,8 +26,13 @@
|
||||
"community": "Gemeinschaft",
|
||||
"continue-to-registration": "Weiter zur Registrierung",
|
||||
"current-community": "Aktuelle Gemeinschaft",
|
||||
"members": "Mitglieder",
|
||||
"moderators": "Moderatoren",
|
||||
"myContributions": "Meine Beiträge zum Gemeinwohl",
|
||||
"openContributionLinks": "öffentliche Beitrags-Linkliste",
|
||||
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
||||
"other-communities": "Weitere Gemeinschaften",
|
||||
"statistic": "Statistik",
|
||||
"submitContribution": "Beitrag einreichen",
|
||||
"switch-to-this-community": "zu dieser Gemeinschaft wechseln"
|
||||
},
|
||||
@ -55,10 +60,12 @@
|
||||
"selectDate": "Wann war dein Beitrag?",
|
||||
"submit": "Einreichen",
|
||||
"submitted": "Der Beitrag wurde eingereicht.",
|
||||
"updated": "Der Beitrag wurde geändert."
|
||||
"updated": "Der Beitrag wurde geändert.",
|
||||
"yourActivity": "Bitte trage eine Tätigkeit ein!"
|
||||
},
|
||||
"contribution-link": {
|
||||
"thanksYouWith": "dankt dir mit"
|
||||
"thanksYouWith": "dankt dir mit",
|
||||
"unique": "(einmalig)"
|
||||
},
|
||||
"decay": {
|
||||
"before_startblock_transaction": "Diese Transaktion beinhaltet keine Vergänglichkeit.",
|
||||
@ -117,6 +124,7 @@
|
||||
"from": "Von",
|
||||
"generate_now": "Jetzt generieren",
|
||||
"lastname": "Nachname",
|
||||
"mandatoryField": "Pflichtfeld",
|
||||
"memo": "Nachricht",
|
||||
"message": "Nachricht",
|
||||
"new_balance": "Neuer Kontostand nach Bestätigung",
|
||||
@ -204,6 +212,7 @@
|
||||
"login": "Anmeldung",
|
||||
"math": {
|
||||
"aprox": "~",
|
||||
"asterisk": "*",
|
||||
"equal": "=",
|
||||
"minus": "−",
|
||||
"pipe": "|"
|
||||
@ -221,6 +230,7 @@
|
||||
"navigation": {
|
||||
"admin_area": "Adminbereich",
|
||||
"community": "Gemeinschaft",
|
||||
"info": "Information",
|
||||
"logout": "Abmelden",
|
||||
"members_area": "Mitgliederbereich",
|
||||
"overview": "Übersicht",
|
||||
@ -243,6 +253,9 @@
|
||||
"changeLanguage": "Sprache ändern",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"fr": "Français",
|
||||
"nl": "Dutch",
|
||||
"success": "Deine Sprache wurde erfolgreich geändert."
|
||||
},
|
||||
"name": {
|
||||
@ -294,6 +307,14 @@
|
||||
"uppercase": "Großbuchstabe erforderlich."
|
||||
}
|
||||
},
|
||||
"statistic": {
|
||||
"activeUsers": "Aktive Mitglieder",
|
||||
"deletedUsers": "Gelöschte Mitglieder",
|
||||
"totalGradidoAvailable": "GDD insgesamt im Umlauf",
|
||||
"totalGradidoCreated": "GDD insgesamt geschöpft",
|
||||
"totalGradidoDecayed": "GDD insgesamt verfallen",
|
||||
"totalGradidoUnbookedDecayed": "Gesamter ungebuchter GDD Verfall"
|
||||
},
|
||||
"success": "Erfolg",
|
||||
"time": {
|
||||
"days": "Tage",
|
||||
|
||||
@ -26,8 +26,13 @@
|
||||
"community": "Community",
|
||||
"continue-to-registration": "Continue to registration",
|
||||
"current-community": "Current community",
|
||||
"members": "Members",
|
||||
"moderators": "Moderators",
|
||||
"myContributions": "My contributions to the common good",
|
||||
"openContributionLinks": "open Contribution links list",
|
||||
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
||||
"other-communities": "Other communities",
|
||||
"statistic": "Statistics",
|
||||
"submitContribution": "Submit contribution",
|
||||
"switch-to-this-community": "Switch to this community"
|
||||
},
|
||||
@ -55,10 +60,12 @@
|
||||
"selectDate": "When was your contribution?",
|
||||
"submit": "Submit",
|
||||
"submitted": "The contribution was submitted.",
|
||||
"updated": "The contribution was changed."
|
||||
"updated": "The contribution was changed.",
|
||||
"yourActivity": "Please enter an activity!"
|
||||
},
|
||||
"contribution-link": {
|
||||
"thanksYouWith": "thanks you with"
|
||||
"thanksYouWith": "thanks you with",
|
||||
"unique": "(unique)"
|
||||
},
|
||||
"decay": {
|
||||
"before_startblock_transaction": "This transaction does not include decay.",
|
||||
@ -117,6 +124,7 @@
|
||||
"from": "from",
|
||||
"generate_now": "Generate now",
|
||||
"lastname": "Lastname",
|
||||
"mandatoryField": "mandatory field",
|
||||
"memo": "Message",
|
||||
"message": "Message",
|
||||
"new_balance": "Account balance after confirmation",
|
||||
@ -204,6 +212,7 @@
|
||||
"login": "Login",
|
||||
"math": {
|
||||
"aprox": "~",
|
||||
"asterisk": "*",
|
||||
"equal": "=",
|
||||
"minus": "−",
|
||||
"pipe": "|"
|
||||
@ -221,6 +230,7 @@
|
||||
"navigation": {
|
||||
"admin_area": "Admin Area",
|
||||
"community": "Community",
|
||||
"info": "Information",
|
||||
"logout": "Logout",
|
||||
"members_area": "Members area",
|
||||
"overview": "Overview",
|
||||
@ -243,6 +253,9 @@
|
||||
"changeLanguage": "Change language",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"fr": "Français",
|
||||
"nl": "Holandés",
|
||||
"success": "Your language has been successfully updated."
|
||||
},
|
||||
"name": {
|
||||
@ -294,6 +307,14 @@
|
||||
"uppercase": "One uppercase letter required."
|
||||
}
|
||||
},
|
||||
"statistic": {
|
||||
"activeUsers": "Active members",
|
||||
"deletedUsers": "Deleted members",
|
||||
"totalGradidoAvailable": "Total GDD in circulation",
|
||||
"totalGradidoCreated": "Total created GDD",
|
||||
"totalGradidoDecayed": "Total GDD decay",
|
||||
"totalGradidoUnbookedDecayed": "Total unbooked GDD decay"
|
||||
},
|
||||
"success": "Success",
|
||||
"time": {
|
||||
"days": "Days",
|
||||
|
||||
341
frontend/src/locales/es.json
Normal file
341
frontend/src/locales/es.json
Normal file
@ -0,0 +1,341 @@
|
||||
{
|
||||
"100": "100%",
|
||||
"1000thanks": "1000 Gracias, por estar con nosotros!",
|
||||
"125": "125%",
|
||||
"85": "85%",
|
||||
"advanced-calculation": "Proyección",
|
||||
"auth": {
|
||||
"left": {
|
||||
"dignity": "Dignidad",
|
||||
"donation": "Donación",
|
||||
"gratitude": "Gratitud",
|
||||
"hasAccount": "Ya estas registrado?",
|
||||
"hereLogin": "Regístrate aquí",
|
||||
"learnMore": "Infórmate aquí …",
|
||||
"oneDignity": "Damos los unos a los otros y agradecemos con Gradido.",
|
||||
"oneDonation": "Eres un regalo para la comunidad. 1000 gracias por estar con nosotros.",
|
||||
"oneGratitude": "Por los demás, por toda la humanidad, por la naturaleza."
|
||||
},
|
||||
"navbar": {
|
||||
"aboutGradido": "Sobre Gradido"
|
||||
}
|
||||
},
|
||||
"back": "Volver",
|
||||
"community": {
|
||||
"choose-another-community": "Escoger otra comunidad",
|
||||
"community": "Comunidad",
|
||||
"continue-to-registration": "Continuar con el registro",
|
||||
"current-community": "Comunidad actual",
|
||||
"members": "Miembros",
|
||||
"moderators": "Moderadores",
|
||||
"myContributions": "Mis contribuciones al bien común",
|
||||
"openContributionLinks": "lista de enlaces de contribuciones públicas",
|
||||
"openContributionLinkText": "La comunidad \"{name}\" proporciona actualmente las siguientes {count} creaciones automáticas.",
|
||||
"other-communities": "Otras comunidades",
|
||||
"statistic": "Estadísticas",
|
||||
"submitContribution": "Aportar una contribución",
|
||||
"switch-to-this-community": "cambiar a esta comunidad"
|
||||
},
|
||||
"contribution": {
|
||||
"activity": "Actividad",
|
||||
"alert": {
|
||||
"communityNoteList": "Aquí encontrarás todas las contribuciones enviadas y confirmadas de todos los miembros de esta comunidad.",
|
||||
"confirm": "confirmado",
|
||||
"myContributionNoteList": "Puedes editar o eliminar las contribuciones enviadas que aún no han sido confirmadas en cualquier momento.",
|
||||
"myContributionNoteSupport": "Pronto existirá la posibilidad de que puedas dialogar con los moderadores. Si tienes algún problema ahora, ponte en contacto con el equipo de asistencia.",
|
||||
"pending": "Enviado y a la espera de confirmación",
|
||||
"rejected": "rechazado"
|
||||
},
|
||||
"date": "Contribución para:",
|
||||
"delete": "Eliminar la contribución. ¿Estás seguro?",
|
||||
"deleted": "¡La contribución ha sido borrada! Pero seguirá siendo visible.",
|
||||
"formText": {
|
||||
"bringYourTalentsTo": "¡Contribuye a la comunidad con tus talentos! Premiamos tu compromiso voluntario con 20 GDD por hora hasta un máximo de 1.000 GDD al mes.",
|
||||
"describeYourCommunity": "¡Describe tu contribución al bien-común con detalles de las horas e introduce una cantidad de 20 GDD por hora! Tras la confirmación de un moderador, el importe se abonará en tu cuenta.",
|
||||
"maxGDDforMonth": "Sólo puede presentar un máximo de {amount} GDD para el mes seleccionado.",
|
||||
"openAmountForMonth": "Para <b>{monthAndYear}</b> aún puedes presentar <b>{creation}</b> GDD.",
|
||||
"yourContribution": "Tu contribución a la comunidad."
|
||||
},
|
||||
"noDateSelected": "Elige cualquier fecha del mes.",
|
||||
"selectDate": "¿Cuando fue tu contribución?",
|
||||
"submit": "Enviar",
|
||||
"submitted": "Tu contribución ha sido enviada.",
|
||||
"updated": "La contribución se modificó.",
|
||||
"yourActivity": "¡Por favor, introduce una actividad!"
|
||||
},
|
||||
"contribution-link": {
|
||||
"thanksYouWith": "te agradece con",
|
||||
"unique": "(único)"
|
||||
},
|
||||
"decay": {
|
||||
"before_startblock_transaction": "Esta transacción no implica disminución en su valor.",
|
||||
"calculation_decay": "Cálculo de la disminución gradual del valor",
|
||||
"calculation_total": "Cálculo de la suma total",
|
||||
"decay": "Disminución gradual del valor",
|
||||
"decay_introduced": "La disminución gradual empezó el:",
|
||||
"decay_since_last_transaction": "Disminución gradual",
|
||||
"last_transaction": "Transacción anterior",
|
||||
"past_time": "Tiempo transcurrido",
|
||||
"Starting_block_decay": "Startblock disminución gradual",
|
||||
"total": "Total",
|
||||
"types": {
|
||||
"creation": "Creado",
|
||||
"noDecay": "sin disminución gradual",
|
||||
"receive": "Recibido",
|
||||
"send": "Enviado"
|
||||
}
|
||||
},
|
||||
"delete": "Eliminar",
|
||||
"em-dash": "—",
|
||||
"error": {
|
||||
"email-already-sent": "Ya te hemos enviado un correo electrónico hace menos de 10 minutos.",
|
||||
"empty-transactionlist": "Ha habido un error en la transmisión del número de sus transacciones.",
|
||||
"error": "Error!",
|
||||
"no-account": "Lamentablemente no hemos podido encontrar una cuenta (activada) con estos datos.",
|
||||
"no-transactionlist": "Lamentablemente, hubo un error. No se ha transmitido ninguna transacción desde el servidor.",
|
||||
"no-user": "No hay usuario con estas referencias.",
|
||||
"session-expired": "La sesión se cerró por razones de seguridad.",
|
||||
"unknown-error": "Error desconocido: "
|
||||
},
|
||||
"followUs": "sigue nos:",
|
||||
"footer": {
|
||||
"app_version": "App versión {version}",
|
||||
"copyright": {
|
||||
"link": "Gradido-Akademie",
|
||||
"year": "© {year}"
|
||||
},
|
||||
"imprint": "Aviso legal",
|
||||
"privacy_policy": "Protección de Datos",
|
||||
"short_hash": "({shortHash})",
|
||||
"whitepaper": "Whitepaper"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Importe",
|
||||
"at": "am",
|
||||
"cancel": "Cancelar",
|
||||
"change": "Cambiar",
|
||||
"check_now": "Revisar",
|
||||
"close": "Cerrar",
|
||||
"current_balance": "Saldo de cuenta actual",
|
||||
"date": "Fecha",
|
||||
"description": "Descripción",
|
||||
"email": "E-Mail",
|
||||
"firstname": "Nombre",
|
||||
"from": "De",
|
||||
"generate_now": "crear ahora",
|
||||
"lastname": "Apellido",
|
||||
"mandatoryField": "campo obligatorio",
|
||||
"memo": "Mensaje",
|
||||
"message": "Noticia",
|
||||
"new_balance": "Saldo de cuenta nuevo depués de confirmación",
|
||||
"no_gdd_available": "No dispones de GDD para enviar.",
|
||||
"password": "Contraseña",
|
||||
"passwordRepeat": "Repetir contraseña",
|
||||
"password_new": "contraseña nueva",
|
||||
"password_new_repeat": "Repetir contraseña nueva",
|
||||
"password_old": "contraseña antigua",
|
||||
"recipient": "Destinatario",
|
||||
"reset": "Restablecer",
|
||||
"save": "Guardar",
|
||||
"scann_code": "<strong>QR Code Scanner</strong> - Escanea el código QR de tu pareja",
|
||||
"sender": "Remitente",
|
||||
"send_check": "Confirma tu transacción. Por favor revisa toda la información nuevamente!",
|
||||
"send_now": "Enviar ahora",
|
||||
"send_transaction_error": "Desafortunadamente, la transacción no se pudo ejecutar!",
|
||||
"send_transaction_success": "Su transacción fue ejecutada con éxito",
|
||||
"sorry": "Disculpa",
|
||||
"thx": "Gracias",
|
||||
"time": "Tiempo",
|
||||
"to": "hasta",
|
||||
"to1": "para",
|
||||
"validation": {
|
||||
"gddSendAmount": "El campo {_field_} debe ser un número entre {min} y {max} con un máximo de dos decimales",
|
||||
"is-not": "No es posible transferirte Gradidos a ti mismo",
|
||||
"usernmae-regex": "El nombre de usuario debe comenzar con una letra seguida de al menos dos caracteres alfanuméricos.",
|
||||
"usernmae-unique": "Este nombre de usuario ya está adjudicado."
|
||||
},
|
||||
"your_amount": "Tu importe"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"gdd_per_link": {
|
||||
"choose-amount": "Selecciona una cantidad que te gustaría enviar a través de un enlace. También puedes ingresar un mensaje. Cuando haces clic en 'Generar ahora', se crea un enlace que puedes enviar.",
|
||||
"copy-link": "Copiar enlace",
|
||||
"copy-link-with-text": "Copiar texto y enlace",
|
||||
"created": "El enlace ha sido creado",
|
||||
"credit-your-gradido": "Para que se te acrediten los Gradidos, haz clic en el enlace!",
|
||||
"decay-14-day": "Disminución gradual por 14 días",
|
||||
"delete-the-link": "Eliminar el enlace?",
|
||||
"deleted": "El enlace ha sido eliminado!",
|
||||
"expiredOn": "Vencido el:",
|
||||
"has-account": "Ya tienes una cuenta Gradido?",
|
||||
"header": "Transferir Gradidos por medio de un enlace",
|
||||
"isFree": "Gradido es gratis en todo el mundo.",
|
||||
"link-and-text-copied": "El enlace y su mensaje se han copiado en el portapapeles. Ahora puedes ponerlo en un correo electrónico o mensaje.",
|
||||
"link-copied": "El enlace se ha copiado en el portapapeles. Ahora puedes pegarlo en un correo electrónico o mensaje.",
|
||||
"link-deleted": "El enlace se eliminó el {date}.",
|
||||
"link-expired": "El enlace ya no es válido. La validez expiró el {date}.",
|
||||
"link-overview": "Resumen de enlaces",
|
||||
"links_count": "Enlaces activos",
|
||||
"links_sum": "Enlaces abiertos y códigos QR",
|
||||
"no-account": "Aún no tienes una cuenta de Gradido?",
|
||||
"no-redeem": "No puedes canjear tu propio enlace!",
|
||||
"not-copied": "¡Desafortunadamente, su dispositivo no permite copiar! Copie el enlace manualmente!",
|
||||
"redeem": "Canjear",
|
||||
"redeem-text": "¿Quieres canjear el importe ahora?",
|
||||
"redeemed": "¡Canjeado con éxito! Tu cuenta ha sido acreditada con {n} GDD.",
|
||||
"redeemed-at": "El enlace ya se canjeó el {date}.",
|
||||
"redeemed-title": "canjeado",
|
||||
"to-login": "iniciar sesión",
|
||||
"to-register": "Registre una nueva cuenta.",
|
||||
"validUntil": "Válido hasta",
|
||||
"validUntilDate": "El enlace es válido hasta el {date} ."
|
||||
},
|
||||
"gdt": {
|
||||
"calculation": "Cálculo del Gradido Transform",
|
||||
"contribution": "Importe",
|
||||
"conversion": "Conversión",
|
||||
"conversion-gdt-euro": "Conversión Euro / Gradido Transform (GDT)",
|
||||
"credit": "Abono",
|
||||
"factor": "Factor",
|
||||
"formula": "Formula de cálculo",
|
||||
"funding": "Las donaciones",
|
||||
"gdt": "Gradido Transform",
|
||||
"gdt-received": "Gradido Transform (GDT) recibido",
|
||||
"no-transactions": "Aún no tienes un Gradido Transform (GDT).",
|
||||
"not-reachable": "No es posible acceder al servidor GDT.",
|
||||
"publisher": "Tu nuevo miembro referido ha pagado la cuota",
|
||||
"raise": "Aumento",
|
||||
"recruited-member": "Miembro invitado"
|
||||
},
|
||||
"language": "Idioma",
|
||||
"link-load": "recargar el último enlace |recargar los últimos {n} enlaces | descargar más {n} enlaces",
|
||||
"login": "iniciar sesión",
|
||||
"math": {
|
||||
"aprox": "~",
|
||||
"asterisk": "*",
|
||||
"equal": "=",
|
||||
"minus": "−",
|
||||
"pipe": "|"
|
||||
},
|
||||
"message": {
|
||||
"activateEmail": "Tu cuenta aún no ha sido activada. Por favor revisa tu correo electrónico y haz clic en el enlace de activación o solicita uno nuevo enlace de activación a través de la página restablecer contraseña.",
|
||||
"checkEmail": "Tu correo electrónico ha sido verificado con éxito. Puedes registrarte ahora.",
|
||||
"email": "Te hemos enviado un correo electrónico.",
|
||||
"errorTitle": "Atención!",
|
||||
"register": "Ya estás registrado, por favor revisa tu correo electrónico y haz clic en el enlace de activación.",
|
||||
"reset": "Tu contraseña ha sido cambiada.",
|
||||
"title": "Gracias!",
|
||||
"unsetPassword": "Tu contraseña aún no ha sido configurada. Por favor reinícialo."
|
||||
},
|
||||
"navigation": {
|
||||
"admin_area": "Área de administración",
|
||||
"community": "Comunidad",
|
||||
"info": "Información",
|
||||
"logout": "Salir",
|
||||
"members_area": "Área de afiliados",
|
||||
"overview": "Resumen",
|
||||
"profile": "Mi Perfil",
|
||||
"send": "Enviar",
|
||||
"support": "Soporte",
|
||||
"transactions": "Transacciones"
|
||||
},
|
||||
"qrCode": "Código QR",
|
||||
"send_gdd": "Enviar GDD",
|
||||
"send_per_link": "Enviar GDD mediante un enlace",
|
||||
"session": {
|
||||
"extend": "Permanecer en sesión iniciada",
|
||||
"lightText": "Si no has realizado ninguna acción durante más de 10 minutos, se cerrará tu sesión por razones de seguridad.",
|
||||
"logoutIn": "Cerrar sesión en ",
|
||||
"warningText": "Aún estas?"
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"changeLanguage": "Cambiar idioma",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
"es": "Español",
|
||||
"fr": "Francés",
|
||||
"nl": "Holandés",
|
||||
"success": "Tu idioma ha sido cambiado con éxito."
|
||||
},
|
||||
"name": {
|
||||
"change-name": "Cambiar nombre",
|
||||
"change-success": "Tu nombre ha sido cambiado con éxito."
|
||||
},
|
||||
"newsletter": {
|
||||
"newsletter": "Informaciones por correo electrónico",
|
||||
"newsletterFalse": "No recibirás informaciones por correo electrónico.",
|
||||
"newsletterTrue": "Recibirás informaciones por correo electrónico."
|
||||
},
|
||||
"password": {
|
||||
"change-password": "Cambiar contraseña",
|
||||
"forgot_pwd": "Olvide la contraseña?",
|
||||
"resend_subtitle": "Su enlace de activación ha caducado. Puedes solicitar uno nuevo aquí.",
|
||||
"reset": "Restablecer contraseña",
|
||||
"reset-password": {
|
||||
"text": "Ahora introduce una nueva contraseña con la que quieras acceder a tu cuenta de Gradido en el futuro.."
|
||||
},
|
||||
"send_now": "Enviar",
|
||||
"set": "Establecer contraseña",
|
||||
"set-password": {
|
||||
"text": "Ahora guarda tu nueva contraseña, que podrás utilizar para acceder a tu cuenta de Gradido en el futuro."
|
||||
},
|
||||
"subtitle": "Si has olvidado tu contraseña, puedes restablecerla aquí."
|
||||
}
|
||||
},
|
||||
"signin": "Iniciar sesión",
|
||||
"signup": "Registrarse",
|
||||
"site": {
|
||||
"forgotPassword": {
|
||||
"heading": "Por favor, introduce la dirección de correo electrónico con la que estas registrado en Gradido."
|
||||
},
|
||||
"login": {
|
||||
"heading": "Inicia sesión con tus datos de acceso. Manténlos seguros en todo momento!"
|
||||
},
|
||||
"resetPassword": {
|
||||
"heading": "Por favor, introduce tu contraseña y repítela."
|
||||
},
|
||||
"signup": {
|
||||
"agree": "Acepto la <a href='https://gradido.net/de/datenschutz/' target='_blank' >Política de privacidad</a>.",
|
||||
"dont_match": "Las contraseñas no coinciden.",
|
||||
"heading": "Regístrate introduciendo todos los datos completos y en los campos correctos.",
|
||||
"lowercase": "Se requiere una letra minúscula.",
|
||||
"minimum": "Al menos 8 caracteres.",
|
||||
"no-whitespace": "Sin espacios ni tabulaciones.",
|
||||
"one_number": "Se requiere un número.",
|
||||
"special-char": "Caracteres especiales requeridos (por ejemplo, _ o &)",
|
||||
"uppercase": "Letra mayúscula requerida."
|
||||
}
|
||||
},
|
||||
"statistic": {
|
||||
"activeUsers": "miembros activos",
|
||||
"deletedUsers": "miembros eliminados",
|
||||
"totalGradidoAvailable": "GDD total en circulación",
|
||||
"totalGradidoCreated": "GDD total creado",
|
||||
"totalGradidoDecayed": "GDD total decaído",
|
||||
"totalGradidoUnbookedDecayed": "GDD no contabilizado decaído"
|
||||
},
|
||||
"success": "Lo lograste",
|
||||
"time": {
|
||||
"days": "Días",
|
||||
"hours": "Horas",
|
||||
"minutes": "Minutos",
|
||||
"month": "Mes",
|
||||
"months": "Meses",
|
||||
"seconds": "Segundos",
|
||||
"years": "Año"
|
||||
},
|
||||
"transaction": {
|
||||
"gdd-text": "Transacciones Gradido",
|
||||
"gdt-text": "Transacciones GradidoTransform ",
|
||||
"nullTransactions": "Todavía no tienes ninguna transacción en tu cuenta.",
|
||||
"receiverDeleted": "La cuenta del destinatario ha sido eliminada.",
|
||||
"receiverNotFound": "Destinatario no encontrado",
|
||||
"show_all": "Ver todas las transacciones de <strong>{count}</strong>"
|
||||
},
|
||||
"transaction-link": {
|
||||
"send_you": "te envía"
|
||||
},
|
||||
"via_link": "atraves de un enlace",
|
||||
"welcome": "Bienvenido a la comunidad."
|
||||
}
|
||||
341
frontend/src/locales/fr.json
Normal file
341
frontend/src/locales/fr.json
Normal file
@ -0,0 +1,341 @@
|
||||
{
|
||||
"100": "100%",
|
||||
"1000thanks": "1000 mercis d'être avec nous!",
|
||||
"125": "125%",
|
||||
"85": "85%",
|
||||
"advanced-calculation": "Calcul avancé",
|
||||
"auth": {
|
||||
"left": {
|
||||
"dignity": "Dignité",
|
||||
"donation": "Donation",
|
||||
"gratitude": "Gratitude",
|
||||
"hasAccount": "Avez-vous déjà un compte?",
|
||||
"hereLogin": "Connectez-vous ici",
|
||||
"learnMore": "Pour plus de détails …",
|
||||
"oneDignity": "Nous nous donnons mutuellement et rendons grâce avec Gradido.",
|
||||
"oneDonation": "Vous êtes précieux pour la communauté. 1000 mercis d’être parmi nous.",
|
||||
"oneGratitude": "Les uns pour les autres, pour tout le monde, pour la nature."
|
||||
},
|
||||
"navbar": {
|
||||
"aboutGradido": "À propos de Gradido"
|
||||
}
|
||||
},
|
||||
"back": "Retour",
|
||||
"community": {
|
||||
"choose-another-community": "Choisissez une autre communauté",
|
||||
"community": "Communauté",
|
||||
"continue-to-registration": "Continuez l´inscription",
|
||||
"current-community": "Communauté actuelle",
|
||||
"members": "Membres",
|
||||
"moderators": "Modérateurs",
|
||||
"myContributions": "Mes contributions aux biens communs",
|
||||
"openContributionLinks": "liste de liens de contribution publique",
|
||||
"openContributionLinkText": "Les {count} créations automatiques suivantes sont actuellement fournies par la communauté \"{name}\".",
|
||||
"other-communities": "Autres communautés",
|
||||
"statistic": "Statistiques",
|
||||
"submitContribution": "Donner une contribution",
|
||||
"switch-to-this-community": "Passer à cette communauté"
|
||||
},
|
||||
"contribution": {
|
||||
"activity": "Activité",
|
||||
"alert": {
|
||||
"communityNoteList": "Vous trouverez ci-contre toutes les contributions versées et certifiées de tous les membres de cette communauté.",
|
||||
"confirm": " Approuvé",
|
||||
"myContributionNoteList": "À tout moment vous pouvez éditer ou supprimer les données qui n´ont pas été confirmées.",
|
||||
"myContributionNoteSupport": "Vous aurez bientôt la possibilité de dialoguer avec un médiateur. Si vous rencontrez un problème maintenant, merci de contacter l´aide en ligne.",
|
||||
"pending": "Inscription en attente de validation",
|
||||
"rejected": "supprimé"
|
||||
},
|
||||
"date": "Contribution pour:",
|
||||
"delete": "Supprimer la contribution! Êtes-vous sûr?",
|
||||
"deleted": "La contribution a été supprimée! Mais elle restera visible.",
|
||||
"formText": {
|
||||
"bringYourTalentsTo": "Apportez vos talents à la communauté! Votre participation bénévole sera récompensée de 20 GDD/heure jusqu´à un plafond de 1000 GDD/mois.",
|
||||
"describeYourCommunity": "Décrivez votre activité/service à la communauté en mentionnant le nombre d´heures, et calculez le montant à raison de 20 GDD/heure! Après confirmation par l´un de nos collaborateurs, le montant sera crédité sur votre compte.",
|
||||
"maxGDDforMonth": "Vous pouvez seulement déclarer un montant maximum de (montant) GDD pour le mois sélectionné.",
|
||||
"openAmountForMonth": "Pour <b>{monthAndYear}</b>, vous pouvez encore déclarer <b>{creation}</b> GDD.",
|
||||
"yourContribution": "Votre contribution au bien commun"
|
||||
},
|
||||
"noDateSelected": "Choisissez n´importe quelle date du mois",
|
||||
"selectDate": "Quand a été effectuée votre contribution?",
|
||||
"submit": "Soumettre",
|
||||
"submitted": "La contribution a été soumise.",
|
||||
"updated": "La contribution a été modifiée.",
|
||||
"yourActivity": "Veuillez saisir une activité!"
|
||||
},
|
||||
"contribution-link": {
|
||||
"thanksYouWith": "vous remercie avec",
|
||||
"unique": "(unique)"
|
||||
},
|
||||
"decay": {
|
||||
"before_startblock_transaction": "Cette transaction n´est pas péremptoire.",
|
||||
"calculation_decay": "Calcul de la décroissance",
|
||||
"calculation_total": "Calcul du montant total",
|
||||
"decay": "Décroissance",
|
||||
"decay_introduced": "Décroissance engagée le:",
|
||||
"decay_since_last_transaction": "Décroissance depuis la dernière transaction",
|
||||
"last_transaction": "Dernière transaction:",
|
||||
"past_time": "Le temps a expiré",
|
||||
"Starting_block_decay": "Début de la décroissance",
|
||||
"total": "Total",
|
||||
"types": {
|
||||
"creation": "Créé",
|
||||
"noDecay": "Aucun déclin",
|
||||
"receive": "Reçu",
|
||||
"send": "Envoyé"
|
||||
}
|
||||
},
|
||||
"delete": "Supprimer",
|
||||
"em-dash": "—",
|
||||
"error": {
|
||||
"email-already-sent": "Nous vous avons déjà envoyé un email il y a moins de 10 minutes.",
|
||||
"empty-transactionlist": "Il y a eu une erreur lors de la transmission du numéro de votre transaction.",
|
||||
"error": "Erreur!",
|
||||
"no-account": "Malheureusement nous n´avons pas pu trouver de compte (actif) correspondant aux données transmises.",
|
||||
"no-transactionlist": "Il y a malheureusement eu une erreur. Aucune transaction n´a été envoyée depuis l`serveur.",
|
||||
"no-user": "Pas d`utilisateur pour cet identifiant.",
|
||||
"session-expired": "La session a expiré pour des raisons de sécurité.",
|
||||
"unknown-error": "Erreur inconnue: "
|
||||
},
|
||||
"followUs": "Suivez-nous:",
|
||||
"footer": {
|
||||
"app_version": "Version de l'application {version}",
|
||||
"copyright": {
|
||||
"link": "Académie Gradido",
|
||||
"year": "© {year}"
|
||||
},
|
||||
"imprint": "Notification légale",
|
||||
"privacy_policy": "Politique de confidentialité",
|
||||
"short_hash": "({shortHash})",
|
||||
"whitepaper": "Papier blanc"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Montant",
|
||||
"at": "à",
|
||||
"cancel": "Annuler",
|
||||
"change": "Changer",
|
||||
"check_now": "Vérifier maintenant",
|
||||
"close": "Fermer",
|
||||
"current_balance": "Solde actuel",
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"email": "Email",
|
||||
"firstname": " Prénom",
|
||||
"from": "de",
|
||||
"generate_now": "Produire maintenant",
|
||||
"lastname": "Nom",
|
||||
"mandatoryField": "champ obligatoire",
|
||||
"memo": "Note",
|
||||
"message": "Message",
|
||||
"new_balance": "Montant du solde après confirmation",
|
||||
"no_gdd_available": "Vous n´avez pas de GDD à envoyer.",
|
||||
"password": "Mot de passe",
|
||||
"passwordRepeat": "Répétez le mot de passe",
|
||||
"password_new": "Nouveau mot de passe",
|
||||
"password_new_repeat": "Répétez le nouveau mot de passe",
|
||||
"password_old": "Ancien mot de passe",
|
||||
"recipient": "Destinataire",
|
||||
"reset": "Réinitialiser",
|
||||
"save": "Sauvegarder",
|
||||
"scann_code": "<strong>QR Code Scanner</strong> - Scannez le QR code de votre partenaire",
|
||||
"sender": "Expéditeur",
|
||||
"send_check": "Confirmez la transaction. Veuillez revérifier toutes les données svp!",
|
||||
"send_now": "Envoyez maintenant",
|
||||
"send_transaction_error": "Malheureusement, la transaction n´a pas pu être effectuée!",
|
||||
"send_transaction_success": "Votre transaction a été effectuée avec succès",
|
||||
"sorry": "Désolé",
|
||||
"thx": "Merci",
|
||||
"time": "Heure",
|
||||
"to": "à",
|
||||
"to1": "à",
|
||||
"validation": {
|
||||
"gddSendAmount": "L´espace {_field_} doit comprendre un nombre entre {min} et {max} avec un maximum de deux chiffres après la virgule",
|
||||
"is-not": "Vous ne pouvez pas vous envoyer de Gradido à vous-même",
|
||||
"usernmae-regex": "Le nom d´utilisateur doit commencer par une lettre, suivi d´au moins deux caractères alphanumériques.",
|
||||
"usernmae-unique": "Ce nom d´utilisateur est déjà pris."
|
||||
},
|
||||
"your_amount": "Votre montant"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"gdd_per_link": {
|
||||
"choose-amount": "Sélectionnez le montant que vous souhaitez envoyer via lien. Vous pouvez également joindre un message. Cliquez sur ‘créer maintenant’ pour établir un lien que vous pourrez partager.",
|
||||
"copy-link": "Copier le lien",
|
||||
"copy-link-with-text": "Copier le lien et le texte",
|
||||
"created": "Le lien a été créé!",
|
||||
"credit-your-gradido": "Pour l´accréditation du Gradido, cliquer sur le lien!",
|
||||
"decay-14-day": "Perte sur 14 jours",
|
||||
"delete-the-link": "Supprimer le lien?",
|
||||
"deleted": "Le lien a été supprimé!",
|
||||
"expiredOn": "A expiré le",
|
||||
"has-account": "Vous avez déjà un compte Gradido?",
|
||||
"header": "Envoyer des Gradidos via lien",
|
||||
"isFree": "Gradido est gratuit mondialement.",
|
||||
"link-and-text-copied": "Le lien et votre message ont été copiés dans le presse-papier. Vous pouvez maintenant le joindre à un email ou à un message..",
|
||||
"link-copied": "Le lien a été copié dans le presse-papier. Vous pouvez désormais le coller dans votre email ou votre message.",
|
||||
"link-deleted": "Le lien a été supprimé le on {date}.",
|
||||
"link-expired": "Le lien n´est plus valide. Sa validité a expiré le {date}.",
|
||||
"link-overview": "Aperçu du lien",
|
||||
"links_count": "Liens actifs",
|
||||
"links_sum": "Ouvrir les liens et les QR codes",
|
||||
"no-account": "Vous n´avez pas encore de compte Gradido?",
|
||||
"no-redeem": "Vous n´êtes pas autorisé à percevoir votre propre lien!",
|
||||
"not-copied": "Malheureusement votre appareil ne permet pas de copier! Veuillez copier le lien manuellement svp!",
|
||||
"redeem": "Encaisser",
|
||||
"redeem-text": "Voulez-vous percevoir le montant maintenant?",
|
||||
"redeemed": "Encaissé avec succès! Votre compte est crédité de {n} GDD.",
|
||||
"redeemed-at": "Le lien a déjà été perçu le {date}.",
|
||||
"redeemed-title": "encaisser",
|
||||
"to-login": "Connexion",
|
||||
"to-register": "Enregistrer un nouveau compte.",
|
||||
"validUntil": "Valide jusqu´au",
|
||||
"validUntilDate": "Le lien est valide jusqu´au {date}."
|
||||
},
|
||||
"gdt": {
|
||||
"calculation": "Calcul de Gradido Transform",
|
||||
"contribution": "Contribution",
|
||||
"conversion": "Conversion",
|
||||
"conversion-gdt-euro": "Conversion Euro / Gradido Transform (GDT)",
|
||||
"credit": "Crédit",
|
||||
"factor": "Coefficient",
|
||||
"formula": "Formule de calcul",
|
||||
"funding": "Aux contributions au financement",
|
||||
"gdt": "Gradido Transform",
|
||||
"gdt-received": "Gradido Transform (GDT) perçu",
|
||||
"no-transactions": "Vous ne possédez pas encore Gradido Transform (GDT).",
|
||||
"not-reachable": "Le Serveur GDT n´est pas accessible.",
|
||||
"publisher": "Un membre que vous avez référé a apporté un contribution",
|
||||
"raise": "Augmentation",
|
||||
"recruited-member": "Membre invité"
|
||||
},
|
||||
"language": "Langage",
|
||||
"link-load": "Enregistrer le dernier lien | Enregistrer les derniers {n} liens | Enregistrer plus de {n} liens",
|
||||
"login": "Connexion",
|
||||
"math": {
|
||||
"aprox": "~",
|
||||
"asterisk": "*",
|
||||
"equal": "=",
|
||||
"minus": "−",
|
||||
"pipe": "|"
|
||||
},
|
||||
"message": {
|
||||
"activateEmail": "Votre compte n´a pas encore été activé. Veuillez vérifier vos emails et cliquer sur le lien d´activation ou faites la demande d´un nouveau lien en utilisant la page qui permet de générer un nouveau mot de passe.",
|
||||
"checkEmail": "Votre email a bien été vérifié. Vous pouvez vous enregistrer maintenant.",
|
||||
"email": "Nous vous avons envoyé un email.",
|
||||
"errorTitle": "Attention!",
|
||||
"register": "Vous êtes enregistré maintenant, merci de vérifier votre boîte mail et cliquer sur le lien d´activation.",
|
||||
"reset": "Votre mot de passe a été modifié.",
|
||||
"title": "Merci!",
|
||||
"unsetPassword": "Votre mot de passe n´a pas été accepté. Merci de le réinitialiser."
|
||||
},
|
||||
"navigation": {
|
||||
"admin_area": "Partie administrative",
|
||||
"community": "Communauté",
|
||||
"info": "Information",
|
||||
"logout": "Déconnexion",
|
||||
"members_area": "Partie réservée aux membres",
|
||||
"overview": "Aperçu",
|
||||
"profile": "Mon profile",
|
||||
"send": "Envoyer",
|
||||
"support": "Aide",
|
||||
"transactions": "Transactions"
|
||||
},
|
||||
"qrCode": "QR Code",
|
||||
"send_gdd": "Envoyer GDD",
|
||||
"send_per_link": "Envoyer GDD via lien",
|
||||
"session": {
|
||||
"extend": "Rester connecter",
|
||||
"lightText": "S´il n´apparaît aucune activité pendant plus de 10 minutes, la session expirera pour des raisons de sécurité.",
|
||||
"logoutIn": "Se déconnecter ",
|
||||
"warningText": "Êtes-vous toujours connecté?"
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"changeLanguage": "Changer la langue",
|
||||
"de": "Allemand",
|
||||
"en": "Anglais",
|
||||
"es": "Espagnol",
|
||||
"fr": "Français",
|
||||
"nl": "Néerlandais",
|
||||
"success": "Votre langue de préférence a bien été actualisée."
|
||||
},
|
||||
"name": {
|
||||
"change-name": "Changer le nom",
|
||||
"change-success": "Votre nom a bien été changé."
|
||||
},
|
||||
"newsletter": {
|
||||
"newsletter": "Information par email",
|
||||
"newsletterFalse": "Vous ne recevrez aucune information par email.",
|
||||
"newsletterTrue": "Vous recevrez de l´information par email."
|
||||
},
|
||||
"password": {
|
||||
"change-password": "Changer le mot de passe",
|
||||
"forgot_pwd": "Mot de passe oublié?",
|
||||
"resend_subtitle": "Votre lien d´activation a expiré, vous pouvez en obtenir un nouveau ici.",
|
||||
"reset": "Réinitialiser le mot de passe",
|
||||
"reset-password": {
|
||||
"text": "Entrez un nouveau mot de passe que vous utiliserez dans le futur pour vous connecter à votre compte Gradido.."
|
||||
},
|
||||
"send_now": "Envoyer maintenant",
|
||||
"set": "Définir le mot de passe",
|
||||
"set-password": {
|
||||
"text": "Sauvegardez votre nouveau mot de passe maintenant, que vous pourrez utiliser pour vous connecter à votre compte Gradido dans le futur."
|
||||
},
|
||||
"subtitle": "Si vous avez oublié votre mot de passe, vous pouvez le réinitialiser ici."
|
||||
}
|
||||
},
|
||||
"signin": "S´identifier",
|
||||
"signup": "S´inscrire",
|
||||
"site": {
|
||||
"forgotPassword": {
|
||||
"heading": "Veuillez entrer l´adresse email sous laquelle vous êtes enregistré ici svp."
|
||||
},
|
||||
"login": {
|
||||
"heading": "Vous connecter avec vos données d´accès. Gardez les en sécurité!"
|
||||
},
|
||||
"resetPassword": {
|
||||
"heading": "Entrez votre mot de passe et répétez l´action svp."
|
||||
},
|
||||
"signup": {
|
||||
"agree": "J´accepte le <a href='https://gradido.net/en/datenschutz/' target='_blank' > politique de confidentialité </a>.",
|
||||
"dont_match": "Les mots de passe ne correspondent pas.",
|
||||
"heading": "Vous enregistrer en entrant toutes les données demandées dans les champs requis.",
|
||||
"lowercase": "Une lettre minuscule est requise.",
|
||||
"minimum": "8 caractères minimum.",
|
||||
"no-whitespace": "Pas d´espace ni d´onglet",
|
||||
"one_number": "Un chiffre requis.",
|
||||
"special-char": "Un caractère spécial requis (e.g. _ or ä)",
|
||||
"uppercase": "Une lettre majuscule requise."
|
||||
}
|
||||
},
|
||||
"statistic": {
|
||||
"activeUsers": "Membres actifs",
|
||||
"deletedUsers": "Membres supprimés",
|
||||
"totalGradidoAvailable": "GDD total en circulation",
|
||||
"totalGradidoCreated": "GDD total puisé",
|
||||
"totalGradidoDecayed": "Total de GDD écoulé",
|
||||
"totalGradidoUnbookedDecayed": "Total GDD non comptabilisé écoulé"
|
||||
},
|
||||
"success": "Avec succès",
|
||||
"time": {
|
||||
"days": "Jours",
|
||||
"hours": "Heures",
|
||||
"minutes": "Minutes",
|
||||
"month": "Moi",
|
||||
"months": "Mois",
|
||||
"seconds": "Secondes",
|
||||
"years": "Année"
|
||||
},
|
||||
"transaction": {
|
||||
"gdd-text": "Transactions Gradido",
|
||||
"gdt-text": "Transactions de GradidoTransform",
|
||||
"nullTransactions": "Vous n´avez pas encore de transaction effectuée sur votre compte.",
|
||||
"receiverDeleted": "Le compte du destinataire n´existe plus",
|
||||
"receiverNotFound": "Destinataire inconnu",
|
||||
"show_all": "Voir toutes les <strong>{count}</strong> transactions."
|
||||
},
|
||||
"transaction-link": {
|
||||
"send_you": "veut vous envoyer"
|
||||
},
|
||||
"via_link": "par lien",
|
||||
"welcome": "Bienvenu dans la communauté"
|
||||
}
|
||||
@ -11,6 +11,24 @@ const locales = [
|
||||
iso: 'de-DE',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: 'Español',
|
||||
code: 'es',
|
||||
iso: 'es-ES',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: 'Français',
|
||||
code: 'fr',
|
||||
iso: 'fr-FR',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
name: 'Holandés',
|
||||
code: 'nl',
|
||||
iso: 'nl-NL',
|
||||
enabled: true,
|
||||
},
|
||||
]
|
||||
|
||||
export default locales
|
||||
|
||||
341
frontend/src/locales/nl.json
Normal file
341
frontend/src/locales/nl.json
Normal file
@ -0,0 +1,341 @@
|
||||
{
|
||||
"100": "100%",
|
||||
"1000thanks": "1000 dank, omdat je bij ons bent!",
|
||||
"125": "125%",
|
||||
"85": "85%",
|
||||
"advanced-calculation": "Voorcalculatie",
|
||||
"auth": {
|
||||
"left": {
|
||||
"dignity": "Waardigheid",
|
||||
"donation": "Gift",
|
||||
"gratitude": "Dankbaarheid",
|
||||
"hasAccount": "Je hebt al een rekening?",
|
||||
"hereLogin": "Hier aanmelden",
|
||||
"learnMore": "Meer ervaren …",
|
||||
"oneDignity": "We geven aan elkaar en bedanken met Gradido.",
|
||||
"oneDonation": "Jij bent een geschenk voor de gemeenschap. 1000 dank dat je bij ons bent.",
|
||||
"oneGratitude": "Voor elkaar, voor alle mensen, voor de natuur."
|
||||
},
|
||||
"navbar": {
|
||||
"aboutGradido": "Over Gradido"
|
||||
}
|
||||
},
|
||||
"back": "Terug",
|
||||
"community": {
|
||||
"choose-another-community": "Kies een andere gemeenschap",
|
||||
"community": "Gemeenschap",
|
||||
"continue-to-registration": "Verder ter registratie",
|
||||
"current-community": "Actuele gemeenschap",
|
||||
"members": "Leden",
|
||||
"moderators": "Moderators",
|
||||
"myContributions": "Mijn bijdragen voor het algemeen belang",
|
||||
"openContributionLinks": "openbare lijst van bijdragen",
|
||||
"openContributionLinkText": "De volgende {count} automatische creaties worden momenteel aangeboden door de gemeenschap \"{name}\".",
|
||||
"other-communities": "Verdere gemeenschappen",
|
||||
"statistic": "Statistieken",
|
||||
"submitContribution": "Bijdrage indienen",
|
||||
"switch-to-this-community": "naar deze gemeenschap wisselen"
|
||||
},
|
||||
"contribution": {
|
||||
"activity": "Activiteit",
|
||||
"alert": {
|
||||
"communityNoteList": "Hier vind je alle ingediende en bevestigde bijdragen van alle leden uit deze gemeenschap.",
|
||||
"confirm": "bevestigt",
|
||||
"myContributionNoteList": "Ingediende bijdragen, die nog niet bevestigd zijn, kun je op elk moment wijzigen of verwijderen.",
|
||||
"myContributionNoteSupport": "Hier heb je binnenkort de mogelijkheid een gesprek met een moderator te voeren. Mocht je nu problemen hebben, dan neem alsjeblieft contact op met Support.",
|
||||
"pending": "Ingediend en wacht op bevestiging",
|
||||
"rejected": "afgewezen"
|
||||
},
|
||||
"date": "Bijdrage voor:",
|
||||
"delete": "Bijdrage verwijderen! Weet je het zeker?",
|
||||
"deleted": "De bijdrage werd verwijderd! Blijft echter zichtbaar.",
|
||||
"formText": {
|
||||
"bringYourTalentsTo": "Bied je met jouw talenten in de gemeenschap aan! Jouw vrijwillige inzet belonen we met 20 GGD per uur, tot maximaal 1.000 GGD per maand.",
|
||||
"describeYourCommunity": "Beschrijf jouw activiteit voor het algemeen belang met vermelding van het aantal uren en registreer een bedrag van 20 GDD per uur! Na bevestiging door een moderator wordt het bedrag op jouw rekening bijgeschreven.",
|
||||
"maxGDDforMonth": "Je kunt voor de geselecteerde maand nog maximaal {amount} GDD indienen.",
|
||||
"openAmountForMonth": "Voor <b>{monthAndYear}</b> kun je nog <b>{creation}</b> GDD indienen.",
|
||||
"yourContribution": "Jouw bijdrage voor het algemeen belang"
|
||||
},
|
||||
"noDateSelected": "Kies een willekeurige datum in de maand",
|
||||
"selectDate": "Wanneer was jouw bijdrage?",
|
||||
"submit": "Indienen",
|
||||
"submitted": "De bijdrage werd ingediend.",
|
||||
"updated": "De bijdrage werd veranderd.",
|
||||
"yourActivity": "Voer een activiteit in!"
|
||||
},
|
||||
"contribution-link": {
|
||||
"thanksYouWith": "bedankt jou met",
|
||||
"unique": "(uniek)"
|
||||
},
|
||||
"decay": {
|
||||
"before_startblock_transaction": "Deze transactie heeft geen vergankelijkheid.",
|
||||
"calculation_decay": "Berekening van de vergankelijkheid",
|
||||
"calculation_total": "Berekening van het totaalbedrag",
|
||||
"decay": "Vergankelijkheid",
|
||||
"decay_introduced": "De vergankelijkheid werd ingevoerd op:",
|
||||
"decay_since_last_transaction": "Vergankelijkheid sinds de laatste transactie",
|
||||
"last_transaction": "Laatste transactie",
|
||||
"past_time": "Verlopen tijd",
|
||||
"Starting_block_decay": "Startblok vergankelijkheid",
|
||||
"total": "Totaal",
|
||||
"types": {
|
||||
"creation": "Gecreëerd",
|
||||
"noDecay": "Geen vergankelijkheid",
|
||||
"receive": "Ontvangen",
|
||||
"send": "Verstuurd"
|
||||
}
|
||||
},
|
||||
"delete": "Verwijderen",
|
||||
"em-dash": "—",
|
||||
"error": {
|
||||
"email-already-sent": "Wij hebben jou minder dan 10 minuten geleden een email gestuurd.",
|
||||
"empty-transactionlist": "Er was een fout met de overdracht van het aantal van jouw transacties.",
|
||||
"error": "Fout!",
|
||||
"no-account": "Helaas kunnen we geen (geactiveerde) rekening met deze gegevens vinden.",
|
||||
"no-transactionlist": " Helaas was er een fout. Door de server werden er geen transacties overgedragen.",
|
||||
"no-user": "Geen gebruiker met deze login gegevens.",
|
||||
"session-expired": "De sessie werd om veiligheidsredenen beëindigd.",
|
||||
"unknown-error": "Onbekende fout: "
|
||||
},
|
||||
"followUs": "volg ons:",
|
||||
"footer": {
|
||||
"app_version": "app versie {version}",
|
||||
"copyright": {
|
||||
"link": "Gradido-Academie",
|
||||
"year": "© {year}"
|
||||
},
|
||||
"imprint": "Colofon",
|
||||
"privacy_policy": "Privacyverklaring",
|
||||
"short_hash": "({shortHash})",
|
||||
"whitepaper": "Witboek"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Bedrag",
|
||||
"at": "op",
|
||||
"cancel": "Annuleren",
|
||||
"change": "Wijzigen",
|
||||
"check_now": "Nu controleren",
|
||||
"close": "Sluiten",
|
||||
"current_balance": "Actueel banksaldo",
|
||||
"date": "Datum",
|
||||
"description": "Beschrijving",
|
||||
"email": "Email",
|
||||
"firstname": "Voornaam",
|
||||
"from": "Van",
|
||||
"generate_now": "Nu genereren",
|
||||
"lastname": "Achternaam",
|
||||
"mandatoryField": "verplicht veld",
|
||||
"memo": "Memo",
|
||||
"message": "Bericht",
|
||||
"new_balance": "Nieuw banksaldo na bevestiging",
|
||||
"no_gdd_available": "Je hebt geen GDD om te versturen.",
|
||||
"password": "Wachtwoord",
|
||||
"passwordRepeat": "Wachtwoord herhalen",
|
||||
"password_new": "Nieuw wachtwoord",
|
||||
"password_new_repeat": "Nieuw wachtwoord herhalen",
|
||||
"password_old": "Oud wachtwoord",
|
||||
"recipient": "Ontvanger",
|
||||
"reset": "Resetten",
|
||||
"save": "Opslaan",
|
||||
"scann_code": "<strong>QR Code Scanner</strong> - Scan de QR Code van uw partner",
|
||||
"sender": "Afzender",
|
||||
"send_check": "Bevestig jouw transactie. Controleer alsjeblieft nogmaals alle gegevens!",
|
||||
"send_now": "Nu versturen",
|
||||
"send_transaction_error": "Helaas kon de transactie niet uitgevoerd worden!",
|
||||
"send_transaction_success": " Jouw transactie werd succesvol uitgevoerd ",
|
||||
"sorry": "Sorry",
|
||||
"thx": "Dankjewel",
|
||||
"time": "Tijd",
|
||||
"to": "tot",
|
||||
"to1": "aan",
|
||||
"validation": {
|
||||
"gddSendAmount": "Het veld {_field_} moet een getal tussen {min} en {max} met maximaal twee cijfers achter de komma zijn",
|
||||
"is-not": "Je kunt geen Gradidos aan jezelf overmaken",
|
||||
"usernmae-regex": "De gebruikersnaam moet met een letter beginnen, waarop minimaal twee alfanumerieke tekens dienen te volgen.",
|
||||
"usernmae-unique": "De gebruikersnaam is al bezet."
|
||||
},
|
||||
"your_amount": "Jouw bijdrage"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"gdd_per_link": {
|
||||
"choose-amount": "Kies een bedrag dat je per link versturen wil. Je kunt ook nog een bericht invullen. Wanneer je „Nu genereren“ klikt, wordt er een link gecreëerd die je kunt versturen.",
|
||||
"copy-link": "Link kopiëren",
|
||||
"copy-link-with-text": "Link en tekst kopiëren",
|
||||
"created": "De link werd gecreëerd!",
|
||||
"credit-your-gradido": "Om de Gradidos bijgeschreven te krijgen, klik op de link!",
|
||||
"decay-14-day": "Vergankelijkheid voor 14 dagen",
|
||||
"delete-the-link": "De link verwijderen?",
|
||||
"deleted": "De link werd verwijderd!",
|
||||
"expiredOn": "Afgelopen op",
|
||||
"has-account": "Heb je al een Gradido rekening?",
|
||||
"header": "Gradidos per link versturen",
|
||||
"isFree": "Gradido is gratis wereldwijd.",
|
||||
"link-and-text-copied": "De link en jouw bericht werden naar het klembord gekopieerd. Je kunt ze nu in een email of bericht invoegen.",
|
||||
"link-copied": "Link werd naar het klembord gekopieerd. Je kunt deze nu in een email of bericht invoegen.",
|
||||
"link-deleted": "De link werd op {date} verwijderd.",
|
||||
"link-expired": "De link is niet meer geldig. De geldigheid is op {date} afgelopen.",
|
||||
"link-overview": "Overzicht links",
|
||||
"links_count": "Actieve links",
|
||||
"links_sum": "Open links en QR-Codes",
|
||||
"no-account": "Je hebt nog geen Gradido rekening?",
|
||||
"no-redeem": "Je mag je eigen link niet inwisselen!",
|
||||
"not-copied": "Jouw apparaat laat het kopiëren helaas niet toe! Kopieer de link alsjeblieft met de hand!",
|
||||
"redeem": "Inwisselen",
|
||||
"redeem-text": "Wil je het bedrag nu inwisselen?",
|
||||
"redeemed": "Succesvol ingewisseld! Op jouw rekening werden {n} GDD bijgeschreven.",
|
||||
"redeemed-at": "De link werd al op {date} ingewisseld.",
|
||||
"redeemed-title": "ingewisseld",
|
||||
"to-login": "Inloggen",
|
||||
"to-register": "Registreer een nieuwe rekening.",
|
||||
"validUntil": "Geldig tot",
|
||||
"validUntilDate": "De link is geldig tot {date}."
|
||||
},
|
||||
"gdt": {
|
||||
"calculation": "Berekening van de Gradido Transform",
|
||||
"contribution": "Bedrag",
|
||||
"conversion": "Omrekening",
|
||||
"conversion-gdt-euro": "Omrekening Euro / Gradido Transform (GDT)",
|
||||
"credit": "Krediet",
|
||||
"factor": "Factor",
|
||||
"formula": "Berekeningsformule",
|
||||
"funding": "Naar de donaties",
|
||||
"gdt": "Gradido Transform",
|
||||
"gdt-received": "Gradido Transform (GDT) ontvangen",
|
||||
"no-transactions": "Je hebt nog geen Gradido Transform (GDT).",
|
||||
"not-reachable": "De GDT server is niet bereikbaar.",
|
||||
"publisher": "Jouw geworven lid heeft een bijdrage betaald ",
|
||||
"raise": "Verhoging",
|
||||
"recruited-member": "Uitgenodigd lid"
|
||||
},
|
||||
"language": "Taal",
|
||||
"link-load": "de laatste link herladen | de laatste links herladen | verdere {n} links herladen",
|
||||
"login": "Aanmelding",
|
||||
"math": {
|
||||
"aprox": "~",
|
||||
"asterisk": "*",
|
||||
"equal": "=",
|
||||
"minus": "−",
|
||||
"pipe": "|"
|
||||
},
|
||||
"message": {
|
||||
"activateEmail": "Jouw rekening werd nog niet geactiveerd. Controleer aljeblieft jouw email en klik de activeringslink. Of vraag een nieuwe activeringlink via de Wachtwoord-Reset-Pagina aan.",
|
||||
"checkEmail": "Jouw email werd succesvol geverifieerd. Je kunt je nu aanmelden.",
|
||||
"email": "We hebben jou een email gestuurd.",
|
||||
"errorTitle": "Opgelet!",
|
||||
"register": "Je bent nu geregistreerd. Controleer alsjeblieft je emails en klik op de activeringslink.",
|
||||
"reset": "Jouw wachtwoord werd gewijzigd.",
|
||||
"title": "Dankjewel!",
|
||||
"unsetPassword": "Jouw wachtwoord werd nog niet ingesteld. Doe het alsjeblieft opnieuw."
|
||||
},
|
||||
"navigation": {
|
||||
"admin_area": "Beheerder",
|
||||
"community": "Gemeenschap",
|
||||
"info": "Informatie",
|
||||
"logout": "Afmelden",
|
||||
"members_area": "Ledenbestand",
|
||||
"overview": "Overzicht",
|
||||
"profile": "Mijn profiel",
|
||||
"send": "Sturen",
|
||||
"support": "Support",
|
||||
"transactions": "Transacties"
|
||||
},
|
||||
"qrCode": "QR Code",
|
||||
"send_gdd": "GDD sturen",
|
||||
"send_per_link": "GDD per link versturen",
|
||||
"session": {
|
||||
"extend": "Aangemeld blijven",
|
||||
"lightText": "Wanneer je langer dan 10 minuten geen actie ondernomen hebt, word je om veiligheidsredenen afgemeld.",
|
||||
"logoutIn": "Afmelden in ",
|
||||
"warningText": "Ben je er nog?"
|
||||
},
|
||||
"settings": {
|
||||
"language": {
|
||||
"changeLanguage": "Taal veranderen",
|
||||
"de": "Duits",
|
||||
"en": "Engels",
|
||||
"es": "Spaans",
|
||||
"fr": "Frans",
|
||||
"nl": "Nederlands",
|
||||
"success": "Jouw taal werd succesvol veranderd."
|
||||
},
|
||||
"name": {
|
||||
"change-name": "Naam veranderen",
|
||||
"change-success": "Jouw naam werd succesvol veranderd."
|
||||
},
|
||||
"newsletter": {
|
||||
"newsletter": "Informatie per email",
|
||||
"newsletterFalse": "Je ontvangt geen informatie per email.",
|
||||
"newsletterTrue": "Je ontvangt informatie per email."
|
||||
},
|
||||
"password": {
|
||||
"change-password": "Wachtwoord veranderen",
|
||||
"forgot_pwd": "Wachtwoord vergeten?",
|
||||
"resend_subtitle": "Jouw activeringslink is afgelopen. Je kunt hier een nieuwe aanvragen.",
|
||||
"reset": "Wachtwoord opnieuw instellen",
|
||||
"reset-password": {
|
||||
"text": "Stel een nieuw wachtwoord in, waarmee je je voortaan in jouw Gradido-rekening wilt aanmelden."
|
||||
},
|
||||
"send_now": "Nu versturen",
|
||||
"set": "Wachtwoord instellen",
|
||||
"set-password": {
|
||||
"text": "Sla nu je nieuwe wachtwoord, waarmee je je voortaan in jouw Gradido-rekening kunt aanmelden, op."
|
||||
},
|
||||
"subtitle": "Wanneer je het wachtwoord hebt vergeten, kun je het hier opnieuw instellen."
|
||||
}
|
||||
},
|
||||
"signin": "Aanmelden",
|
||||
"signup": "Registreren",
|
||||
"site": {
|
||||
"forgotPassword": {
|
||||
"heading": "Geef alsjeblieft jouw email, waarmee je bij Gradido aangemeld bent."
|
||||
},
|
||||
"login": {
|
||||
"heading": "Meld je met jouw inloggegevens aan. Sla deze altijd veilig op!"
|
||||
},
|
||||
"resetPassword": {
|
||||
"heading": "Vul alsjeblieft jouw wachtwoord in, en herhaal het."
|
||||
},
|
||||
"signup": {
|
||||
"agree": "Ik ga akkoord met <a href='https://gradido.net/de/datenschutz/' target='_blank' >Datenschutzerklärung</a>.",
|
||||
"dont_match": "De wachtwoorden zijn niet gelijk.",
|
||||
"heading": "Schrijf je in door alle gegevens volledig en in de juiste velden in te vullen.",
|
||||
"lowercase": "Een kleine letter is noodzakelijk.",
|
||||
"minimum": "Minstens 8 tekens.",
|
||||
"no-whitespace": "Geen spaties en tabs",
|
||||
"one_number": "Getal noodzakelijk.",
|
||||
"special-char": "Speciaal teken noodzakelijk (bijv. _ of é)",
|
||||
"uppercase": "Hoofdletter noodzakelijk."
|
||||
}
|
||||
},
|
||||
"statistic": {
|
||||
"activeUsers": "Actieve leden",
|
||||
"deletedUsers": "Verwijderde leden",
|
||||
"totalGradidoAvailable": "Totaal GDD in omloop",
|
||||
"totalGradidoCreated": "Totaal GDD geschept",
|
||||
"totalGradidoDecayed": "Totaal GDD vervallen",
|
||||
"totalGradidoUnbookedDecayed": "Totaal niet geboekte GDD vervallen"
|
||||
},
|
||||
"success": "Succes",
|
||||
"time": {
|
||||
"days": "Dagen",
|
||||
"hours": "Uren",
|
||||
"minutes": "Minuten",
|
||||
"month": "Maand",
|
||||
"months": "Maanden",
|
||||
"seconds": "Seconden",
|
||||
"years": "Jaar"
|
||||
},
|
||||
"transaction": {
|
||||
"gdd-text": "Gradido transacties",
|
||||
"gdt-text": "GradidoTransform transacties",
|
||||
"nullTransactions": "Je hebt nog geen transacties op jouw rekening.",
|
||||
"receiverDeleted": "De rekening van de ontvanger werd verwijderd",
|
||||
"receiverNotFound": "Ontvanger niet gevonden",
|
||||
"show_all": "Alle <strong>{count}</strong> Transacties bekijken."
|
||||
},
|
||||
"transaction-link": {
|
||||
"send_you": "stuurt jou"
|
||||
},
|
||||
"via_link": "via een link",
|
||||
"welcome": "Welkom in de gemeenschap"
|
||||
}
|
||||
130
frontend/src/pages/InfoStatistic.spec.js
Normal file
130
frontend/src/pages/InfoStatistic.spec.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import InfoStatistic from './InfoStatistic'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
import { listContributionLinks, communityStatistics, searchAdminUsers } from '@/graphql/queries'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
listContributionLinks: {
|
||||
count: 2,
|
||||
links: [
|
||||
{
|
||||
id: 1,
|
||||
amount: 200,
|
||||
name: 'Dokumenta 2017',
|
||||
memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2017',
|
||||
cycle: 'ONCE',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
amount: 200,
|
||||
name: 'Dokumenta 2022',
|
||||
memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022',
|
||||
cycle: 'ONCE',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
searchAdminUsers: {
|
||||
userCount: 2,
|
||||
userList: [
|
||||
{ firstName: 'Peter', lastName: 'Lustig' },
|
||||
{ firstName: 'Super', lastName: 'Admin' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
communityStatistics: {
|
||||
totalUsers: 3113,
|
||||
activeUsers: 1057,
|
||||
deletedUsers: 35,
|
||||
totalGradidoCreated: '4083774.05000000000000000000',
|
||||
totalGradidoDecayed: '-1062639.13634129622923372197',
|
||||
totalGradidoAvailable: '2513565.869444365732411569',
|
||||
totalGradidoUnbookedDecayed: '-500474.6738366222166261272',
|
||||
},
|
||||
},
|
||||
})
|
||||
.mockResolvedValue('default')
|
||||
|
||||
describe('InfoStatistic', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$i18n: {
|
||||
locale: 'en',
|
||||
},
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(InfoStatistic, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the info page', () => {
|
||||
expect(wrapper.find('div.info-statistic').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('calls listContributionLinks', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: listContributionLinks,
|
||||
fetchPolicy: 'network-only',
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('calls searchAdminUsers', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: searchAdminUsers,
|
||||
fetchPolicy: 'network-only',
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('calls getCommunityStatistics', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
query: communityStatistics,
|
||||
fetchPolicy: 'network-only',
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('error apolloQueryMock', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloQueryMock.mockRejectedValue({
|
||||
message: 'uups',
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('toasts three error messages', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith(
|
||||
'listContributionLinks has no result, use default data',
|
||||
)
|
||||
expect(toastErrorSpy).toBeCalledWith('searchAdminUsers has no result, use default data')
|
||||
expect(toastErrorSpy).toBeCalledWith('communityStatistics has no result, use default data')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
166
frontend/src/pages/InfoStatistic.vue
Normal file
166
frontend/src/pages/InfoStatistic.vue
Normal file
@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="info-statistic">
|
||||
<b-container>
|
||||
<div class="h3">{{ $t('community.community') }}</div>
|
||||
<div class="h1">{{ CONFIG.COMMUNITY_NAME }}</div>
|
||||
<div>
|
||||
{{ CONFIG.COMMUNITY_DESCRIPTION }}
|
||||
</div>
|
||||
<div>
|
||||
{{ CONFIG.COMMUNITY_URL }}
|
||||
</div>
|
||||
</b-container>
|
||||
|
||||
<hr />
|
||||
<b-container>
|
||||
<div class="h3">{{ $t('community.openContributionLinks') }}</div>
|
||||
<small>
|
||||
{{
|
||||
$t('community.openContributionLinkText', {
|
||||
name: CONFIG.COMMUNITY_NAME,
|
||||
count,
|
||||
})
|
||||
}}
|
||||
</small>
|
||||
<ul>
|
||||
<li v-for="item in itemsContributionLinks" v-bind:key="item.id">
|
||||
<div>{{ item.name }}</div>
|
||||
<div>{{ item.memo }}</div>
|
||||
<div>
|
||||
{{ item.amount | GDD }}
|
||||
<span v-if="item.cycle === 'ONCE'">{{ $t('contribution-link.unique') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</b-container>
|
||||
<hr />
|
||||
<b-container>
|
||||
<div class="h3">{{ $t('community.moderators') }}</div>
|
||||
<ul>
|
||||
<li v-for="item in itemsAdminUser" v-bind:key="item.id">
|
||||
{{ item.firstName }} {{ item.lastName }}
|
||||
</li>
|
||||
</ul>
|
||||
<b-link href="mailto: abc@example.com">{{ supportMail }}</b-link>
|
||||
</b-container>
|
||||
<hr />
|
||||
<b-container>
|
||||
<div class="h3">{{ $t('community.statistic') }}</div>
|
||||
<div>
|
||||
<div>
|
||||
{{ $t('community.members') }}
|
||||
<span class="h4">{{ totalUsers }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.activeUsers') }}
|
||||
<span class="h4">{{ activeUsers }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.deletedUsers') }}
|
||||
<span class="h4">{{ deletedUsers }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoCreated') }}
|
||||
<span class="h4">{{ totalGradidoCreated | GDD }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoDecayed') }}
|
||||
<span class="h4">{{ totalGradidoDecayed | GDD }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoAvailable') }}
|
||||
<span class="h4">{{ totalGradidoAvailable | GDD }}</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('statistic.totalGradidoUnbookedDecayed') }}
|
||||
<span class="h4">{{ totalGradidoUnbookedDecayed | GDD }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CONFIG from '@/config'
|
||||
import { listContributionLinks, communityStatistics, searchAdminUsers } from '@/graphql/queries'
|
||||
|
||||
export default {
|
||||
name: 'InfoStatistic',
|
||||
data() {
|
||||
return {
|
||||
CONFIG,
|
||||
count: null,
|
||||
countAdminUser: null,
|
||||
itemsContributionLinks: [],
|
||||
itemsAdminUser: [],
|
||||
supportMail: 'support@supportemail.de',
|
||||
membersCount: '1203',
|
||||
totalUsers: null,
|
||||
activeUsers: null,
|
||||
deletedUsers: null,
|
||||
totalGradidoCreated: null,
|
||||
totalGradidoDecayed: null,
|
||||
totalGradidoAvailable: null,
|
||||
totalGradidoUnbookedDecayed: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getContributionLinks() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listContributionLinks,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.count = result.data.listContributionLinks.count
|
||||
this.itemsContributionLinks = result.data.listContributionLinks.links
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('listContributionLinks has no result, use default data')
|
||||
})
|
||||
},
|
||||
getAdminUsers() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: searchAdminUsers,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.countAdminUser = result.data.searchAdminUsers.userCount
|
||||
this.itemsAdminUser = result.data.searchAdminUsers.userList
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('searchAdminUsers has no result, use default data')
|
||||
})
|
||||
},
|
||||
getCommunityStatistics() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: communityStatistics,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.totalUsers = result.data.communityStatistics.totalUsers
|
||||
this.activeUsers = result.data.communityStatistics.activeUsers
|
||||
this.deletedUsers = result.data.communityStatistics.deletedUsers
|
||||
this.totalGradidoCreated = result.data.communityStatistics.totalGradidoCreated
|
||||
this.totalGradidoDecayed = result.data.communityStatistics.totalGradidoDecayed
|
||||
this.totalGradidoAvailable = result.data.communityStatistics.totalGradidoAvailable
|
||||
this.totalGradidoUnbookedDecayed =
|
||||
result.data.communityStatistics.totalGradidoUnbookedDecayed
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('communityStatistics has no result, use default data')
|
||||
})
|
||||
},
|
||||
updateTransactions(pagination) {
|
||||
this.$emit('update-transactions', pagination)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getContributionLinks()
|
||||
this.getAdminUsers()
|
||||
this.getCommunityStatistics()
|
||||
this.updateTransactions(0)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -6,6 +6,7 @@ import { toastErrorSpy } from '@test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mockRouterReplace = jest.fn()
|
||||
const windowScrollToMock = jest.fn()
|
||||
window.scrollTo = windowScrollToMock
|
||||
|
||||
@ -39,6 +40,9 @@ describe('Transactions', () => {
|
||||
$apollo: {
|
||||
query: apolloMock,
|
||||
},
|
||||
$router: {
|
||||
replace: mockRouterReplace,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div class="pb-4">
|
||||
<b-tabs v-model="tabIndex" content-class="" justified>
|
||||
<b-tab :title="`Gradido (${$n(balance, 'decimal')} GDD)`" class="px-4">
|
||||
<b-tab
|
||||
:title="`Gradido (${$n(balance, 'decimal')} GDD)`"
|
||||
class="px-4"
|
||||
@click="$router.replace('/transactions')"
|
||||
>
|
||||
<p class="tab-tex">{{ $t('transaction.gdd-text') }}</p>
|
||||
|
||||
<gdd-transaction-list
|
||||
@ -42,6 +46,7 @@ export default {
|
||||
GdtTransactionList,
|
||||
},
|
||||
props: {
|
||||
gdt: { type: Boolean, default: false },
|
||||
balance: { type: Number, default: 0 },
|
||||
GdtBalance: { type: Number, default: 0 },
|
||||
transactions: {
|
||||
@ -77,6 +82,8 @@ export default {
|
||||
this.transactionsGdt = listGDTEntries.gdtEntries
|
||||
this.transactionGdtCount = listGDTEntries.count
|
||||
window.scrollTo(0, 0)
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.$route.path === '/transactions' ? this.$router.replace('/gdt') : ''
|
||||
})
|
||||
.catch((error) => {
|
||||
this.transactionGdtCount = -1
|
||||
@ -94,10 +101,26 @@ export default {
|
||||
return this.$t('gdt.gdt')
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.gdt) {
|
||||
this.tabIndex = 1
|
||||
this.updateGdt()
|
||||
} else {
|
||||
this.tabIndex = 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.updateGdt()
|
||||
},
|
||||
gdt() {
|
||||
if (this.gdt) {
|
||||
this.tabIndex = 1
|
||||
this.updateGdt()
|
||||
} else {
|
||||
this.tabIndex = 0
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -50,7 +50,7 @@ describe('router', () => {
|
||||
})
|
||||
|
||||
it('has sixteen routes defined', () => {
|
||||
expect(routes).toHaveLength(17)
|
||||
expect(routes).toHaveLength(19)
|
||||
})
|
||||
|
||||
describe('overview', () => {
|
||||
@ -108,6 +108,39 @@ describe('router', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('community', () => {
|
||||
it('requires authorization', () => {
|
||||
expect(routes.find((r) => r.path === '/community').meta.requiresAuth).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads the "Community" page', async () => {
|
||||
const component = await routes.find((r) => r.path === '/community').component()
|
||||
expect(component.default.name).toBe('Community')
|
||||
})
|
||||
})
|
||||
|
||||
describe('info', () => {
|
||||
it('requires authorization', () => {
|
||||
expect(routes.find((r) => r.path === '/information').meta.requiresAuth).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads the "InfoStatistic" page', async () => {
|
||||
const component = await routes.find((r) => r.path === '/information').component()
|
||||
expect(component.default.name).toBe('InfoStatistic')
|
||||
})
|
||||
})
|
||||
|
||||
describe('gdt', () => {
|
||||
it('requires authorization', () => {
|
||||
expect(routes.find((r) => r.path === '/gdt').meta.requiresAuth).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads the "GDT" page', async () => {
|
||||
const component = await routes.find((r) => r.path === '/gdt').component()
|
||||
expect(component.default.name).toBe('Transactions')
|
||||
})
|
||||
})
|
||||
|
||||
describe('login', () => {
|
||||
it('loads the "Login" page', async () => {
|
||||
const component = await routes.find((r) => r.path === '/login/:code?').component()
|
||||
|
||||
@ -34,6 +34,15 @@ const routes = [
|
||||
{
|
||||
path: '/transactions',
|
||||
component: () => import('@/pages/Transactions.vue'),
|
||||
props: { gdt: false },
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/gdt',
|
||||
component: () => import('@/pages/Transactions.vue'),
|
||||
props: { gdt: true },
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
@ -45,6 +54,13 @@ const routes = [
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/information',
|
||||
component: () => import('@/pages/InfoStatistic.vue'),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/login/:code?',
|
||||
component: () => import('@/pages/Login.vue'),
|
||||
|
||||
@ -2156,17 +2156,6 @@
|
||||
jest-message-util "^24.9.0"
|
||||
jest-mock "^24.9.0"
|
||||
|
||||
"@jest/fake-timers@^25.1.0":
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185"
|
||||
integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==
|
||||
dependencies:
|
||||
"@jest/types" "^25.5.0"
|
||||
jest-message-util "^25.5.0"
|
||||
jest-mock "^25.5.0"
|
||||
jest-util "^25.5.0"
|
||||
lolex "^5.0.0"
|
||||
|
||||
"@jest/fake-timers@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad"
|
||||
@ -2378,16 +2367,6 @@
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^13.0.0"
|
||||
|
||||
"@jest/types@^25.5.0":
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d"
|
||||
integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^15.0.0"
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@jest/types@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
|
||||
@ -2482,11 +2461,6 @@
|
||||
string-width "^2.0.0"
|
||||
strip-ansi "^5"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.7":
|
||||
version "7.1.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.17.tgz#f50ac9d20d64153b510578d84f9643f9a3afbe64"
|
||||
@ -3293,11 +3267,6 @@ acorn@^8.0.5:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe"
|
||||
integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA==
|
||||
|
||||
acorn@^8.2.4:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895"
|
||||
integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==
|
||||
|
||||
acorn@^8.5.0, acorn@^8.7.0:
|
||||
version "8.7.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
|
||||
@ -3308,13 +3277,6 @@ address@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
|
||||
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
ajv-errors@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
|
||||
@ -4657,14 +4619,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@ -4995,7 +4949,7 @@ colors@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||
|
||||
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
|
||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
@ -5578,13 +5532,6 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3:
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||
@ -5599,6 +5546,13 @@ debug@^4.0.1, debug@^4.1.1:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3:
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decamelize-keys@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
|
||||
@ -7162,15 +7116,6 @@ forever-agent@~0.6.1:
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
|
||||
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
@ -7864,15 +7809,6 @@ http-parser-js@>=0.5.1:
|
||||
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9"
|
||||
integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==
|
||||
|
||||
http-proxy-agent@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
|
||||
integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
|
||||
dependencies:
|
||||
"@tootallnate/once" "1"
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-proxy-middleware@0.19.1:
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a"
|
||||
@ -7906,14 +7842,6 @@ https-browserify@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||
|
||||
https-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
|
||||
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
human-signals@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||
@ -8430,11 +8358,6 @@ is-potential-custom-element-name@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
|
||||
integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
|
||||
|
||||
is-potential-custom-element-name@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
|
||||
|
||||
is-regex@^1.0.4, is-regex@^1.1.1, is-regex@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251"
|
||||
@ -8849,16 +8772,6 @@ jest-environment-jsdom-fifteen@^1.0.2:
|
||||
jest-util "^24.0.0"
|
||||
jsdom "^15.2.1"
|
||||
|
||||
jest-environment-jsdom-sixteen@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom-sixteen/-/jest-environment-jsdom-sixteen-2.0.0.tgz#0f8c12663ccd9836d248574decffc575bfb091e1"
|
||||
integrity sha512-BF+8P67aEJcd78TQzwSb9P4a73cArOWb5KgqI8eU6cHRWDIJdDRE8XTeZAmOuDSDhKpuEXjKkXwWB3GOJvqHJQ==
|
||||
dependencies:
|
||||
"@jest/fake-timers" "^25.1.0"
|
||||
jest-mock "^25.1.0"
|
||||
jest-util "^25.1.0"
|
||||
jsdom "^16.2.1"
|
||||
|
||||
jest-environment-jsdom@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b"
|
||||
@ -9073,20 +8986,6 @@ jest-message-util@^24.9.0:
|
||||
slash "^2.0.0"
|
||||
stack-utils "^1.0.1"
|
||||
|
||||
jest-message-util@^25.5.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea"
|
||||
integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@jest/types" "^25.5.0"
|
||||
"@types/stack-utils" "^1.0.1"
|
||||
chalk "^3.0.0"
|
||||
graceful-fs "^4.2.4"
|
||||
micromatch "^4.0.2"
|
||||
slash "^3.0.0"
|
||||
stack-utils "^1.0.1"
|
||||
|
||||
jest-message-util@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07"
|
||||
@ -9109,13 +9008,6 @@ jest-mock@^24.0.0, jest-mock@^24.9.0:
|
||||
dependencies:
|
||||
"@jest/types" "^24.9.0"
|
||||
|
||||
jest-mock@^25.1.0, jest-mock@^25.5.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a"
|
||||
integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==
|
||||
dependencies:
|
||||
"@jest/types" "^25.5.0"
|
||||
|
||||
jest-mock@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302"
|
||||
@ -9392,17 +9284,6 @@ jest-util@^24.0.0, jest-util@^24.9.0:
|
||||
slash "^2.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
jest-util@^25.1.0, jest-util@^25.5.0:
|
||||
version "25.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0"
|
||||
integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==
|
||||
dependencies:
|
||||
"@jest/types" "^25.5.0"
|
||||
chalk "^3.0.0"
|
||||
graceful-fs "^4.2.4"
|
||||
is-ci "^2.0.0"
|
||||
make-dir "^3.0.0"
|
||||
|
||||
jest-util@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1"
|
||||
@ -9655,39 +9536,6 @@ jsdom@^15.2.1:
|
||||
ws "^7.0.0"
|
||||
xml-name-validator "^3.0.0"
|
||||
|
||||
jsdom@^16.2.1:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
|
||||
integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==
|
||||
dependencies:
|
||||
abab "^2.0.5"
|
||||
acorn "^8.2.4"
|
||||
acorn-globals "^6.0.0"
|
||||
cssom "^0.4.4"
|
||||
cssstyle "^2.3.0"
|
||||
data-urls "^2.0.0"
|
||||
decimal.js "^10.2.1"
|
||||
domexception "^2.0.1"
|
||||
escodegen "^2.0.0"
|
||||
form-data "^3.0.0"
|
||||
html-encoding-sniffer "^2.0.1"
|
||||
http-proxy-agent "^4.0.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
is-potential-custom-element-name "^1.0.1"
|
||||
nwsapi "^2.2.0"
|
||||
parse5 "6.0.1"
|
||||
saxes "^5.0.1"
|
||||
symbol-tree "^3.2.4"
|
||||
tough-cookie "^4.0.0"
|
||||
w3c-hr-time "^1.0.2"
|
||||
w3c-xmlserializer "^2.0.0"
|
||||
webidl-conversions "^6.1.0"
|
||||
whatwg-encoding "^1.0.5"
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.5.0"
|
||||
ws "^7.4.6"
|
||||
xml-name-validator "^3.0.0"
|
||||
|
||||
jsdom@^16.4.0:
|
||||
version "16.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.1.tgz#4ced6bbd7b77d67fb980e64d9e3e6fb900f97dd6"
|
||||
@ -10050,7 +9898,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0:
|
||||
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@ -10067,13 +9915,6 @@ loglevel@^1.6.8:
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197"
|
||||
integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==
|
||||
|
||||
lolex@^5.0.0:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367"
|
||||
integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==
|
||||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
@ -13848,13 +13689,6 @@ tr46@^2.0.2:
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"
|
||||
integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
@ -14792,15 +14626,6 @@ whatwg-url@^8.0.0:
|
||||
tr46 "^2.0.2"
|
||||
webidl-conversions "^6.1.0"
|
||||
|
||||
whatwg-url@^8.5.0:
|
||||
version "8.7.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
|
||||
integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
|
||||
dependencies:
|
||||
lodash "^4.7.0"
|
||||
tr46 "^2.1.0"
|
||||
webidl-conversions "^6.1.0"
|
||||
|
||||
which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
@ -14923,7 +14748,7 @@ ws@^6.0.0, ws@^6.2.1:
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@^7.0.0, ws@^7.4.6:
|
||||
ws@^7.0.0:
|
||||
version "7.5.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
|
||||
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user