mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into slots-for-right-sidebar
This commit is contained in:
commit
d95dc27e17
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -437,7 +437,7 @@ jobs:
|
|||||||
report_name: Coverage Frontend
|
report_name: Coverage Frontend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./coverage/lcov.info
|
||||||
min_coverage: 89
|
min_coverage: 91
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
@ -3,6 +3,10 @@
|
|||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"esbenp.prettier-vscode",
|
"esbenp.prettier-vscode",
|
||||||
"hediet.vscode-drawio"
|
"hediet.vscode-drawio",
|
||||||
|
"streetsidesoftware.code-spell-checker-german",
|
||||||
|
"mtxr.sqltools",
|
||||||
|
"mtxr.sqltools-driver-mysql",
|
||||||
|
"jcbuisson.vue"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@ -1,3 +1,18 @@
|
|||||||
{
|
{
|
||||||
"git.ignoreLimitWarning": true
|
"git.ignoreLimitWarning": true,
|
||||||
|
"sqltools.connections": [
|
||||||
|
{
|
||||||
|
"mysqlOptions": {
|
||||||
|
"authProtocol": "default"
|
||||||
|
},
|
||||||
|
"previewLimit": 50,
|
||||||
|
"server": "localhost",
|
||||||
|
"port": 3306,
|
||||||
|
"driver": "MariaDB",
|
||||||
|
"name": "localhost",
|
||||||
|
"database": "gradido_community",
|
||||||
|
"username": "root",
|
||||||
|
"password": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
}
|
}
|
||||||
@ -31,7 +31,6 @@
|
|||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"graphql": "^15.5.1",
|
"graphql": "^15.5.1",
|
||||||
"i18n": "^0.15.1",
|
"i18n": "^0.15.1",
|
||||||
"jest": "^27.2.4",
|
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"log4js": "^6.4.6",
|
"log4js": "^6.4.6",
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export enum RIGHTS {
|
|||||||
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS',
|
||||||
CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE',
|
CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE',
|
||||||
LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES',
|
LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES',
|
||||||
|
OPEN_CREATIONS = 'OPEN_CREATIONS',
|
||||||
// Admin
|
// Admin
|
||||||
SEARCH_USERS = 'SEARCH_USERS',
|
SEARCH_USERS = 'SEARCH_USERS',
|
||||||
SET_USER_ROLE = 'SET_USER_ROLE',
|
SET_USER_ROLE = 'SET_USER_ROLE',
|
||||||
|
|||||||
@ -33,6 +33,7 @@ export const ROLE_USER = new Role('user', [
|
|||||||
RIGHTS.COMMUNITY_STATISTICS,
|
RIGHTS.COMMUNITY_STATISTICS,
|
||||||
RIGHTS.CREATE_CONTRIBUTION_MESSAGE,
|
RIGHTS.CREATE_CONTRIBUTION_MESSAGE,
|
||||||
RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES,
|
RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES,
|
||||||
|
RIGHTS.OPEN_CREATIONS,
|
||||||
])
|
])
|
||||||
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
||||||
|
|
||||||
|
|||||||
14
backend/src/graphql/model/OpenCreation.ts
Normal file
14
backend/src/graphql/model/OpenCreation.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { ObjectType, Field, Int } from 'type-graphql'
|
||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class OpenCreation {
|
||||||
|
@Field(() => Int)
|
||||||
|
month: number
|
||||||
|
|
||||||
|
@Field(() => Int)
|
||||||
|
year: number
|
||||||
|
|
||||||
|
@Field(() => Decimal)
|
||||||
|
amount: Decimal
|
||||||
|
}
|
||||||
@ -1,13 +1,11 @@
|
|||||||
import { ObjectType, Field } from 'type-graphql'
|
import { ObjectType, Field } from 'type-graphql'
|
||||||
import { KlickTipp } from './KlickTipp'
|
import { KlickTipp } from './KlickTipp'
|
||||||
import { User as dbUser } from '@entity/User'
|
import { User as dbUser } from '@entity/User'
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { FULL_CREATION_AVAILABLE } from '../resolver/const/const'
|
|
||||||
import { UserContact } from './UserContact'
|
import { UserContact } from './UserContact'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class User {
|
export class User {
|
||||||
constructor(user: dbUser, creation: Decimal[] = FULL_CREATION_AVAILABLE) {
|
constructor(user: dbUser) {
|
||||||
this.id = user.id
|
this.id = user.id
|
||||||
this.gradidoID = user.gradidoID
|
this.gradidoID = user.gradidoID
|
||||||
this.alias = user.alias
|
this.alias = user.alias
|
||||||
@ -26,7 +24,6 @@ export class User {
|
|||||||
this.isAdmin = user.isAdmin
|
this.isAdmin = user.isAdmin
|
||||||
this.klickTipp = null
|
this.klickTipp = null
|
||||||
this.hasElopage = null
|
this.hasElopage = null
|
||||||
this.creation = creation
|
|
||||||
this.hideAmountGDD = user.hideAmountGDD
|
this.hideAmountGDD = user.hideAmountGDD
|
||||||
this.hideAmountGDT = user.hideAmountGDT
|
this.hideAmountGDT = user.hideAmountGDT
|
||||||
}
|
}
|
||||||
@ -34,9 +31,6 @@ export class User {
|
|||||||
@Field(() => Number)
|
@Field(() => Number)
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
// `public_key` binary(32) DEFAULT NULL,
|
|
||||||
// `privkey` binary(80) DEFAULT NULL,
|
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
gradidoID: string
|
gradidoID: string
|
||||||
|
|
||||||
@ -62,9 +56,6 @@ export class User {
|
|||||||
@Field(() => Date, { nullable: true })
|
@Field(() => Date, { nullable: true })
|
||||||
deletedAt: Date | null
|
deletedAt: Date | null
|
||||||
|
|
||||||
// `password` bigint(20) unsigned DEFAULT 0,
|
|
||||||
// `email_hash` binary(32) DEFAULT NULL,
|
|
||||||
|
|
||||||
@Field(() => Date)
|
@Field(() => Date)
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
@ -84,8 +75,6 @@ export class User {
|
|||||||
@Field(() => Number, { nullable: true })
|
@Field(() => Number, { nullable: true })
|
||||||
publisherId: number | null
|
publisherId: number | null
|
||||||
|
|
||||||
// `passphrase` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
|
|
||||||
|
|
||||||
@Field(() => Date, { nullable: true })
|
@Field(() => Date, { nullable: true })
|
||||||
isAdmin: Date | null
|
isAdmin: Date | null
|
||||||
|
|
||||||
@ -94,7 +83,4 @@ export class User {
|
|||||||
|
|
||||||
@Field(() => Boolean, { nullable: true })
|
@Field(() => Boolean, { nullable: true })
|
||||||
hasElopage: boolean | null
|
hasElopage: boolean | null
|
||||||
|
|
||||||
@Field(() => [Decimal])
|
|
||||||
creation: Decimal[]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,8 +11,9 @@ import { Transaction as DbTransaction } from '@entity/Transaction'
|
|||||||
import { AdminCreateContributions } from '@model/AdminCreateContributions'
|
import { AdminCreateContributions } from '@model/AdminCreateContributions'
|
||||||
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
|
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
|
||||||
import { Contribution, ContributionListResult } from '@model/Contribution'
|
import { Contribution, ContributionListResult } from '@model/Contribution'
|
||||||
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
|
||||||
import { Decay } from '@model/Decay'
|
import { Decay } from '@model/Decay'
|
||||||
|
import { OpenCreation } from '@model/OpenCreation'
|
||||||
|
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
||||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||||
import { Order } from '@enum/Order'
|
import { Order } from '@enum/Order'
|
||||||
import { ContributionType } from '@enum/ContributionType'
|
import { ContributionType } from '@enum/ContributionType'
|
||||||
@ -27,6 +28,7 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
|||||||
import { Context, getUser, getClientTimezoneOffset } from '@/server/context'
|
import { Context, getUser, getClientTimezoneOffset } from '@/server/context'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import {
|
import {
|
||||||
|
getCreationDates,
|
||||||
getUserCreation,
|
getUserCreation,
|
||||||
getUserCreations,
|
getUserCreations,
|
||||||
validateContribution,
|
validateContribution,
|
||||||
@ -691,4 +693,23 @@ export class ContributionResolver {
|
|||||||
)
|
)
|
||||||
// return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
|
// return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authorized([RIGHTS.OPEN_CREATIONS])
|
||||||
|
@Query(() => [OpenCreation])
|
||||||
|
async openCreations(
|
||||||
|
@Arg('userId', () => Int, { nullable: true }) userId: number | null,
|
||||||
|
@Ctx() context: Context,
|
||||||
|
): Promise<OpenCreation[]> {
|
||||||
|
const id = userId || getUser(context).id
|
||||||
|
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||||
|
const creationDates = getCreationDates(clientTimezoneOffset)
|
||||||
|
const creations = await getUserCreation(id, clientTimezoneOffset)
|
||||||
|
return creationDates.map((date, index) => {
|
||||||
|
return {
|
||||||
|
month: date.getMonth(),
|
||||||
|
year: date.getFullYear(),
|
||||||
|
amount: creations[index],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,7 +58,7 @@ import {
|
|||||||
EventSendConfirmationEmail,
|
EventSendConfirmationEmail,
|
||||||
EventActivateAccount,
|
EventActivateAccount,
|
||||||
} from '@/event/Event'
|
} from '@/event/Event'
|
||||||
import { getUserCreation, getUserCreations } from './util/creations'
|
import { getUserCreations } from './util/creations'
|
||||||
import { isValidPassword } from '@/password/EncryptorUtils'
|
import { isValidPassword } from '@/password/EncryptorUtils'
|
||||||
import { FULL_CREATION_AVAILABLE } from './const/const'
|
import { FULL_CREATION_AVAILABLE } from './const/const'
|
||||||
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
||||||
@ -114,9 +114,8 @@ export class UserResolver {
|
|||||||
async verifyLogin(@Ctx() context: Context): Promise<User> {
|
async verifyLogin(@Ctx() context: Context): Promise<User> {
|
||||||
logger.info('verifyLogin...')
|
logger.info('verifyLogin...')
|
||||||
// TODO refactor and do not have duplicate code with login(see below)
|
// TODO refactor and do not have duplicate code with login(see below)
|
||||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
|
||||||
const userEntity = getUser(context)
|
const userEntity = getUser(context)
|
||||||
const user = new User(userEntity, await getUserCreation(userEntity.id, clientTimezoneOffset))
|
const user = new User(userEntity)
|
||||||
// Elopage Status & Stored PublisherId
|
// Elopage Status & Stored PublisherId
|
||||||
user.hasElopage = await this.hasElopage(context)
|
user.hasElopage = await this.hasElopage(context)
|
||||||
|
|
||||||
@ -132,7 +131,6 @@ export class UserResolver {
|
|||||||
@Ctx() context: Context,
|
@Ctx() context: Context,
|
||||||
): Promise<User> {
|
): Promise<User> {
|
||||||
logger.info(`login with ${email}, ***, ${publisherId} ...`)
|
logger.info(`login with ${email}, ***, ${publisherId} ...`)
|
||||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
|
||||||
email = email.trim().toLowerCase()
|
email = email.trim().toLowerCase()
|
||||||
const dbUser = await findUserByEmail(email)
|
const dbUser = await findUserByEmail(email)
|
||||||
if (dbUser.deletedAt) {
|
if (dbUser.deletedAt) {
|
||||||
@ -163,7 +161,7 @@ export class UserResolver {
|
|||||||
logger.addContext('user', dbUser.id)
|
logger.addContext('user', dbUser.id)
|
||||||
logger.debug('validation of login credentials successful...')
|
logger.debug('validation of login credentials successful...')
|
||||||
|
|
||||||
const user = new User(dbUser, await getUserCreation(dbUser.id, clientTimezoneOffset))
|
const user = new User(dbUser)
|
||||||
logger.debug(`user= ${JSON.stringify(user, null, 2)}`)
|
logger.debug(`user= ${JSON.stringify(user, null, 2)}`)
|
||||||
|
|
||||||
i18n.setLocale(user.language)
|
i18n.setLocale(user.language)
|
||||||
|
|||||||
@ -101,15 +101,19 @@ export const getUserCreation = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getCreationMonths = (timezoneOffset: number): number[] => {
|
const getCreationMonths = (timezoneOffset: number): number[] => {
|
||||||
|
return getCreationDates(timezoneOffset).map((date) => date.getMonth() + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCreationDates = (timezoneOffset: number): Date[] => {
|
||||||
const clientNow = new Date()
|
const clientNow = new Date()
|
||||||
clientNow.setTime(clientNow.getTime() - timezoneOffset * 60 * 1000)
|
clientNow.setTime(clientNow.getTime() - timezoneOffset * 60 * 1000)
|
||||||
logger.info(
|
logger.info(
|
||||||
`getCreationMonths -- offset: ${timezoneOffset} -- clientNow: ${clientNow.toISOString()}`,
|
`getCreationMonths -- offset: ${timezoneOffset} -- clientNow: ${clientNow.toISOString()}`,
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
new Date(clientNow.getFullYear(), clientNow.getMonth() - 2, 1).getMonth() + 1,
|
new Date(clientNow.getFullYear(), clientNow.getMonth() - 2, 1),
|
||||||
new Date(clientNow.getFullYear(), clientNow.getMonth() - 1, 1).getMonth() + 1,
|
new Date(clientNow.getFullYear(), clientNow.getMonth() - 1, 1),
|
||||||
clientNow.getMonth() + 1,
|
clientNow,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -231,3 +231,32 @@ This opens the `crontab` in edit-mode and insert the following entry:
|
|||||||
```bash
|
```bash
|
||||||
0 4 * * * find /tmp -name "yarn--*" -ctime +1 -exec rm -r {} \; > /dev/null
|
0 4 * * * find /tmp -name "yarn--*" -ctime +1 -exec rm -r {} \; > /dev/null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Define Cronjob To start backup script automatically
|
||||||
|
|
||||||
|
At least at production stage we need a daily backup of our database. This can be done by adding a cronjob
|
||||||
|
to start the existing backup.sh script.
|
||||||
|
|
||||||
|
### On production / stage3 / stage2
|
||||||
|
|
||||||
|
To check for existing cronjobs for the `gradido` user, please
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -l
|
||||||
|
```
|
||||||
|
|
||||||
|
This show all existing entries of the crontab for user `gradido`
|
||||||
|
|
||||||
|
To install/add the cronjob for a daily backup at 3:00am please
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
and insert the following line
|
||||||
|
```bash
|
||||||
|
0 3 * * * ~/gradido/deployment/bare_metal/backup.sh
|
||||||
|
```
|
||||||
|
|||||||
@ -0,0 +1,103 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import OpenCreationsAmount from './OpenCreationsAmount.vue'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('OpenCreationsAmount', () => {
|
||||||
|
let wrapper
|
||||||
|
|
||||||
|
const mocks = {
|
||||||
|
$t: jest.fn((t) => t),
|
||||||
|
$d: jest.fn((date, formatter = null) => {
|
||||||
|
return { date, formatter }
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisMonth = new Date()
|
||||||
|
const lastMonth = new Date(thisMonth.getFullYear(), thisMonth.getMonth() - 1)
|
||||||
|
|
||||||
|
const propsData = {
|
||||||
|
minimalDate: lastMonth,
|
||||||
|
maxGddLastMonth: 400,
|
||||||
|
maxGddThisMonth: 600,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(OpenCreationsAmount, {
|
||||||
|
localVue,
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the component', () => {
|
||||||
|
expect(wrapper.find('div.appBoxShadow').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders two dates', () => {
|
||||||
|
expect(mocks.$d).toBeCalledTimes(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the date of last month', () => {
|
||||||
|
expect(mocks.$d).toBeCalledWith(lastMonth, 'monthAndYear')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders the date of this month', () => {
|
||||||
|
expect(mocks.$d).toBeCalledWith(expect.any(Date), 'monthAndYear')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('open creations for both months', () => {
|
||||||
|
it('renders submitted contributions text', () => {
|
||||||
|
expect(mocks.$t).toBeCalledWith('contribution.submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not render max reached text', () => {
|
||||||
|
expect(mocks.$t).not.toBeCalledWith('maxReached')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders submitted hours last month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(1).findAll('div.col').at(2).text()).toBe('30 h')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders available hours last month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(1).findAll('div.col').at(3).text()).toBe('20 h')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders submitted hours this month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(2).findAll('div.col').at(2).text()).toBe('20 h')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders available hours this month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(2).findAll('div.col').at(3).text()).toBe('30 h')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('no creations available for last month', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper.setProps({ maxGddLastMonth: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders submitted contributions text', () => {
|
||||||
|
expect(mocks.$t).toBeCalledWith('contribution.submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders max reached text', () => {
|
||||||
|
expect(mocks.$t).toBeCalledWith('maxReached')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders submitted hours last month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(1).findAll('div.col').at(2).text()).toBe('50 h')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders available hours last month', () => {
|
||||||
|
expect(wrapper.findAll('div.row').at(1).findAll('div.col').at(3).text()).toBe('0 h')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -14,9 +14,9 @@
|
|||||||
{{ maxGddLastMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
|
{{ maxGddLastMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="d-none d-md-inline text-197 text-center">
|
<b-col class="d-none d-md-inline text-197 text-center">
|
||||||
{{ (1000 - maxGddLastMonth) / 20 }} {{ $t('h') }}
|
{{ hoursSubmittedLastMonth }} {{ $t('h') }}
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="text-4 text-center">{{ maxGddLastMonth / 20 }} {{ $t('h') }}</b-col>
|
<b-col class="text-4 text-center">{{ hoursAvailableLastMonth }} {{ $t('h') }}</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
|
|
||||||
<b-row class="font-weight-bold">
|
<b-row class="font-weight-bold">
|
||||||
@ -25,9 +25,9 @@
|
|||||||
{{ maxGddThisMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
|
{{ maxGddThisMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="d-none d-md-inline text-197 text-center">
|
<b-col class="d-none d-md-inline text-197 text-center">
|
||||||
{{ (1000 - maxGddThisMonth) / 20 }} {{ $t('h') }}
|
{{ hoursSubmittedThisMonth }} {{ $t('h') }}
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="text-4 text-center">{{ maxGddThisMonth / 20 }} {{ $t('h') }}</b-col>
|
<b-col class="text-4 text-center">{{ hoursAvailableThisMonth }} {{ $t('h') }}</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -40,5 +40,19 @@ export default {
|
|||||||
maxGddLastMonth: { type: Number, required: true },
|
maxGddLastMonth: { type: Number, required: true },
|
||||||
maxGddThisMonth: { type: Number, required: true },
|
maxGddThisMonth: { type: Number, required: true },
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
hoursSubmittedThisMonth() {
|
||||||
|
return (1000 - this.maxGddThisMonth) / 20
|
||||||
|
},
|
||||||
|
hoursSubmittedLastMonth() {
|
||||||
|
return (1000 - this.maxGddLastMonth) / 20
|
||||||
|
},
|
||||||
|
hoursAvailableThisMonth() {
|
||||||
|
return this.maxGddThisMonth / 20
|
||||||
|
},
|
||||||
|
hoursAvailableLastMonth() {
|
||||||
|
return this.maxGddLastMonth / 20
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,112 +1,115 @@
|
|||||||
<template>
|
<template>
|
||||||
<b-row class="transaction-form">
|
<div class="transaction-form">
|
||||||
<b-col cols="12">
|
<b-row>
|
||||||
<b-card class="appBoxShadow gradido-border-radius" body-class="p-3">
|
<b-col cols="12">
|
||||||
<validation-observer v-slot="{ handleSubmit }" ref="formValidator">
|
<b-card class="appBoxShadow gradido-border-radius" body-class="p-3">
|
||||||
<b-form role="form" @submit.prevent="handleSubmit(onSubmit)" @reset="onReset">
|
<validation-observer v-slot="{ handleSubmit }" ref="formValidator">
|
||||||
<b-form-radio-group v-model="radioSelected" class="container">
|
<b-form role="form" @submit.prevent="handleSubmit(onSubmit)" @reset="onReset">
|
||||||
<b-row class="mb-4">
|
<b-form-radio-group v-model="radioSelected" class="container">
|
||||||
<b-col cols="12" lg="6">
|
<b-row class="mb-4">
|
||||||
<b-row class="bg-248 gradido-border-radius pt-lg-2 mr-lg-2">
|
<b-col cols="12" lg="6">
|
||||||
<b-col cols="10" @click="radioSelected = sendTypes.send" class="pointer">
|
<b-row class="bg-248 gradido-border-radius pt-lg-2 mr-lg-2">
|
||||||
{{ $t('send_gdd') }}
|
<b-col cols="10" @click="radioSelected = sendTypes.send" class="pointer">
|
||||||
</b-col>
|
{{ $t('send_gdd') }}
|
||||||
<b-col cols="2">
|
</b-col>
|
||||||
<b-form-radio
|
<b-col cols="2">
|
||||||
name="shipping"
|
<b-form-radio
|
||||||
size="lg"
|
name="shipping"
|
||||||
:value="sendTypes.send"
|
size="lg"
|
||||||
stacked
|
:value="sendTypes.send"
|
||||||
class="custom-radio-button pointer"
|
stacked
|
||||||
></b-form-radio>
|
class="custom-radio-button pointer"
|
||||||
</b-col>
|
></b-form-radio>
|
||||||
</b-row>
|
</b-col>
|
||||||
</b-col>
|
</b-row>
|
||||||
|
</b-col>
|
||||||
|
<b-col>
|
||||||
|
<b-row class="bg-248 gradido-border-radius pt-lg-2 ml-lg-2 mt-2 mt-lg-0">
|
||||||
|
<b-col cols="10" @click="radioSelected = sendTypes.link" class="pointer">
|
||||||
|
{{ $t('send_per_link') }}
|
||||||
|
</b-col>
|
||||||
|
<b-col cols="2" class="pointer">
|
||||||
|
<b-form-radio
|
||||||
|
name="shipping"
|
||||||
|
:value="sendTypes.link"
|
||||||
|
size="lg"
|
||||||
|
class="custom-radio-button"
|
||||||
|
></b-form-radio>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
|
||||||
|
<div class="mt-4 mb-4" v-if="radioSelected === sendTypes.link">
|
||||||
|
<h2 class="alert-heading">{{ $t('gdd_per_link.header') }}</h2>
|
||||||
|
<div>
|
||||||
|
{{ $t('gdd_per_link.choose-amount') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-form-radio-group>
|
||||||
|
<b-row>
|
||||||
<b-col>
|
<b-col>
|
||||||
<b-row class="bg-248 gradido-border-radius pt-lg-2 ml-lg-2 mt-2 mt-lg-0">
|
<b-row>
|
||||||
<b-col cols="10" @click="radioSelected = sendTypes.link" class="pointer">
|
<b-col cols="12">
|
||||||
{{ $t('send_per_link') }}
|
<div v-if="radioSelected === sendTypes.send">
|
||||||
|
<input-email
|
||||||
|
:name="$t('form.recipient')"
|
||||||
|
:label="$t('form.recipient')"
|
||||||
|
:placeholder="$t('form.email')"
|
||||||
|
v-model="form.email"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
|
@onValidation="onValidation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="2" class="pointer">
|
<b-col cols="12" lg="6">
|
||||||
<b-form-radio
|
<input-amount
|
||||||
name="shipping"
|
v-model="form.amount"
|
||||||
:value="sendTypes.link"
|
:name="$t('form.amount')"
|
||||||
size="lg"
|
:label="$t('form.amount')"
|
||||||
class="custom-radio-button"
|
:placeholder="'0.01'"
|
||||||
></b-form-radio>
|
:rules="{ required: true, gddSendAmount: [0.01, balance] }"
|
||||||
|
typ="TransactionForm"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
|
></input-amount>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
|
|
||||||
<div class="mt-4 mb-4" v-if="radioSelected === sendTypes.link">
|
<b-row>
|
||||||
<h2 class="alert-heading">{{ $t('gdd_per_link.header') }}</h2>
|
<b-col>
|
||||||
<div>
|
<input-textarea
|
||||||
{{ $t('gdd_per_link.choose-amount') }}
|
v-model="form.memo"
|
||||||
</div>
|
:name="$t('form.message')"
|
||||||
|
:label="$t('form.message')"
|
||||||
|
:placeholder="$t('form.message')"
|
||||||
|
:rules="{ required: true, min: 5, max: 255 }"
|
||||||
|
:disabled="isBalanceDisabled"
|
||||||
|
/>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
<div v-if="!!isBalanceDisabled" class="text-danger mt-5">
|
||||||
|
{{ $t('form.no_gdd_available') }}
|
||||||
</div>
|
</div>
|
||||||
</b-form-radio-group>
|
<b-row v-else class="test-buttons mt-5">
|
||||||
<b-row>
|
<b-col>
|
||||||
<b-col>
|
<b-button type="reset" variant="secondary" @click="onReset">
|
||||||
<b-row>
|
{{ $t('form.reset') }}
|
||||||
<b-col cols="12">
|
</b-button>
|
||||||
<div v-if="radioSelected === sendTypes.send">
|
</b-col>
|
||||||
<input-email
|
<b-col class="text-right">
|
||||||
:name="$t('form.recipient')"
|
<b-button type="submit" variant="gradido">
|
||||||
:label="$t('form.recipient')"
|
{{ $t('form.check_now') }}
|
||||||
:placeholder="$t('form.email')"
|
</b-button>
|
||||||
v-model="form.email"
|
</b-col>
|
||||||
:disabled="isBalanceDisabled"
|
</b-row>
|
||||||
/>
|
</b-form>
|
||||||
</div>
|
</validation-observer>
|
||||||
</b-col>
|
</b-card>
|
||||||
<b-col cols="12" lg="6">
|
</b-col>
|
||||||
<input-amount
|
</b-row>
|
||||||
v-model="form.amount"
|
</div>
|
||||||
:name="$t('form.amount')"
|
|
||||||
:label="$t('form.amount')"
|
|
||||||
:placeholder="'0.01'"
|
|
||||||
:rules="{ required: true, gddSendAmount: [0.01, balance] }"
|
|
||||||
typ="TransactionForm"
|
|
||||||
:disabled="isBalanceDisabled"
|
|
||||||
></input-amount>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<input-textarea
|
|
||||||
v-model="form.memo"
|
|
||||||
:name="$t('form.message')"
|
|
||||||
:label="$t('form.message')"
|
|
||||||
:placeholder="$t('form.message')"
|
|
||||||
:rules="{ required: true, min: 5, max: 255 }"
|
|
||||||
:disabled="isBalanceDisabled"
|
|
||||||
/>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<div v-if="!!isBalanceDisabled" class="text-danger mt-5">
|
|
||||||
{{ $t('form.no_gdd_available') }}
|
|
||||||
</div>
|
|
||||||
<b-row v-else class="test-buttons mt-5">
|
|
||||||
<b-col>
|
|
||||||
<b-button type="reset" variant="secondary" @click="onReset">
|
|
||||||
{{ $t('form.reset') }}
|
|
||||||
</b-button>
|
|
||||||
</b-col>
|
|
||||||
<b-col class="text-right">
|
|
||||||
<b-button type="submit" variant="gradido">
|
|
||||||
{{ $t('form.check_now') }}
|
|
||||||
</b-button>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-form>
|
|
||||||
</validation-observer>
|
|
||||||
</b-card>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { SEND_TYPES } from '@/pages/Send.vue'
|
import { SEND_TYPES } from '@/pages/Send.vue'
|
||||||
@ -140,6 +143,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onValidation() {
|
||||||
|
this.$refs.formValidator.validate()
|
||||||
|
},
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
this.$emit('set-transaction', {
|
this.$emit('set-transaction', {
|
||||||
selected: this.radioSelected,
|
selected: this.radioSelected,
|
||||||
@ -153,6 +159,7 @@ export default {
|
|||||||
this.form.email = ''
|
this.form.email = ''
|
||||||
this.form.amount = ''
|
this.form.amount = ''
|
||||||
this.form.memo = ''
|
this.form.memo = ''
|
||||||
|
this.$refs.formValidator.validate()
|
||||||
},
|
},
|
||||||
setNewRecipientEmail() {
|
setNewRecipientEmail() {
|
||||||
this.form.email = this.recipientEmail ? this.recipientEmail : this.form.email
|
this.form.email = this.recipientEmail ? this.recipientEmail : this.form.email
|
||||||
@ -177,6 +184,9 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
this.setNewRecipientEmail()
|
this.setNewRecipientEmail()
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.form.email !== '') this.$refs.formValidator.validate()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
<div v-else>{{ errorResult }}</div>
|
<div v-else>{{ errorResult }}</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-center mt-5">
|
<p class="text-center mt-5">
|
||||||
<b-button variant="secondary" @click="$emit('on-reset')">
|
<b-button variant="secondary" @click="$emit('on-back')">
|
||||||
{{ $t('form.close') }}
|
{{ $t('form.close') }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
@focus="amountFocused = true"
|
@focus="amountFocused = true"
|
||||||
@blur="normalizeAmount(true)"
|
@blur="normalizeAmount(true)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
autocomplete="off"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
|
|
||||||
<b-form-invalid-feedback v-bind="ariaMsg">
|
<b-form-invalid-feedback v-bind="ariaMsg">
|
||||||
@ -63,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentValue: '',
|
currentValue: this.value,
|
||||||
amountValue: 0.0,
|
amountValue: 0.0,
|
||||||
amountFocused: false,
|
amountFocused: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
@focus="emailFocused = true"
|
@focus="emailFocused = true"
|
||||||
@blur="normalizeEmail()"
|
@blur="normalizeEmail()"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
autocomplete="off"
|
||||||
></b-form-input>
|
></b-form-input>
|
||||||
<b-form-invalid-feedback v-bind="ariaMsg">
|
<b-form-invalid-feedback v-bind="ariaMsg">
|
||||||
{{ errors[0] }}
|
{{ errors[0] }}
|
||||||
@ -62,7 +63,10 @@ export default {
|
|||||||
this.$emit('input', this.currentValue)
|
this.$emit('input', this.currentValue)
|
||||||
},
|
},
|
||||||
value() {
|
value() {
|
||||||
if (this.value !== this.currentValue) this.currentValue = this.value
|
if (this.value !== this.currentValue) {
|
||||||
|
this.currentValue = this.value
|
||||||
|
}
|
||||||
|
this.$emit('onValidation')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@ -58,7 +58,7 @@ describe('InputTextarea', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('has the value ""', () => {
|
it('has the value ""', () => {
|
||||||
expect(wrapper.vm.currentValue).toEqual('')
|
expect(wrapper.vm.currentValue).toEqual('Long enough')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has the label "input-field-label"', () => {
|
it('has the label "input-field-label"', () => {
|
||||||
@ -72,9 +72,8 @@ describe('InputTextarea', () => {
|
|||||||
|
|
||||||
describe('input value changes', () => {
|
describe('input value changes', () => {
|
||||||
it('emits input with new value', async () => {
|
it('emits input with new value', async () => {
|
||||||
await wrapper.find('textarea').setValue('Long enough')
|
await wrapper.find('textarea').setValue('New Text')
|
||||||
expect(wrapper.emitted('input')).toBeTruthy()
|
expect(wrapper.emitted('input')).toEqual([['New Text']])
|
||||||
expect(wrapper.emitted('input')).toEqual([['Long enough']])
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentValue: '',
|
currentValue: this.value,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
<b-icon icon="layers" aria-hidden="true"></b-icon>
|
<b-icon icon="layers" aria-hidden="true"></b-icon>
|
||||||
<span class="ml-2">{{ $t('gdt.gdt') }}</span>
|
<span class="ml-2">{{ $t('gdt.gdt') }}</span>
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
<b-nav-item to="/community#my" class="" active-class="activeRoute">
|
<b-nav-item to="/community" class="" active-class="activeRoute">
|
||||||
<b-icon icon="people" aria-hidden="true"></b-icon>
|
<b-icon icon="people" aria-hidden="true"></b-icon>
|
||||||
<span class="ml-2">{{ $t('creation') }}</span>
|
<span class="ml-2">{{ $t('creation') }}</span>
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="nav-community">
|
<div class="nav-community container">
|
||||||
<b-row class="nav-row">
|
<b-row class="nav-row">
|
||||||
<b-col cols="12" lg="4" md="4">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="#edit">
|
<b-btn active-class="btn-active" block variant="link" to="/community#edit">
|
||||||
<b-icon icon="pencil" class="mr-2" />
|
<b-icon icon="pencil" class="mr-2" />
|
||||||
{{ $t('community.submitContribution') }}
|
{{ $t('community.submitContribution') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="12" lg="4" md="4">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="#my">
|
<b-btn active-class="btn-active" block variant="link" to="/community#my">
|
||||||
<b-icon icon="person" class="mr-2" />
|
<b-icon icon="person" class="mr-2" />
|
||||||
{{ $t('community.myContributions') }}
|
{{ $t('community.myContributions') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="12" lg="4" md="4">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="#all">
|
<b-btn active-class="btn-active" block variant="link" to="/community#all">
|
||||||
<b-icon icon="people" class="mr-2" />
|
<b-icon icon="people" class="mr-2" />
|
||||||
{{ $t('community.community') }}
|
{{ $t('community.community') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
:size="72"
|
:size="72"
|
||||||
:color="'#fff'"
|
:color="'#fff'"
|
||||||
:username="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`"
|
:username="`${transaction.linkedUser.firstName} ${transaction.linkedUser.lastName}`"
|
||||||
:initials="`${transaction.linkedUser.firstName[0]} ${transaction.linkedUser.lastName[0]}`"
|
:initials="`${transaction.linkedUser.firstName[0]}${transaction.linkedUser.lastName[0]}`"
|
||||||
></avatar>
|
></avatar>
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
|
|||||||
@ -154,7 +154,6 @@ export const login = gql`
|
|||||||
hasElopage
|
hasElopage
|
||||||
publisherId
|
publisherId
|
||||||
isAdmin
|
isAdmin
|
||||||
creation
|
|
||||||
hideAmountGDD
|
hideAmountGDD
|
||||||
hideAmountGDT
|
hideAmountGDT
|
||||||
}
|
}
|
||||||
|
|||||||
@ -250,3 +250,13 @@ export const listContributionMessages = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const openCreations = gql`
|
||||||
|
query {
|
||||||
|
openCreations {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|||||||
@ -2,33 +2,14 @@ import { mount } from '@vue/test-utils'
|
|||||||
import Community from './Community'
|
import Community from './Community'
|
||||||
import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup'
|
import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup'
|
||||||
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
||||||
import { listContributions, listAllContributions, verifyLogin } from '@/graphql/queries'
|
import { listContributions, listAllContributions } from '@/graphql/queries'
|
||||||
|
|
||||||
import VueRouter from 'vue-router'
|
|
||||||
import routes from '../routes/routes'
|
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
localVue.use(VueRouter)
|
|
||||||
|
|
||||||
const mockStoreDispach = jest.fn()
|
const mockStoreDispach = jest.fn()
|
||||||
const apolloQueryMock = jest.fn()
|
const apolloQueryMock = jest.fn()
|
||||||
const apolloMutationMock = jest.fn()
|
const apolloMutationMock = jest.fn()
|
||||||
|
const apolloRefetchMock = jest.fn()
|
||||||
const router = new VueRouter({
|
|
||||||
base: '/',
|
|
||||||
routes,
|
|
||||||
linkActiveClass: 'active',
|
|
||||||
mode: 'history',
|
|
||||||
// scrollBehavior: (to, from, savedPosition) => {
|
|
||||||
// if (savedPosition) {
|
|
||||||
// return savedPosition
|
|
||||||
// }
|
|
||||||
// if (to.hash) {
|
|
||||||
// return { selector: to.hash }
|
|
||||||
// }
|
|
||||||
// return { x: 0, y: 0 }
|
|
||||||
// },
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Community', () => {
|
describe('Community', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
@ -39,6 +20,11 @@ describe('Community', () => {
|
|||||||
$apollo: {
|
$apollo: {
|
||||||
query: apolloQueryMock,
|
query: apolloQueryMock,
|
||||||
mutate: apolloMutationMock,
|
mutate: apolloMutationMock,
|
||||||
|
queries: {
|
||||||
|
OpenCreations: {
|
||||||
|
refetch: apolloRefetchMock,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
$store: {
|
$store: {
|
||||||
dispatch: mockStoreDispach,
|
dispatch: mockStoreDispach,
|
||||||
@ -49,12 +35,17 @@ describe('Community', () => {
|
|||||||
$i18n: {
|
$i18n: {
|
||||||
locale: 'en',
|
locale: 'en',
|
||||||
},
|
},
|
||||||
|
$router: {
|
||||||
|
push: jest.fn(),
|
||||||
|
},
|
||||||
|
$route: {
|
||||||
|
hash: 'my',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
return mount(Community, {
|
return mount(Community, {
|
||||||
localVue,
|
localVue,
|
||||||
router,
|
|
||||||
mocks,
|
mocks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -207,10 +198,7 @@ describe('Community', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('verifies the login (to get the new creations available)', () => {
|
it('verifies the login (to get the new creations available)', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith({
|
expect(apolloRefetchMock).toBeCalled()
|
||||||
query: verifyLogin,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('set all data to the default values)', () => {
|
it('set all data to the default values)', () => {
|
||||||
@ -294,10 +282,7 @@ describe('Community', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('verifies the login (to get the new creations available)', () => {
|
it('verifies the login (to get the new creations available)', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith({
|
expect(apolloRefetchMock).toBeCalled()
|
||||||
query: verifyLogin,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('set all data to the default values)', () => {
|
it('set all data to the default values)', () => {
|
||||||
@ -376,10 +361,7 @@ describe('Community', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('verifies the login (to get the new creations available)', () => {
|
it('verifies the login (to get the new creations available)', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith({
|
expect(apolloRefetchMock).toBeCalled()
|
||||||
query: verifyLogin,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
<b-tab no-body>
|
<b-tab no-body>
|
||||||
<open-creations-amount
|
<open-creations-amount
|
||||||
:minimalDate="minimalDate"
|
:minimalDate="minimalDate"
|
||||||
:maxGddThisMonth="maxGddThisMonth"
|
:maxGddLastMonth="maxForMonths[0]"
|
||||||
:maxGddLastMonth="maxGddLastMonth"
|
:maxGddThisMonth="maxForMonths[1]"
|
||||||
/>
|
/>
|
||||||
<div class="mb-3"></div>
|
<div class="mb-3"></div>
|
||||||
<contribution-form
|
<contribution-form
|
||||||
@ -15,8 +15,8 @@
|
|||||||
v-model="form"
|
v-model="form"
|
||||||
:isThisMonth="isThisMonth"
|
:isThisMonth="isThisMonth"
|
||||||
:minimalDate="minimalDate"
|
:minimalDate="minimalDate"
|
||||||
:maxGddLastMonth="maxGddLastMonth"
|
:maxGddLastMonth="maxForMonths[0]"
|
||||||
:maxGddThisMonth="maxGddThisMonth"
|
:maxGddThisMonth="maxForMonths[1]"
|
||||||
/>
|
/>
|
||||||
</b-tab>
|
</b-tab>
|
||||||
<b-tab no-body>
|
<b-tab no-body>
|
||||||
@ -52,7 +52,7 @@ import OpenCreationsAmount from '@/components/Contributions/OpenCreationsAmount.
|
|||||||
import ContributionForm from '@/components/Contributions/ContributionForm.vue'
|
import ContributionForm from '@/components/Contributions/ContributionForm.vue'
|
||||||
import ContributionList from '@/components/Contributions/ContributionList.vue'
|
import ContributionList from '@/components/Contributions/ContributionList.vue'
|
||||||
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
||||||
import { listContributions, listAllContributions, verifyLogin } from '@/graphql/queries'
|
import { listContributions, listAllContributions, openCreations } from '@/graphql/queries'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Community',
|
name: 'Community',
|
||||||
@ -82,6 +82,7 @@ export default {
|
|||||||
},
|
},
|
||||||
updateAmount: '',
|
updateAmount: '',
|
||||||
maximalDate: new Date(),
|
maximalDate: new Date(),
|
||||||
|
openCreations: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -90,6 +91,23 @@ export default {
|
|||||||
this.hashLink = this.$route.hash
|
this.hashLink = this.$route.hash
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
apollo: {
|
||||||
|
OpenCreations: {
|
||||||
|
query() {
|
||||||
|
return openCreations
|
||||||
|
},
|
||||||
|
fetchPolicy: 'network-only',
|
||||||
|
variables() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
update({ openCreations }) {
|
||||||
|
this.openCreations = openCreations
|
||||||
|
},
|
||||||
|
error({ message }) {
|
||||||
|
this.toastError(message)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route(to, from) {
|
$route(to, from) {
|
||||||
this.tabIndex = this.tabLinkHashes.findIndex((hashLink) => hashLink === to.hash)
|
this.tabIndex = this.tabLinkHashes.findIndex((hashLink) => hashLink === to.hash)
|
||||||
@ -120,17 +138,20 @@ export default {
|
|||||||
formDate.getMonth() === this.maximalDate.getMonth()
|
formDate.getMonth() === this.maximalDate.getMonth()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
maxGddLastMonth() {
|
amountToAdd() {
|
||||||
// when existing contribution is 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
|
if (this.form.id) return parseInt(this.updateAmount)
|
||||||
? parseInt(this.$store.state.creation[1]) + parseInt(this.updateAmount)
|
return 0
|
||||||
: parseInt(this.$store.state.creation[1])
|
|
||||||
},
|
},
|
||||||
maxGddThisMonth() {
|
maxForMonths() {
|
||||||
// when existing contribution is edited, the amount is added back on top of the amount
|
const formDate = new Date(this.form.date)
|
||||||
return this.form.id && this.isThisMonth
|
if (this.openCreations && this.openCreations.length)
|
||||||
? parseInt(this.$store.state.creation[2]) + parseInt(this.updateAmount)
|
return this.openCreations.slice(1).map((creation) => {
|
||||||
: parseInt(this.$store.state.creation[2])
|
if (creation.year === formDate.getFullYear() && creation.month === formDate.getMonth())
|
||||||
|
return parseInt(creation.amount) + this.amountToAdd
|
||||||
|
return parseInt(creation.amount)
|
||||||
|
})
|
||||||
|
return [0, 0]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -160,7 +181,7 @@ export default {
|
|||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
})
|
})
|
||||||
this.verifyLogin()
|
this.$apollo.queries.OpenCreations.refetch()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.toastError(err.message)
|
this.toastError(err.message)
|
||||||
@ -188,7 +209,7 @@ export default {
|
|||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
})
|
})
|
||||||
this.verifyLogin()
|
this.$apollo.queries.OpenCreations.refetch()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.toastError(err.message)
|
this.toastError(err.message)
|
||||||
@ -213,7 +234,7 @@ export default {
|
|||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
})
|
})
|
||||||
this.verifyLogin()
|
this.$apollo.queries.OpenCreations.refetch()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.toastError(err.message)
|
this.toastError(err.message)
|
||||||
@ -259,7 +280,7 @@ export default {
|
|||||||
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
|
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
|
||||||
this.tabIndex = 1
|
this.tabIndex = 1
|
||||||
if (this.$route.hash !== '#my') {
|
if (this.$route.hash !== '#my') {
|
||||||
this.$router.push({ path: '#my' })
|
this.$router.push({ path: '/community#my' })
|
||||||
}
|
}
|
||||||
this.toastInfo('Du hast eine Rückfrage auf eine Contribution. Bitte beantworte diese!')
|
this.toastInfo('Du hast eine Rückfrage auf eine Contribution. Bitte beantworte diese!')
|
||||||
}
|
}
|
||||||
@ -268,22 +289,6 @@ export default {
|
|||||||
this.toastError(err.message)
|
this.toastError(err.message)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
verifyLogin() {
|
|
||||||
this.$apollo
|
|
||||||
.query({
|
|
||||||
query: verifyLogin,
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
const {
|
|
||||||
data: { verifyLogin },
|
|
||||||
} = result
|
|
||||||
this.$store.dispatch('login', verifyLogin)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
this.$emit('logout')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
updateContributionForm(item) {
|
updateContributionForm(item) {
|
||||||
this.form.id = item.id
|
this.form.id = item.id
|
||||||
this.form.date = item.contributionDate
|
this.form.date = item.contributionDate
|
||||||
@ -303,8 +308,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
// verifyLogin is important at this point so that creation is updated on reload if they are deleted in a session in the admin area.
|
|
||||||
this.verifyLogin()
|
|
||||||
this.updateListContributions({
|
this.updateListContributions({
|
||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
@ -315,6 +318,7 @@ export default {
|
|||||||
})
|
})
|
||||||
this.updateTransactions(0)
|
this.updateTransactions(0)
|
||||||
this.tabIndex = 1
|
this.tabIndex = 1
|
||||||
|
this.$router.push({ path: '/community#my' })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const navigatorClipboardMock = jest.fn()
|
|||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
describe.skip('Send', () => {
|
describe('Send', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
|
|
||||||
const propsData = {
|
const propsData = {
|
||||||
|
|||||||
@ -47,9 +47,6 @@ export const mutations = {
|
|||||||
hasElopage: (state, hasElopage) => {
|
hasElopage: (state, hasElopage) => {
|
||||||
state.hasElopage = hasElopage
|
state.hasElopage = hasElopage
|
||||||
},
|
},
|
||||||
creation: (state, creation) => {
|
|
||||||
state.creation = creation
|
|
||||||
},
|
|
||||||
hideAmountGDD: (state, hideAmountGDD) => {
|
hideAmountGDD: (state, hideAmountGDD) => {
|
||||||
state.hideAmountGDD = !!hideAmountGDD
|
state.hideAmountGDD = !!hideAmountGDD
|
||||||
},
|
},
|
||||||
@ -69,7 +66,6 @@ export const actions = {
|
|||||||
commit('hasElopage', data.hasElopage)
|
commit('hasElopage', data.hasElopage)
|
||||||
commit('publisherId', data.publisherId)
|
commit('publisherId', data.publisherId)
|
||||||
commit('isAdmin', data.isAdmin)
|
commit('isAdmin', data.isAdmin)
|
||||||
commit('creation', data.creation)
|
|
||||||
commit('hideAmountGDD', data.hideAmountGDD)
|
commit('hideAmountGDD', data.hideAmountGDD)
|
||||||
commit('hideAmountGDT', data.hideAmountGDT)
|
commit('hideAmountGDT', data.hideAmountGDT)
|
||||||
},
|
},
|
||||||
@ -83,7 +79,6 @@ export const actions = {
|
|||||||
commit('hasElopage', false)
|
commit('hasElopage', false)
|
||||||
commit('publisherId', null)
|
commit('publisherId', null)
|
||||||
commit('isAdmin', false)
|
commit('isAdmin', false)
|
||||||
commit('creation', null)
|
|
||||||
commit('hideAmountGDD', false)
|
commit('hideAmountGDD', false)
|
||||||
commit('hideAmountGDT', true)
|
commit('hideAmountGDT', true)
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
@ -111,7 +106,6 @@ try {
|
|||||||
newsletterState: null,
|
newsletterState: null,
|
||||||
hasElopage: false,
|
hasElopage: false,
|
||||||
publisherId: null,
|
publisherId: null,
|
||||||
creation: null,
|
|
||||||
hideAmountGDD: null,
|
hideAmountGDD: null,
|
||||||
hideAmountGDT: null,
|
hideAmountGDT: null,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -30,7 +30,6 @@ const {
|
|||||||
publisherId,
|
publisherId,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
hasElopage,
|
hasElopage,
|
||||||
creation,
|
|
||||||
hideAmountGDD,
|
hideAmountGDD,
|
||||||
hideAmountGDT,
|
hideAmountGDT,
|
||||||
} = mutations
|
} = mutations
|
||||||
@ -143,14 +142,6 @@ describe('Vuex store', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creation', () => {
|
|
||||||
it('sets the state of creation', () => {
|
|
||||||
const state = { creation: null }
|
|
||||||
creation(state, true)
|
|
||||||
expect(state.creation).toEqual(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('hideAmountGDD', () => {
|
describe('hideAmountGDD', () => {
|
||||||
it('sets the state of hideAmountGDD', () => {
|
it('sets the state of hideAmountGDD', () => {
|
||||||
const state = { hideAmountGDD: false }
|
const state = { hideAmountGDD: false }
|
||||||
@ -183,14 +174,13 @@ describe('Vuex store', () => {
|
|||||||
hasElopage: false,
|
hasElopage: false,
|
||||||
publisherId: 1234,
|
publisherId: 1234,
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
creation: ['1000', '1000', '1000'],
|
|
||||||
hideAmountGDD: false,
|
hideAmountGDD: false,
|
||||||
hideAmountGDT: true,
|
hideAmountGDT: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
it('calls eleven commits', () => {
|
it('calls eleven commits', () => {
|
||||||
login({ commit, state }, commitedData)
|
login({ commit, state }, commitedData)
|
||||||
expect(commit).toHaveBeenCalledTimes(11)
|
expect(commit).toHaveBeenCalledTimes(10)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits email', () => {
|
it('commits email', () => {
|
||||||
@ -233,19 +223,14 @@ describe('Vuex store', () => {
|
|||||||
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', true)
|
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits creation', () => {
|
|
||||||
login({ commit, state }, commitedData)
|
|
||||||
expect(commit).toHaveBeenNthCalledWith(9, 'creation', ['1000', '1000', '1000'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits hideAmountGDD', () => {
|
it('commits hideAmountGDD', () => {
|
||||||
login({ commit, state }, commitedData)
|
login({ commit, state }, commitedData)
|
||||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDD', false)
|
expect(commit).toHaveBeenNthCalledWith(9, 'hideAmountGDD', false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits hideAmountGDT', () => {
|
it('commits hideAmountGDT', () => {
|
||||||
login({ commit, state }, commitedData)
|
login({ commit, state }, commitedData)
|
||||||
expect(commit).toHaveBeenNthCalledWith(11, 'hideAmountGDT', true)
|
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDT', true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -255,7 +240,7 @@ describe('Vuex store', () => {
|
|||||||
|
|
||||||
it('calls eleven commits', () => {
|
it('calls eleven commits', () => {
|
||||||
logout({ commit, state })
|
logout({ commit, state })
|
||||||
expect(commit).toHaveBeenCalledTimes(11)
|
expect(commit).toHaveBeenCalledTimes(10)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits token', () => {
|
it('commits token', () => {
|
||||||
@ -298,19 +283,14 @@ describe('Vuex store', () => {
|
|||||||
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', false)
|
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits creation', () => {
|
|
||||||
logout({ commit, state })
|
|
||||||
expect(commit).toHaveBeenNthCalledWith(9, 'creation', null)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits hideAmountGDD', () => {
|
it('commits hideAmountGDD', () => {
|
||||||
logout({ commit, state })
|
logout({ commit, state })
|
||||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDD', false)
|
expect(commit).toHaveBeenNthCalledWith(9, 'hideAmountGDD', false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('commits hideAmountGDT', () => {
|
it('commits hideAmountGDT', () => {
|
||||||
logout({ commit, state })
|
logout({ commit, state })
|
||||||
expect(commit).toHaveBeenNthCalledWith(11, 'hideAmountGDT', true)
|
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDT', true)
|
||||||
})
|
})
|
||||||
// how to get this working?
|
// how to get this working?
|
||||||
it.skip('calls localStorage.clear()', () => {
|
it.skip('calls localStorage.clear()', () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user