Merge branch 'master' into 503-Transaction-List-Pagination-Pages-clickable

This commit is contained in:
Alexander Friedland 2022-03-29 11:48:47 +02:00 committed by GitHub
commit 6c953af2f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 596 additions and 29 deletions

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils'
import CreationTransactionListFormular from './CreationTransactionListFormular.vue'
import CreationTransactionList from './CreationTransactionList.vue'
import { toastErrorSpy } from '../../test/testSetup'
const localVue = global.localVue
@ -46,11 +46,11 @@ const propsData = {
fields: ['date', 'balance', 'name', 'memo', 'decay'],
}
describe('CreationTransactionListFormular', () => {
describe('CreationTransactionList', () => {
let wrapper
const Wrapper = () => {
return mount(CreationTransactionListFormular, { localVue, mocks, propsData })
return mount(CreationTransactionList, { localVue, mocks, propsData })
}
describe('mount', () => {

View File

@ -1,6 +1,6 @@
<template>
<div class="component-creation-transaction-list">
{{ $t('transactionlist.title') }}
<div class="h3">{{ $t('transactionlist.title') }}</div>
<b-table striped hover :fields="fields" :items="items"></b-table>
</div>
</template>

View File

@ -0,0 +1,129 @@
import { mount } from '@vue/test-utils'
import OpenCreationsTable from './OpenCreationsTable.vue'
const localVue = global.localVue
const apolloMutateMock = jest.fn().mockResolvedValue({})
const apolloQueryMock = jest.fn().mockResolvedValue({})
const propsData = {
items: [
{
id: 4,
firstName: 'Bob',
lastName: 'der Baumeister',
email: 'bob@baumeister.de',
amount: 300,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: 1,
creation: [700, 1000, 1000],
__typename: 'PendingCreation',
},
{
id: 5,
firstName: 'Räuber',
lastName: 'Hotzenplotz',
email: 'raeuber@hotzenplotz.de',
amount: 210,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: 1,
creation: [790, 1000, 1000],
__typename: 'PendingCreation',
},
{
id: 6,
firstName: 'Stephen',
lastName: 'Hawking',
email: 'stephen@hawking.uk',
amount: 330,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: 1,
creation: [670, 1000, 1000],
__typename: 'PendingCreation',
},
],
fields: [
{ key: 'bookmark', label: 'delete' },
{ key: 'email', label: 'e_mail' },
{ key: 'firstName', label: 'firstname' },
{ key: 'lastName', label: 'lastname' },
{
key: 'amount',
label: 'creation',
formatter: (value) => {
return value + ' GDD'
},
},
{ key: 'memo', label: 'text' },
{
key: 'date',
label: 'date',
formatter: (value) => {
return value
},
},
{ key: 'moderator', label: 'moderator' },
{ key: 'edit_creation', label: 'edit' },
{ key: 'confirm', label: 'save' },
],
}
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
$apollo: {
mutate: apolloMutateMock,
query: apolloQueryMock,
},
$store: {
state: {
moderator: {
id: 0,
name: 'test moderator',
},
},
},
}
describe('OpenCreationsTable', () => {
let wrapper
const Wrapper = () => {
return mount(OpenCreationsTable, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
wrapper = Wrapper()
})
it('has a DIV element with the class .open-creations-table', () => {
expect(wrapper.find('div.open-creations-table').exists()).toBeTruthy()
})
it('has a table with three rows', () => {
expect(wrapper.findAll('tbody > tr')).toHaveLength(3)
})
it('find first button.bi-pencil-square for open EditCreationFormular ', () => {
expect(wrapper.findAll('tr').at(1).find('.bi-pencil-square').exists()).toBeTruthy()
})
describe('show edit details', () => {
beforeEach(async () => {
await wrapper.findAll('tr').at(1).find('.bi-pencil-square').trigger('click')
})
it.skip('has a component element with name EditCreationFormular', () => {
expect(wrapper.findComponent({ name: 'EditCreationFormular' }).exists()).toBe(true)
})
it.skip('renders the component component-edit-creation-formular', () => {
expect(wrapper.find('div.component-edit-creation-formular').exists()).toBeTruthy()
})
})
})
})

View File

@ -1,5 +1,5 @@
<template>
<div class="component-open-creations-table">
<div class="open-creations-table">
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
<template #cell(bookmark)="row">
<b-button

View File

@ -69,10 +69,9 @@
: ''
"
/>
<creation-transaction-list-formular
v-if="!row.item.deletedAt"
:userId="row.item.userId"
/>
<creation-transaction-list v-if="!row.item.deletedAt" :userId="row.item.userId" />
<transaction-link-list :userId="row.item.userId" />
<deleted-user-formular :item="row.item" @updateDeletedAt="updateDeletedAt" />
</b-card>
</template>
@ -82,7 +81,8 @@
<script>
import CreationFormular from '../CreationFormular.vue'
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue'
import CreationTransactionListFormular from '../CreationTransactionListFormular.vue'
import CreationTransactionList from '../CreationTransactionList.vue'
import TransactionLinkList from '../TransactionLinkList.vue'
import DeletedUserFormular from '../DeletedUserFormular.vue'
export default {
@ -90,7 +90,8 @@ export default {
components: {
CreationFormular,
ConfirmRegisterMailFormular,
CreationTransactionListFormular,
CreationTransactionList,
TransactionLinkList,
DeletedUserFormular,
},
props: {

View File

@ -0,0 +1,140 @@
import { mount } from '@vue/test-utils'
import TransactionLinkList from './TransactionLinkList.vue'
import { listTransactionLinksAdmin } from '../graphql/listTransactionLinksAdmin.js'
import { toastErrorSpy } from '../../test/testSetup'
const localVue = global.localVue
const apolloQueryMock = jest.fn()
apolloQueryMock.mockResolvedValue({
data: {
listTransactionLinksAdmin: {
linkCount: 8,
linkList: [
{
amount: '19.99',
code: '62ef8236ace7217fbd066c5a',
createdAt: '2022-03-24T17:43:09.000Z',
deletedAt: null,
holdAvailableAmount: '20.51411720068412022949',
id: 36,
memo: 'Kein Trick, keine Zauberrei,\nbei Gradidio sei dabei!',
redeemedAt: null,
validUntil: '2022-04-07T17:43:09.000Z',
},
{
amount: '19.99',
code: '2b603f36521c617fbd066cef',
createdAt: '2022-03-24T17:43:09.000Z',
deletedAt: null,
holdAvailableAmount: '20.51411720068412022949',
id: 37,
memo: 'Kein Trick, keine Zauberrei,\nbei Gradidio sei dabei!',
redeemedAt: null,
validUntil: '2022-04-07T17:43:09.000Z',
},
{
amount: '19.99',
code: '0bb789b5bd5b717fbd066eb5',
createdAt: '2022-03-24T17:43:09.000Z',
deletedAt: '2022-03-24T17:43:09.000Z',
holdAvailableAmount: '20.51411720068412022949',
id: 40,
memo: 'Da habe ich mich wohl etwas übernommen.',
redeemedAt: '2022-04-07T14:43:09.000Z',
validUntil: '2022-04-07T17:43:09.000Z',
},
{
amount: '19.99',
code: '2d4a763e516b317fbd066a85',
createdAt: '2022-01-01T00:00:00.000Z',
deletedAt: null,
holdAvailableAmount: '20.51411720068412022949',
id: 33,
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
redeemedAt: null,
validUntil: '2022-01-15T00:00:00.000Z',
},
],
},
},
})
const propsData = {
userId: 42,
}
const mocks = {
$apollo: {
query: apolloQueryMock,
},
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
}
describe('TransactionLinkList', () => {
let wrapper
const Wrapper = () => {
return mount(TransactionLinkList, { localVue, mocks, propsData })
}
describe('mount', () => {
beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper()
})
it('calls the API', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
query: listTransactionLinksAdmin,
variables: {
currentPage: 1,
pageSize: 5,
userId: 42,
},
}),
)
})
it('has 4 items in the table', () => {
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
})
it('has pagination buttons', () => {
expect(wrapper.findComponent({ name: 'BPagination' }).exists()).toBe(true)
})
describe('next page', () => {
beforeAll(async () => {
jest.clearAllMocks()
await wrapper.findComponent({ name: 'BPagination' }).vm.$emit('input', 2)
})
it('calls the API again', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
query: listTransactionLinksAdmin,
variables: {
currentPage: 1,
pageSize: 5,
userId: 42,
},
}),
)
})
})
describe('server response with error', () => {
beforeEach(() => {
apolloQueryMock.mockRejectedValue({ message: 'Oh no!' })
wrapper = Wrapper()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
})
})

View File

@ -0,0 +1,105 @@
<template>
<div class="transaction-link-list">
<div v-if="items.length > 0">
<div class="h3">{{ $t('transactionlink.form_header') }}</div>
<b-table striped hover :fields="fields" :items="items"></b-table>
</div>
<b-pagination
pills
size="lg"
v-model="currentPage"
:per-page="perPage"
:total-rows="rows"
align="center"
></b-pagination>
</div>
</template>
<script>
import { listTransactionLinksAdmin } from '../graphql/listTransactionLinksAdmin.js'
export default {
name: 'TransactionLinkList',
props: {
userId: { type: Number, required: true },
},
data() {
return {
items: [],
rows: 0,
currentPage: 1,
perPage: 5,
}
},
methods: {
getListTransactionLinks() {
this.$apollo
.query({
query: listTransactionLinksAdmin,
variables: {
currentPage: this.currentPage,
pageSize: this.perPage,
userId: this.userId,
},
})
.then((result) => {
this.rows = result.data.listTransactionLinksAdmin.linkCount
this.items = result.data.listTransactionLinksAdmin.linkList
})
.catch((error) => {
this.toastError(error.message)
})
},
},
computed: {
fields() {
return [
{
key: 'createdAt',
label: this.$t('transactionlink.created'),
formatter: (value, key, item) => {
return this.$d(new Date(value))
},
},
{
key: 'amount',
label: this.$t('transactionlist.amount'),
formatter: (value, key, item) => {
return `${value} GDD`
},
},
{ key: 'memo', label: this.$t('transactionlist.memo') },
{
key: 'validUntil',
label: this.$t('transactionlink.valid_until'),
formatter: (value, key, item) => {
return this.$d(new Date(value))
},
},
{
key: 'status',
label: 'status',
formatter: (value, key, item) => {
// deleted
if (item.deletedAt) return this.$t('deleted') + ': ' + this.$d(new Date(item.deletedAt))
// redeemed
if (item.redeemedAt)
return this.$t('redeemed') + ': ' + this.$d(new Date(item.redeemedAt))
// expired
if (new Date() > new Date(item.validUntil))
return this.$t('expired') + ': ' + this.$d(new Date(item.validUntil))
// open
return this.$t('open')
},
},
]
},
},
created() {
this.getListTransactionLinks()
},
watch: {
currentPage() {
this.getListTransactionLinks()
},
},
}
</script>

View File

@ -0,0 +1,20 @@
import gql from 'graphql-tag'
export const listTransactionLinksAdmin = gql`
query ($currentPage: Int = 1, $pageSize: Int = 5, $userId: Int!) {
listTransactionLinksAdmin(currentPage: $currentPage, pageSize: $pageSize, userId: $userId) {
linkCount
linkList {
id
amount
holdAvailableAmount
memo
code
createdAt
validUntil
redeemedAt
deletedAt
}
}
}
`

View File

@ -27,6 +27,7 @@
"edit": "Bearbeiten",
"enabled": "aktiviert",
"error": "Fehler",
"expired": "abgelaufen",
"e_mail": "E-Mail",
"firstname": "Vorname",
"footer": {
@ -56,6 +57,7 @@
"user_search": "Nutzersuche"
},
"not_open_creations": "Keine offenen Schöpfungen",
"open": "offen",
"open_creations": "Offene Schöpfungen",
"overlay": {
"confirm": {
@ -66,6 +68,7 @@
"yes": "Ja, Schöpfung bestätigen und speichern!"
}
},
"redeemed": "eingelöst",
"remove": "Entfernen",
"removeNotSelf": "Als Admin / Moderator kannst du dich nicht selber löschen.",
"remove_all": "alle Nutzer entfernen",
@ -73,6 +76,11 @@
"status": "Status",
"success": "Erfolg",
"text": "Text",
"transactionlink": {
"created": "Erstellt",
"form_header": "Transaktion-Links",
"valid_until": "Gültig bis"
},
"transactionlist": {
"amount": "Betrag",
"balanceDate": "Schöpfungsdatum",

View File

@ -27,6 +27,7 @@
"edit": "Edit",
"enabled": "enabled",
"error": "Error",
"expired": "expired",
"e_mail": "E-mail",
"firstname": "Firstname",
"footer": {
@ -56,6 +57,7 @@
"user_search": "User search"
},
"not_open_creations": "No open creations",
"open": "open",
"open_creations": "Open creations",
"overlay": {
"confirm": {
@ -66,6 +68,7 @@
"yes": "Yes, confirm and save creation!"
}
},
"redeemed": "redeemed",
"remove": "Remove",
"removeNotSelf": "As admin / moderator you cannot delete yourself.",
"remove_all": "Remove all users",
@ -73,6 +76,11 @@
"status": "Status",
"success": "Success",
"text": "Text",
"transactionlink": {
"created": "Created",
"form_header": "Transaction links",
"valid_until": "Valid until"
},
"transactionlist": {
"amount": "Amount",
"balanceDate": "Creation date",

View File

@ -35,4 +35,5 @@ export enum RIGHTS {
DELETE_USER = 'DELETE_USER',
UNDELETE_USER = 'UNDELETE_USER',
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
}

View File

@ -0,0 +1,13 @@
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export default class TransactionLinkFilters {
@Field(() => Boolean, { nullable: true, defaultValue: true })
withDeleted?: boolean
@Field(() => Boolean, { nullable: true, defaultValue: true })
withExpired?: boolean
@Field(() => Boolean, { nullable: true, defaultValue: true })
withRedeemed?: boolean
}

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql'
import { ObjectType, Field, Int } from 'type-graphql'
import Decimal from 'decimal.js-light'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { User } from './User'
@ -52,3 +52,12 @@ export class TransactionLink {
@Field(() => User, { nullable: true })
redeemedBy: User | null
}
@ObjectType()
export class TransactionLinkResult {
@Field(() => Int)
linkCount: number
@Field(() => [TransactionLink])
linkList: TransactionLink[]
}

View File

@ -9,6 +9,8 @@ import {
ObjectLiteral,
getConnection,
In,
MoreThan,
FindOperator,
} from '@dbTools/typeorm'
import { UserAdmin, SearchUsersResult } from '@model/UserAdmin'
import { PendingCreation } from '@model/PendingCreation'
@ -21,6 +23,8 @@ import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs'
import SearchUsersArgs from '@arg/SearchUsersArgs'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { Transaction } from '@model/Transaction'
import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { TransactionRepository } from '@repository/Transaction'
import { calculateDecay } from '@/util/decay'
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
@ -32,6 +36,7 @@ import { TransactionTypeId } from '@enum/TransactionTypeId'
import Decimal from 'decimal.js-light'
import { Decay } from '@model/Decay'
import Paginated from '@arg/Paginated'
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
import { Order } from '@enum/Order'
import { communityUser } from '@/util/communityUser'
@ -369,6 +374,41 @@ export class AdminResolver {
const user = await dbUser.findOneOrFail({ id: userId })
return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
}
@Authorized([RIGHTS.LIST_TRANSACTION_LINKS_ADMIN])
@Query(() => TransactionLinkResult)
async listTransactionLinksAdmin(
@Args()
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
@Args()
filters: TransactionLinkFilters,
@Arg('userId', () => Int) userId: number,
): Promise<TransactionLinkResult> {
const user = await dbUser.findOneOrFail({ id: userId })
const where: {
userId: number
redeemedBy?: number | null
validUntil?: FindOperator<Date> | null
} = {
userId,
}
if (!filters.withRedeemed) where.redeemedBy = null
if (!filters.withExpired) where.validUntil = MoreThan(new Date())
const [transactionLinks, count] = await dbTransactionLink.findAndCount({
where,
withDeleted: filters.withDeleted,
order: {
createdAt: order,
},
skip: (currentPage - 1) * pageSize,
take: pageSize,
})
return {
linkCount: count,
linkList: transactionLinks.map((tl) => new TransactionLink(tl, new User(user))),
}
}
}
interface CreationMap {

View File

@ -34,6 +34,9 @@ import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualT
import Decimal from 'decimal.js-light'
import { calculateDecay } from '@/util/decay'
const MEMO_MAX_CHARS = 255
const MEMO_MIN_CHARS = 5
export const executeTransaction = async (
amount: Decimal,
memo: string,
@ -45,6 +48,14 @@ export const executeTransaction = async (
throw new Error('Sender and Recipient are the same.')
}
if (memo.length > MEMO_MAX_CHARS) {
throw new Error(`memo text is too long (${MEMO_MAX_CHARS} characters maximum)`)
}
if (memo.length < MEMO_MIN_CHARS) {
throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`)
}
// validate amount
const receivedCallDate = new Date()
const sendBalance = await calculateBalance(sender.id, amount.mul(-1), receivedCallDate)

View File

@ -62,11 +62,8 @@ describe('TransactionForm', () => {
})
})
describe('is selected: "send"', () => {
describe('send GDD', () => {
beforeEach(async () => {
// await wrapper.setData({
// selected: 'send',
// })
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
})
@ -78,15 +75,18 @@ describe('TransactionForm', () => {
beforeEach(() => {
wrapper.setProps({ balance: 100.0 })
})
describe('transaction form show because balance 100,0 GDD', () => {
it('has no warning message ', () => {
expect(wrapper.find('.errors').exists()).toBe(false)
})
it('has a reset button', () => {
expect(wrapper.find('.test-buttons').findAll('button').at(0).attributes('type')).toBe(
'reset',
)
})
it('has a submit button', () => {
expect(wrapper.find('.test-buttons').findAll('button').at(1).attributes('type')).toBe(
'submit',
@ -121,6 +121,12 @@ describe('TransactionForm', () => {
expect(wrapper.find('span.errors').text()).toBe('validations.messages.email')
})
it('flushes an error message when email is the email of logged in user', async () => {
await wrapper.find('#input-group-1').find('input').setValue('user@example.org')
await flushPromises()
expect(wrapper.find('span.errors').text()).toBe('form.validation.is-not')
})
it('trims the email after blur', async () => {
await wrapper.find('#input-group-1').find('input').setValue(' valid@email.com ')
await wrapper.find('#input-group-1').find('input').trigger('blur')
@ -195,6 +201,41 @@ describe('TransactionForm', () => {
expect(wrapper.find('span.errors').text()).toBe('validations.messages.min')
})
it('flushes an error message when memo is more than 255 characters', async () => {
await wrapper.find('#input-group-3').find('textarea').setValue(`
Es ist ein König in Thule, der trinkt
Champagner, es geht ihm nichts drüber;
Und wenn er seinen Champagner trinkt,
Dann gehen die Augen ihm über.
Die Ritter sitzen um ihn her,
Die ganze Historische Schule;
Ihm aber wird die Zunge schwer,
Es lallt der König von Thule:
Als Alexander, der Griechenheld,
Mit seinem kleinen Haufen
Erobert hatte die ganze Welt,
Da gab er sich ans Saufen.
Ihn hatten so durstig gemacht der Krieg
Und die Schlachten, die er geschlagen;
Er soff sich zu Tode nach dem Sieg,
Er konnte nicht viel vertragen.
Ich aber bin ein stärkerer Mann
Und habe mich klüger besonnen:
Wie jener endete, fang ich an,
Ich hab mit dem Trinken begonnen.
Im Rausche wird der Heldenzug
Mir später weit besser gelingen;
Dann werde ich, taumelnd von Krug zu Krug,
Die ganze Welt bezwingen.`)
await flushPromises()
expect(wrapper.find('span.errors').text()).toBe('validations.messages.max')
})
it('flushes no error message when memo is valid', async () => {
await wrapper.find('#input-group-3').find('textarea').setValue('Long enough')
await flushPromises()

View File

@ -99,7 +99,7 @@
:rules="{
required: true,
min: 5,
max: 150,
max: 255,
}"
:name="$t('form.message')"
v-slot="{ errors }"

View File

@ -10,9 +10,20 @@
</b-col>
<b-col cols="7">
<div class="gdd-transaction-list-item-name">
<b-link v-if="linkedUser && linkedUser.email" @click.stop="tunnelEmail">
{{ itemText }}
</b-link>
<div v-if="linkedUser && linkedUser.email">
<b-link @click.stop="tunnelEmail">
{{ itemText }}
</b-link>
<span v-if="transactionLinkId">
{{ $t('via_link') }}
<b-icon
icon="link45deg"
variant="muted"
class="m-mb-1"
:title="$t('gdd_per_link.redeemed-title')"
/>
</span>
</div>
<span v-else>{{ itemText }}</span>
</div>
</b-col>
@ -35,6 +46,11 @@ export default {
type: String,
required: false,
},
transactionLinkId: {
type: Number,
required: false,
default: null,
},
},
methods: {
tunnelEmail() {

View File

@ -2,7 +2,9 @@
<div class="date-row">
<b-row>
<b-col cols="5">
<div class="text-right">{{ diffNow ? $t('gdd_per_link.expired') : $t('form.date') }}</div>
<div class="text-right">
{{ diffNow ? $t('gdd_per_link.valid_until') : $t('form.date') }}
</div>
</b-col>
<b-col cols="7">
<div class="gdd-transaction-list-item-date">

View File

@ -13,7 +13,12 @@
<b-col cols="11">
<!-- Amount / Name || Text -->
<amount-and-name-row :amount="amount" :linkedUser="linkedUser" v-on="$listeners" />
<amount-and-name-row
v-on="$listeners"
:amount="amount"
:linkedUser="linkedUser"
:transactionLinkId="transactionLinkId"
/>
<!-- Nachricht Memo -->
<memo-row :memo="memo" />
@ -86,6 +91,10 @@ export default {
type: Date,
required: true,
},
transactionLinkId: {
type: Number,
required: false,
},
},
data() {
return {

View File

@ -13,7 +13,12 @@
<b-col cols="11">
<!-- Amount / Name -->
<amount-and-name-row :amount="amount" :linkedUser="linkedUser" v-on="$listeners" />
<amount-and-name-row
v-on="$listeners"
:amount="amount"
:linkedUser="linkedUser"
:transactionLinkId="transactionLinkId"
/>
<!-- Memo -->
<memo-row :memo="memo" />
@ -87,6 +92,10 @@ export default {
type: Date,
required: true,
},
transactionLinkId: {
type: Number,
required: false,
},
},
data() {
return {

View File

@ -70,6 +70,7 @@ export const transactionsQuery = gql`
linkedUser {
email
}
transactionLinkId
}
}
}

View File

@ -100,7 +100,6 @@
"decay-14-day": "Vergänglichkeit für 14 Tage",
"delete-the-link": "Den Link löschen?",
"deleted": "Der Link wurde gelöscht!",
"expired": "Abgelaufen",
"has-account": "Du besitzt bereits ein Gradido Konto",
"header": "Gradidos versenden per Link",
"link-copied": "Link wurde in die Zwischenablage kopiert",
@ -116,8 +115,10 @@
"redeem-text": "Willst du den Betrag jetzt einlösen?",
"redeemed": "Erfolgreich eingelöst! Deinem Konto wurden {n} GDD gutgeschrieben.",
"redeemed-at": "Der Link wurde bereits am {date} eingelöst.",
"redeemed-title": "eingelöst",
"to-login": "Log dich ein",
"to-register": "Registriere ein neues Konto"
"to-register": "Registriere ein neues Konto",
"valid_until": "Gültig bis"
},
"gdt": {
"calculation": "Berechnung der GradidoTransform",
@ -249,5 +250,6 @@
},
"transaction-link": {
"send_you": "sendet dir"
}
},
"via_link": "über einen Link"
}

View File

@ -100,7 +100,6 @@
"decay-14-day": "Decay for 14 days",
"delete-the-link": "Delete the link?",
"deleted": "The link was deleted!",
"expired": "Expired",
"has-account": "You already have a Gradido account",
"header": "Send Gradidos via link",
"link-copied": "Link copied to clipboard",
@ -116,8 +115,10 @@
"redeem-text": "Do you want to redeem the amount now?",
"redeemed": "Successfully redeemed! Your account has been credited with {n} GDD.",
"redeemed-at": "The link was already redeemed on {date}.",
"redeemed-title": "redeemed",
"to-login": "Log in",
"to-register": "Register a new account"
"to-register": "Register a new account",
"valid_until": "Valid until"
},
"gdt": {
"calculation": "Calculation of GradidoTransform",
@ -249,5 +250,6 @@
},
"transaction-link": {
"send_you": "wants to send you"
}
},
"via_link": "via Link"
}