diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e95a2b038..2be814cc0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -261,7 +261,7 @@ jobs: report_name: Coverage Frontend type: lcov result_path: ./coverage/lcov.info - min_coverage: 47 + min_coverage: 59 token: ${{ github.token }} ############################################################################## diff --git a/backend/src/graphql/inputs/GdtInputs.ts b/backend/src/graphql/inputs/GdtInputs.ts index 33f3b797d..1404fbf14 100644 --- a/backend/src/graphql/inputs/GdtInputs.ts +++ b/backend/src/graphql/inputs/GdtInputs.ts @@ -1,16 +1,31 @@ -import { ArgsType, Field } from 'type-graphql' +import { ArgsType, Field, Int } from 'type-graphql' @ArgsType() export class GdtTransactionInput { @Field(() => String) email: string - @Field(() => Number) - firstPage?: number + @Field(() => Int, { nullable: true }) + currentPage?: number - @Field(() => Number) - items?: number + @Field(() => Int, { nullable: true }) + pageSize?: number - @Field(() => String) + @Field(() => String, { nullable: true }) + order?: string +} + +@ArgsType() +export class GdtTransactionSessionIdInput { + @Field(() => Number) + sessionId: number + + @Field(() => Int, { nullable: true }) + currentPage?: number + + @Field(() => Int, { nullable: true }) + pageSize?: number + + @Field(() => String, { nullable: true }) order?: string } diff --git a/backend/src/graphql/models/Decay.ts b/backend/src/graphql/models/Decay.ts index c6ce85548..e0234f588 100644 --- a/backend/src/graphql/models/Decay.ts +++ b/backend/src/graphql/models/Decay.ts @@ -9,6 +9,7 @@ export class Decay { this.decayStart = json.decay_start this.decayEnd = json.decay_end this.decayDuration = json.decay_duration + this.decayStartBlock = json.decay_start_block } @Field(() => Number) @@ -22,4 +23,7 @@ export class Decay { @Field(() => String, { nullable: true }) decayDuration?: string + + @Field(() => Int, { nullable: true }) + decayStartBlock?: number } diff --git a/backend/src/graphql/models/GdtEntry.ts b/backend/src/graphql/models/GdtEntry.ts index bc87abbc0..09ee35a70 100644 --- a/backend/src/graphql/models/GdtEntry.ts +++ b/backend/src/graphql/models/GdtEntry.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { ObjectType, Field } from 'type-graphql' export enum GdtEntryType { diff --git a/backend/src/graphql/models/GdtEntryList.ts b/backend/src/graphql/models/GdtEntryList.ts index 233d8472d..301ac179b 100644 --- a/backend/src/graphql/models/GdtEntryList.ts +++ b/backend/src/graphql/models/GdtEntryList.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { GdtEntry } from './GdtEntry' import { ObjectType, Field } from 'type-graphql' @@ -31,8 +33,8 @@ export class GdtEntryList { @Field(() => Number) count: number - @Field(() => [GdtEntry]) - gdtEntries: GdtEntry[] + @Field(() => [GdtEntry], { nullable: true }) + gdtEntries?: GdtEntry[] @Field(() => Number) gdtSum: number diff --git a/backend/src/graphql/models/Transaction.ts b/backend/src/graphql/models/Transaction.ts index fb17866c4..87535c4a2 100644 --- a/backend/src/graphql/models/Transaction.ts +++ b/backend/src/graphql/models/Transaction.ts @@ -11,7 +11,6 @@ import { Decay } from './Decay' @ObjectType() export class Transaction { constructor(json: any) { - // console.log('Transaction constructor', json) this.type = json.type this.balance = Number(json.balance) this.decayStart = json.decay_start @@ -43,8 +42,8 @@ export class Transaction { @Field(() => String) memo: string - @Field(() => Number) - transactionId: number + @Field(() => Number, { nullable: true }) + transactionId?: number @Field({ nullable: true }) name?: string diff --git a/backend/src/graphql/resolvers/GdtResolver.ts b/backend/src/graphql/resolvers/GdtResolver.ts index e5d8661e9..0ff187bd1 100644 --- a/backend/src/graphql/resolvers/GdtResolver.ts +++ b/backend/src/graphql/resolvers/GdtResolver.ts @@ -2,7 +2,7 @@ import { Resolver, Query, /* Mutation, */ Args } from 'type-graphql' import CONFIG from '../../config' import { GdtEntryList } from '../models/GdtEntryList' -import { GdtTransactionInput } from '../inputs/GdtInputs' +import { GdtTransactionSessionIdInput } from '../inputs/GdtInputs' import { apiGet } from '../../apis/loginAPI' @Resolver() @@ -10,13 +10,12 @@ export class GdtResolver { @Query(() => GdtEntryList) // eslint-disable-next-line @typescript-eslint/no-explicit-any async listGDTEntries( - @Args() { email, firstPage = 1, items = 5, order = 'DESC' }: GdtTransactionInput, + @Args() + { currentPage = 1, pageSize = 5, order = 'DESC', sessionId }: GdtTransactionSessionIdInput, ): Promise { - email = email.trim().toLowerCase() const result = await apiGet( - `${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${email}/${firstPage}/${items}/${order}`, + `${CONFIG.COMMUNITY_API_URL}listGDTTransactions/${currentPage}/${pageSize}/${order}/${sessionId}`, ) - if (!result.success) { throw new Error(result.data) } diff --git a/backend/src/graphql/resolvers/TransactionResolver.ts b/backend/src/graphql/resolvers/TransactionResolver.ts index 4b9fefa52..607b09691 100644 --- a/backend/src/graphql/resolvers/TransactionResolver.ts +++ b/backend/src/graphql/resolvers/TransactionResolver.ts @@ -1,4 +1,4 @@ -import { Resolver, Query, /* Mutation, */ Args } from 'type-graphql' +import { Resolver, Query, Args } from 'type-graphql' import CONFIG from '../../config' import { TransactionList } from '../models/Transaction' import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput' diff --git a/frontend/.env.dist b/frontend/.env.dist index ffe4869fa..a22b31307 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,3 +1,4 @@ LOGIN_API_URL=http://localhost/login_api/ COMMUNITY_API_URL=http://localhost/api/ -ALLOW_REGISTER=true \ No newline at end of file +ALLOW_REGISTER=true +GRAPHQL_URI=http://localhost:4000/graphql diff --git a/frontend/package.json b/frontend/package.json index c80f164f8..4bc621916 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "@babel/preset-env": "^7.13.12", "@vue/cli-plugin-unit-jest": "^4.5.12", "@vue/test-utils": "^1.1.3", + "apollo-boost": "^0.4.9", "axios": "^0.21.1", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^26.6.3", @@ -45,6 +46,7 @@ "flush-promises": "^1.0.2", "fuse.js": "^3.2.0", "google-maps": "^3.2.1", + "graphql": "^15.5.1", "identity-obj-proxy": "^3.0.0", "jest": "^26.6.3", "jest-canvas-mock": "^2.3.1", @@ -59,6 +61,7 @@ "sweetalert2": "^9.5.4", "vee-validate": "^3.4.5", "vue": "^2.6.11", + "vue-apollo": "^3.0.7", "vue-bootstrap-typeahead": "^0.2.6", "vue-chartjs": "^3.5.0", "vue-cli-plugin-i18n": "^1.0.1", diff --git a/frontend/src/apis/communityAPI.js b/frontend/src/apis/communityAPI.js deleted file mode 100644 index 4f151d3a2..000000000 --- a/frontend/src/apis/communityAPI.js +++ /dev/null @@ -1,71 +0,0 @@ -import axios from 'axios' -import CONFIG from '../config' - -const apiGet = async (url) => { - try { - const result = await axios.get(url) - if (result.status !== 200) { - throw new Error('HTTP Status Error ' + result.status) - } - if (result.data.state !== 'success') { - throw new Error(result.data.msg) - } - return { success: true, result } - } catch (error) { - return { success: false, result: error } - } -} - -const apiPost = async (url, payload) => { - try { - const result = await axios.post(url, payload) - if (result.status !== 200) { - throw new Error('HTTP Status Error ' + result.status) - } - if (result.data.state !== 'success') { - throw new Error(result.data.msg) - } - return { success: true, result } - } catch (error) { - return { success: false, result: error } - } -} - -const communityAPI = { - balance: async (sessionId) => { - return apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + sessionId) - }, - transactions: async (sessionId, firstPage = 1, items = 5, order = 'DESC') => { - return apiGet( - `${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${sessionId}`, - ) - }, - transactionsgdt: async (sessionId, firstPage = 1, items = 5, order = 'DESC') => { - return apiGet( - `${CONFIG.COMMUNITY_API_URL}listGDTTransactions/${firstPage}/${items}/${order}/${sessionId}`, - ) - }, - /* http://localhost/vue/public/json-example/admin_transactionGdt_list.json - http://localhost/state-balances/ajaxGdtOverview - create: async (sessionId, email, amount, memo, target_date = new Date() ) => { - const payload = { - sessionId, - email, - amount, - target_date, - memo, - auto_sign: true, - } - return apiPost(CONFIG.COMMUNITY_API__URL + 'createCoins/', payload) - }, */ - send: async (sessionId, data) => { - const payload = { - session_id: sessionId, - auto_sign: true, - ...data, - } - return apiPost(CONFIG.COMMUNITY_API_URL + 'sendCoins/', payload) - }, -} - -export default communityAPI diff --git a/frontend/src/apis/loginAPI.js b/frontend/src/apis/loginAPI.js deleted file mode 100644 index 9dd8ce98d..000000000 --- a/frontend/src/apis/loginAPI.js +++ /dev/null @@ -1,145 +0,0 @@ -import axios from 'axios' -import CONFIG from '../config' -// eslint-disable-next-line no-unused-vars -import regeneratorRuntime from 'regenerator-runtime' - -// control email-text sended with email verification code -const EMAIL_TYPE = { - DEFAULT: 2, // if user has registered directly - ADMIN: 5, // if user was registered by an admin -} - -const apiGet = async (url) => { - try { - const result = await axios.get(url) - if (result.status !== 200) { - throw new Error('HTTP Status Error ' + result.status) - } - if (!['success', 'warning'].includes(result.data.state)) { - throw new Error(result.data.msg) - } - return { success: true, result } - } catch (error) { - return { success: false, result: error } - } -} - -const apiPost = async (url, payload) => { - try { - const result = await axios.post(url, payload) - if (result.status !== 200) { - throw new Error('HTTP Status Error ' + result.status) - } - if (result.data.state === 'warning') { - return { success: true, result: result.error } - } - if (result.data.state !== 'success') { - throw new Error(result.data.msg) - } - return { success: true, result } - } catch (error) { - return { success: false, result: error } - } -} - -const loginAPI = { - login: async (email, password) => { - const payload = { - email, - password, - } - return apiPost(CONFIG.LOGIN_API_URL + 'unsecureLogin', payload) - }, - logout: async (sessionId) => { - const payload = { session_id: sessionId } - return apiPost(CONFIG.LOGIN_API_URL + 'logout', payload) - }, - create: async (email, firstName, lastName, password) => { - const payload = { - email, - first_name: firstName, - last_name: lastName, - password, - emailType: EMAIL_TYPE.DEFAULT, - login_after_register: true, - } - return apiPost(CONFIG.LOGIN_API_URL + 'createUser', payload) - }, - sendEmail: async (email, email_text = 7, email_verification_code_type = 'resetPassword') => { - const payload = { - email, - email_text, - email_verification_code_type, - } - return apiPost(CONFIG.LOGIN_API_URL + 'sendEmail', payload) - }, - loginViaEmailVerificationCode: async (optin) => { - return apiGet( - CONFIG.LOGIN_API_URL + 'loginViaEmailVerificationCode?emailVerificationCode=' + optin, - ) - }, - getUserInfos: async (sessionId, email) => { - const payload = { - session_id: sessionId, - email: email, - ask: ['user.first_name', 'user.last_name'], - } - return apiPost(CONFIG.LOGIN_API_URL + 'getUserInfos', payload) - }, - updateUserInfos: async (sessionId, email, data) => { - const payload = { - session_id: sessionId, - email, - update: { - 'User.first_name': data.firstName, - 'User.last_name': data.lastName, - // 'User.description': data.description, - }, - } - return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload) - }, - changePassword: async (sessionId, email, password) => { - const payload = { - session_id: sessionId, - email, - password, - } - return apiPost(CONFIG.LOGIN_API_URL + 'resetPassword', payload) - }, - changePasswordProfile: async (sessionId, email, password, passwordNew) => { - const payload = { - session_id: sessionId, - email, - update: { - 'User.password_old': password, - 'User.password': passwordNew, - }, - } - return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload) - }, - changeUsernameProfile: async (sessionId, email, username) => { - const payload = { - session_id: sessionId, - email, - update: { - 'User.username': username, - }, - } - return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload) - }, - updateLanguage: async (sessionId, email, language) => { - const payload = { - session_id: sessionId, - email, - update: { - 'User.language': language, - }, - } - return apiPost(CONFIG.LOGIN_API_URL + 'updateUserInfos', payload) - }, - checkUsername: async (username, groupId = 1) => { - return apiGet(CONFIG.LOGIN_API_URL + `checkUsername?username=${username}&group_id=${groupId}`) - }, -} - -export default loginAPI diff --git a/frontend/src/components/DecayInformation.vue b/frontend/src/components/DecayInformation.vue index cb5ad402d..743881ca5 100644 --- a/frontend/src/components/DecayInformation.vue +++ b/frontend/src/components/DecayInformation.vue @@ -7,7 +7,7 @@
-
+
{{ $t('decay.calculation_decay') }} @@ -16,19 +16,19 @@
-
{{ $t('decay.last_transaction') }}
+
{{ $t('decay.last_transaction') }}
-
+
{{ $t('decay.Starting_block_decay') }}
{{ $t('decay.decay_introduced') }} : - {{ $d($moment.unix(decay.decay_start), 'long') }} + {{ $d($moment.unix(decay.decayStart), 'long') }}
- - {{ $d($moment.unix(decay.decay_start), 'long') }} + + {{ $d($moment.unix(decay.decayStart), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
@@ -37,10 +37,10 @@
-
{{ $t('decay.past_time') }}
+
{{ $t('decay.past_time') }}
-
{{ $t('decay.since_introduction') }}
+
{{ $t('decay.since_introduction') }}
{{ duration.years }} {{ $t('decay.year') }}, @@ -68,24 +68,24 @@ export default { props: { decay: { balance: '', - decay_duration: '', - decay_start: 0, - decay_end: 0, - decay_start_block: 0, + decayDuration: '', + decayStart: 0, + decayEnd: 0, + decayStartBlock: 0, }, decaytyp: { type: String, default: '' }, }, computed: { decayStartBlockTextShort() { - return this.decay.decay_start_block - ? this.$t('decay.decayStart') + this.$d(this.$moment.unix(this.decay.decay_start_block)) + return this.decay.decayStartBlock + ? this.$t('decay.decayStart') + this.$d(this.$moment.unix(this.decay.decayStartBlock)) : '' }, duration() { return this.$moment.duration( this.$moment - .unix(new Date(this.decay.decay_end)) - .diff(this.$moment.unix(new Date(this.decay.decay_start))), + .unix(new Date(this.decay.decayEnd)) + .diff(this.$moment.unix(new Date(this.decay.decayStart))), )._data }, }, diff --git a/frontend/src/components/LanguageSwitch.spec.js b/frontend/src/components/LanguageSwitch.spec.js index 14dc978ac..72771bd37 100644 --- a/frontend/src/components/LanguageSwitch.spec.js +++ b/frontend/src/components/LanguageSwitch.spec.js @@ -3,6 +3,14 @@ import LanguageSwitch from './LanguageSwitch' const localVue = global.localVue +const updateUserInfosQueryMock = jest.fn().mockResolvedValue({ + data: { + updateUserInfos: { + validValues: 1, + }, + }, +}) + describe('LanguageSwitch', () => { let wrapper @@ -20,6 +28,9 @@ describe('LanguageSwitch', () => { $i18n: { locale: 'en', }, + $apollo: { + query: updateUserInfosQueryMock, + }, } const Wrapper = () => { @@ -37,17 +48,22 @@ describe('LanguageSwitch', () => { describe('with locales en and de', () => { describe('empty store', () => { - it('shows English as default navigator langauge', () => { - expect(wrapper.find('button.dropdown-toggle').text()).toBe('English - en') + describe('navigator language is "en-US"', () => { + const languageGetter = jest.spyOn(navigator, 'language', 'get') + + it('shows English as default navigator langauge', async () => { + languageGetter.mockReturnValue('en-US') + wrapper.vm.setCurrentLanguage() + await wrapper.vm.$nextTick() + expect(wrapper.find('button.dropdown-toggle').text()).toBe('English - en') + }) }) describe('navigator language is "de-DE"', () => { - const mockNavigator = jest.fn(() => { - return 'de' - }) + const languageGetter = jest.spyOn(navigator, 'language', 'get') it('shows Deutsch as language ', async () => { - wrapper.vm.getNavigatorLanguage = mockNavigator + languageGetter.mockReturnValue('de-DE') wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() expect(wrapper.find('button.dropdown-toggle').text()).toBe('Deutsch - de') @@ -55,12 +71,21 @@ describe('LanguageSwitch', () => { }) describe('navigator language is "es-ES" (not supported)', () => { - const mockNavigator = jest.fn(() => { - return 'es' - }) + const languageGetter = jest.spyOn(navigator, 'language', 'get') it('shows English as language ', async () => { - wrapper.vm.getNavigatorLanguage = mockNavigator + languageGetter.mockReturnValue('es-ES') + wrapper.vm.setCurrentLanguage() + await wrapper.vm.$nextTick() + expect(wrapper.find('button.dropdown-toggle').text()).toBe('English - en') + }) + }) + + describe('no navigator langauge', () => { + const languageGetter = jest.spyOn(navigator, 'language', 'get') + + it('shows English as language ', async () => { + languageGetter.mockReturnValue(null) wrapper.vm.setCurrentLanguage() await wrapper.vm.$nextTick() expect(wrapper.find('button.dropdown-toggle').text()).toBe('English - en') @@ -91,5 +116,33 @@ describe('LanguageSwitch', () => { }) }) }) + + describe('calls the API', () => { + it("with locale 'en'", () => { + wrapper.findAll('li').at(0).find('a').trigger('click') + expect(updateUserInfosQueryMock).toBeCalledWith( + expect.objectContaining({ + variables: { + sessionId: 1234, + email: 'he@ho.he', + locale: 'en', + }, + }), + ) + }) + + it("with locale 'de'", () => { + wrapper.findAll('li').at(1).find('a').trigger('click') + expect(updateUserInfosQueryMock).toBeCalledWith( + expect.objectContaining({ + variables: { + sessionId: 1234, + email: 'he@ho.he', + locale: 'de', + }, + }), + ) + }) + }) }) }) diff --git a/frontend/src/components/LanguageSwitch.vue b/frontend/src/components/LanguageSwitch.vue index 2b160b3e3..e6f9c86c3 100644 --- a/frontend/src/components/LanguageSwitch.vue +++ b/frontend/src/components/LanguageSwitch.vue @@ -14,7 +14,7 @@ diff --git a/frontend/src/views/Pages/AccountOverview.spec.js b/frontend/src/views/Pages/AccountOverview.spec.js index 8d47cdb27..b890f1471 100644 --- a/frontend/src/views/Pages/AccountOverview.spec.js +++ b/frontend/src/views/Pages/AccountOverview.spec.js @@ -1,13 +1,8 @@ import { mount } from '@vue/test-utils' import AccountOverview from './AccountOverview' -import communityAPI from '../../apis/communityAPI.js' - -jest.mock('../../apis/communityAPI.js') const sendMock = jest.fn() -sendMock.mockReturnValue({ success: true }) - -communityAPI.send = sendMock +sendMock.mockResolvedValue('success') const localVue = global.localVue @@ -27,6 +22,9 @@ describe('AccountOverview', () => { }, }, $n: jest.fn((n) => String(n)), + $apollo: { + query: sendMock, + }, } const Wrapper = () => { @@ -92,11 +90,16 @@ describe('AccountOverview', () => { }) it('calls the API when send-transaction is emitted', async () => { - expect(sendMock).toBeCalledWith(1, { - email: 'user@example.org', - amount: 23.45, - memo: 'Make the best of it!', - }) + expect(sendMock).toBeCalledWith( + expect.objectContaining({ + variables: { + sessionId: 1, + email: 'user@example.org', + amount: 23.45, + memo: 'Make the best of it!', + }, + }), + ) }) it('emits update-balance', () => { @@ -112,7 +115,7 @@ describe('AccountOverview', () => { describe('transaction is confirmed and server response is error', () => { beforeEach(async () => { jest.clearAllMocks() - sendMock.mockReturnValue({ success: false, result: { message: 'receiver not found' } }) + sendMock.mockRejectedValue({ message: 'receiver not found' }) await wrapper .findComponent({ name: 'TransactionConfirmation' }) .vm.$emit('send-transaction') diff --git a/frontend/src/views/Pages/AccountOverview.vue b/frontend/src/views/Pages/AccountOverview.vue index b7dbede2b..b89955a6a 100644 --- a/frontend/src/views/Pages/AccountOverview.vue +++ b/frontend/src/views/Pages/AccountOverview.vue @@ -51,7 +51,7 @@ import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue' import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue' import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue' -import communityAPI from '../../apis/communityAPI.js' +import { sendCoins } from '../../graphql/queries.js' const EMPTY_TRANSACTION_DATA = { email: '', @@ -104,14 +104,22 @@ export default { }, async sendTransaction() { this.loading = true - const result = await communityAPI.send(this.$store.state.sessionId, this.transactionData) - if (result.success) { - this.error = false - this.$emit('update-balance', this.transactionData.amount) - } else { - this.errorResult = result.result.message - this.error = true - } + this.$apollo + .query({ + query: sendCoins, + variables: { + sessionId: this.$store.state.sessionId, + ...this.transactionData, + }, + }) + .then(() => { + this.error = false + this.$emit('update-balance', this.transactionData.amount) + }) + .catch((err) => { + this.errorResult = err.message + this.error = true + }) this.currentTransactionStep = 2 this.loading = false }, diff --git a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue index a9c8a558f..fff400646 100644 --- a/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue +++ b/frontend/src/views/Pages/AccountOverview/GddTransactionList.vue @@ -2,8 +2,8 @@
diff --git a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue index 581208026..8193717cf 100644 --- a/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue +++ b/frontend/src/views/Pages/AccountOverview/GdtTransactionList.vue @@ -6,16 +6,24 @@
@@ -29,7 +37,7 @@ - +
{{ $t('gdt.gdt-receive') }}
{{ $t('gdt.credit') }}
@@ -40,7 +48,7 @@
- +
{{ $t('gdt.your-share') }}
{{ $t('gdt.credit') }}
@@ -65,7 +73,7 @@ - +
{{ $t('form.memo') }}
@@ -87,21 +95,21 @@ - +
-
+
{{ $t('gdt.conversion-gdt-euro') }}
-
+
{{ $t('gdt.publisher') }}
{{ $t('gdt.calculation') }}
- +
{{ $t('gdt.raise') }}
{{ $t('gdt.conversion') }}
@@ -115,7 +123,7 @@
- +
@@ -152,7 +160,7 @@