Merge branch 'monterail_vue3_migration_frontend--settings-page-refactor' into monterail_vue3_migration

This commit is contained in:
Kamila 2024-08-09 08:40:29 +02:00
commit dd9688ea53
167 changed files with 11101 additions and 14681 deletions

View File

@ -44,14 +44,14 @@ jobs:
uses: actions/checkout@v3
- name: Backend | docker-compose mariadb
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: Backend | docker-compose database
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Backend | Unit tests
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test

View File

@ -43,13 +43,13 @@ jobs:
uses: actions/checkout@v3
- name: Database | docker-compose
run: docker-compose -f docker-compose.yml up --detach mariadb
run: docker compose -f docker-compose.yml up --detach mariadb
- name: Database | up
run: docker-compose -f docker-compose.yml run -T database yarn up
run: docker compose -f docker-compose.yml run -T database yarn up
- name: Database | reset
run: docker-compose -f docker-compose.yml run -T database yarn reset
run: docker compose -f docker-compose.yml run -T database yarn reset
lint:
if: needs.files-changed.outputs.database == 'true'

View File

@ -71,15 +71,15 @@ jobs:
- name: Load Docker Image
run: docker load < /tmp/dht-node.tar
- name: docker-compose mariadb
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: docker compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: docker-compose database
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: docker compose database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Sleep for 30 seconds
run: sleep 30s

View File

@ -61,8 +61,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3
- name: DLT-Connector | docker-compose mariadb
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: DLT-Connector | docker compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s

View File

@ -11,17 +11,17 @@ jobs:
uses: actions/checkout@v3
- name: Boot up test system | docker-compose mariadb
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
- name: Boot up test system | docker-compose database
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Boot up test system | docker-compose backend
run: |
cd backend
cp .env.test_e2e .env
cd ..
docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend
docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend
- name: Sleep for 10 seconds
run: sleep 10s
@ -35,10 +35,10 @@ jobs:
yarn && yarn seed
- name: Boot up test system | docker-compose frontends
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx
- name: Boot up test system | docker-compose mailserver
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver
- name: End-to-end tests | prepare
run: |

View File

@ -70,15 +70,15 @@ jobs:
- name: Load Docker Image
run: docker load < /tmp/federation.tar
- name: docker-compose mariadb
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: docker compose mariadb
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
- name: Sleep for 30 seconds
run: sleep 30s
shell: bash
- name: docker-compose database
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: docker compose database
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: Sleep for 30 seconds
run: sleep 30s

View File

@ -95,6 +95,9 @@ services:
#########################################################
mariadb:
image: gradido/mariadb:test
environment:
- MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1
- MARIADB_USER=root
networks:
- internal-net
- external-net

View File

@ -4,15 +4,17 @@ module.exports = {
browser: true,
node: true,
jest: true,
'vue/setup-compiler-macros': true,
},
parserOptions: {
parser: 'babel-eslint',
ecmaVersion: 2020,
},
extends: [
'standard',
'plugin:vue/essential',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
'plugin:@intlify/vue-i18n/recommended',
'prettier',
],
// required to lint *.vue files
plugins: ['vue', 'prettier', 'jest'],
@ -30,14 +32,26 @@ module.exports = {
rules: {
'no-console': ['error'],
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-useless-escape': 0,
'no-unused-vars': 0, // TODO remove at the end of migration and fix
'vue/component-name-in-template-casing': ['error', 'kebab-case'],
'vue/no-static-inline-styles': [
'error',
{
allowBinding: false,
},
],
// 'vue/no-static-inline-styles': [
// 'error',
// {
// allowBinding: false,
// },
// ],
'vue/multi-word-component-names': 0,
'vue/no-v-html': 0,
'vue/no-export-in-script-setup': 0, // TODO remove at the end of migration and fix
'vue/prop-name-casing': 0, // TODO remove at the end of migration and fix
'vue/require-explicit-emits': 0, // TODO remove at the end of migration and fix
'vue/no-static-inline-styles': 0, // TODO remove at the end of migration and fix
'vue/v-on-event-hyphenation': 0, // TODO remove at the end of migration and fix
'vue/require-default-prop': 0, // TODO remove at the end of migration and fix
'vue/no-computed-properties-in-data': 0, // TODO remove at the end of migration and fix
'@intlify/vue-i18n/no-dynamic-keys': 'error',
'@intlify/vue-i18n/no-missing-keys': 0, // TODO remove at the end of migration and fix
'@intlify/vue-i18n/no-unused-keys': [
'error',
{
@ -75,7 +89,7 @@ module.exports = {
localeDir: './src/locales/{en,de}.json',
// Specify the version of `vue-i18n` you are using.
// If not specified, the message will be parsed twice.
messageSyntaxVersion: '^8.22.4',
messageSyntaxVersion: '^9.13.1',
},
},
}

2
frontend/.gitignore vendored
View File

@ -24,3 +24,5 @@ package-lock.json
coverage/
*~
components.d.ts

View File

@ -1,8 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: {
// to edit target browsers: use "browserslist" field in package.json
autoprefixer: {}
}
};

View File

@ -1,7 +1,7 @@
##################################################################################
# BASE ###########################################################################
##################################################################################
FROM node:16-alpine3.14 as base
FROM node:18.20-alpine3.20 as base
# ENVs (available in production aswell, can be overwritten by commandline or env file)
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
@ -98,13 +98,13 @@ COPY --from=build ${DOCKER_WORKDIR}/run ./run
CMD /bin/sh -c "yarn run start"
## add `/usr/src/app/node_modules/.bin` to $PATH
#ENV PATH /usr/src/app/node_modules/.bin:$PATH
#
#
## install and cache app dependencies
#COPY package.json /usr/src/app/package.json
#RUN npm install
#RUN npm install -g @vue/cli
## start app
#CMD ["npm", "run", "serve"]
#CMD ["npm", "run", "serve"]

View File

@ -11,7 +11,7 @@ module.exports = function (api) {
],
]
if (process.env.NODE_ENV === 'test') {
if (import.meta.env.NODE_ENV === 'test') {
plugins.push('transform-require-context')
}

164
frontend/components.d.ts vendored Normal file
View File

@ -0,0 +1,164 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AmountAndNameRow: typeof import('./src/components/TransactionRows/AmountAndNameRow.vue')['default']
AuthCarousel: typeof import('./src/components/Auth/AuthCarousel.vue')['default']
AuthFooter: typeof import('./src/components/Auth/AuthFooter.vue')['default']
AuthNavbar: typeof import('./src/components/Auth/AuthNavbar.vue')['default']
AuthNavbarSmall: typeof import('./src/components/Auth/AuthNavbarSmall.vue')['default']
BAvatar: typeof import('bootstrap-vue-next')['BAvatar']
BBadge: typeof import('bootstrap-vue-next')['BBadge']
BButton: typeof import('bootstrap-vue-next')['BButton']
BCard: typeof import('bootstrap-vue-next')['BCard']
BCardBody: typeof import('bootstrap-vue-next')['BCardBody']
BCardText: typeof import('bootstrap-vue-next')['BCardText']
BCardTitle: typeof import('bootstrap-vue-next')['BCardTitle']
BCarousel: typeof import('bootstrap-vue-next')['BCarousel']
BCarouselSlide: typeof import('bootstrap-vue-next')['BCarouselSlide']
BCol: typeof import('bootstrap-vue-next')['BCol']
BCollapse: typeof import('bootstrap-vue-next')['BCollapse']
BContainer: typeof import('bootstrap-vue-next')['BContainer']
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
BForm: typeof import('bootstrap-vue-next')['BForm']
BFormCheckbox: typeof import('bootstrap-vue-next')['BFormCheckbox']
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
BFormInvalidFeedback: typeof import('bootstrap-vue-next')['BFormInvalidFeedback']
BFormSelect: typeof import('bootstrap-vue-next')['BFormSelect']
BFormTextarea: typeof import('bootstrap-vue-next')['BFormTextarea']
BImg: typeof import('bootstrap-vue-next')['BImg']
BInputGroup: typeof import('bootstrap-vue-next')['BInputGroup']
BLink: typeof import('bootstrap-vue-next')['BLink']
BModal: typeof import('bootstrap-vue-next')['BModal']
BNav: typeof import('bootstrap-vue-next')['BNav']
BNavbar: typeof import('bootstrap-vue-next')['BNavbar']
BNavbarBrand: typeof import('bootstrap-vue-next')['BNavbarBrand']
BNavbarNav: typeof import('bootstrap-vue-next')['BNavbarNav']
BNavItem: typeof import('bootstrap-vue-next')['BNavItem']
BPagination: typeof import('bootstrap-vue-next')['BPagination']
BPopover: typeof import('bootstrap-vue-next')['BPopover']
Breadcrumb: typeof import('./src/components/Breadcrumb/breadcrumb.vue')['default']
BRow: typeof import('bootstrap-vue-next')['BRow']
BTab: typeof import('bootstrap-vue-next')['BTab']
BTabs: typeof import('bootstrap-vue-next')['BTabs']
BToastOrchestrator: typeof import('bootstrap-vue-next')['BToastOrchestrator']
ClipboardCopy: typeof import('./src/components/ClipboardCopy.vue')['default']
CollapseIcon: typeof import('./src/components/TransactionRows/CollapseIcon.vue')['default']
CollapseLinksList: typeof import('./src/components/DecayInformations/CollapseLinksList.vue')['default']
CommunityMember: typeof import('./src/components/Template/ContentHeader/CommunityMember.vue')['default']
CommunityNews: typeof import('./src/components/Overview/CommunityNews.vue')['default']
CommunitySwitch: typeof import('./src/components/CommunitySwitch.vue')['default']
ContentFooter: typeof import('./src/components/ContentFooter.vue')['default']
ContributionForm: typeof import('./src/components/Contributions/ContributionForm.vue')['default']
ContributionInfo: typeof import('./src/components/Template/RightSide/ContributionInfo.vue')['default']
ContributionList: typeof import('./src/components/Contributions/ContributionList.vue')['default']
ContributionListItem: typeof import('./src/components/Contributions/ContributionListItem.vue')['default']
ContributionMessagesFormular: typeof import('./src/components/ContributionMessages/ContributionMessagesFormular.vue')['default']
ContributionMessagesList: typeof import('./src/components/ContributionMessages/ContributionMessagesList.vue')['default']
ContributionMessagesListItem: typeof import('./src/components/ContributionMessages/ContributionMessagesListItem.vue')['default']
DateRow: typeof import('./src/components/TransactionRows/DateRow.vue')['default']
DecayInformation: typeof import('./src/components/DecayInformations/DecayInformation.vue')['default']
DecayInformationBeforeStartblock: typeof import('./src/components/DecayInformations/DecayInformation-BeforeStartblock.vue')['default']
DecayInformationDecay: typeof import('./src/components/DecayInformations/DecayInformation-Decay.vue')['default']
DecayInformationDecayStartblock: typeof import('./src/components/DecayInformations/DecayInformation-DecayStartblock.vue')['default']
DecayInformationLong: typeof import('./src/components/DecayInformations/DecayInformation-Long.vue')['default']
DecayInformationShort: typeof import('./src/components/DecayInformations/DecayInformation-Short.vue')['default']
DecayRow: typeof import('./src/components/TransactionRows/DecayRow.vue')['default']
DurationRow: typeof import('./src/components/TransactionRows/DurationRow.vue')['default']
FigureQrCode: typeof import('./src/components/QrCode/FigureQrCode.vue')['default']
FirstName: typeof import('./src/components/Inputs/FirstName.vue')['default']
GddAmount: typeof import('./src/components/Template/ContentHeader/GddAmount.vue')['default']
GddSend: typeof import('./src/components/GddSend.vue')['default']
GddTransactionList: typeof import('./src/components/GddTransactionList.vue')['default']
GddTransactionListFooter: typeof import('./src/components/GddTransactionListFooter.vue')['default']
GdtAmount: typeof import('./src/components/Template/ContentHeader/GdtAmount.vue')['default']
GdtTransactionList: typeof import('./src/components/GdtTransactionList.vue')['default']
IBiArrowDownCircle: typeof import('~icons/bi/arrow-down-circle')['default']
IBiArrowUpCircle: typeof import('~icons/bi/arrow-up-circle')['default']
IBiBellFill: typeof import('~icons/bi/bell-fill')['default']
IBiCaretDownFill: typeof import('~icons/bi/caret-down-fill')['default']
IBiCash: typeof import('~icons/bi/cash')['default']
IBiChatDots: typeof import('~icons/bi/chat-dots')['default']
IBiCheck: typeof import('~icons/bi/check')['default']
IBiEye: typeof import('~icons/bi/eye')['default']
IBiEyeSlash: typeof import('~icons/bi/eye-slash')['default']
IBiLayers: typeof import('~icons/bi/layers')['default']
IBiPencil: typeof import('~icons/bi/pencil')['default']
IBiPeople: typeof import('~icons/bi/people')['default']
IBiQuestion: typeof import('~icons/bi/question')['default']
IBiShieldCheck: typeof import('~icons/bi/shield-check')['default']
IBiTrash: typeof import('~icons/bi/trash')['default']
IBiXCircle: typeof import('~icons/bi/x-circle')['default']
InputAmount: typeof import('./src/components/Inputs/InputAmount.vue')['default']
InputEmail: typeof import('./src/components/Inputs/InputEmail.vue')['default']
InputHour: typeof import('./src/components/Inputs/InputHour.vue')['default']
InputIdentifier: typeof import('./src/components/Inputs/InputIdentifier.vue')['default']
InputPassword: typeof import('./src/components/Inputs/InputPassword.vue')['default']
InputPasswordConfirmation: typeof import('./src/components/Inputs/InputPasswordConfirmation.vue')['default']
InputTextarea: typeof import('./src/components/Inputs/InputTextarea.vue')['default']
InputUsername: typeof import('./src/components/Inputs/InputUsername.vue')['default']
LanguageSwitch: typeof import('./src/components/LanguageSwitch.vue')['default']
LanguageSwitch2: typeof import('./src/components/LanguageSwitch2.vue')['default']
LanguageSwitchSelect: typeof import('./src/components/LanguageSwitchSelect.vue')['default']
LastContributions: typeof import('./src/components/Template/RightSide/LastContributions.vue')['default']
LastName: typeof import('./src/components/Inputs/LastName.vue')['default']
LastTransactions: typeof import('./src/components/Template/RightSide/LastTransactions.vue')['default']
LinkCountRow: typeof import('./src/components/TransactionRows/LinkCountRow.vue')['default']
MemoRow: typeof import('./src/components/TransactionRows/MemoRow.vue')['default']
Message: typeof import('./src/components/Message/Message.vue')['default']
MobileSidebar: typeof import('./src/components/MobileSidebar/MobileSidebar.vue')['default']
Name: typeof import('./src/components/TransactionRows/Name.vue')['default']
Navbar: typeof import('./src/components/Menu/Navbar.vue')['default']
NavCommunity: typeof import('./src/components/Template/ContentHeader/NavCommunity.vue')['default']
OpenCreationsAmount: typeof import('./src/components/Contributions/OpenCreationsAmount.vue')['default']
Overview: typeof import('./src/components/skeleton/Overview.vue')['default']
ParseMessage: typeof import('./src/components/ContributionMessages/ParseMessage.vue')['default']
RedeemedTextBox: typeof import('./src/components/LinkInformations/RedeemedTextBox.vue')['default']
RedeemInformation: typeof import('./src/components/LinkInformations/RedeemInformation.vue')['default']
RedeemLoggedOut: typeof import('./src/components/LinkInformations/RedeemLoggedOut.vue')['default']
RedeemSelfCreator: typeof import('./src/components/LinkInformations/RedeemSelfCreator.vue')['default']
RedeemValid: typeof import('./src/components/LinkInformations/RedeemValid.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SessionLogoutTimeout: typeof import('./src/components/SessionLogoutTimeout.vue')['default']
Sidebar: typeof import('./src/components/Menu/Sidebar.vue')['default']
Status: typeof import('./src/components/Status.vue')['default']
Transaction: typeof import('./src/components/Transaction.vue')['default']
TransactionCollapse: typeof import('./src/components/TransactionCollapse.vue')['default']
TransactionConfirmationLink: typeof import('./src/components/GddSend/TransactionConfirmationLink.vue')['default']
TransactionConfirmationSend: typeof import('./src/components/GddSend/TransactionConfirmationSend.vue')['default']
TransactionCreation: typeof import('./src/components/Transactions/TransactionCreation.vue')['default']
TransactionDecay: typeof import('./src/components/Transactions/TransactionDecay.vue')['default']
TransactionForm: typeof import('./src/components/GddSend/TransactionForm.vue')['default']
TransactionLink: typeof import('./src/components/TransactionLinks/TransactionLink.vue')['default']
TransactionLinkItem: typeof import('./src/components/TransactionLinkItem.vue')['default']
TransactionLinkSummary: typeof import('./src/components/Transactions/TransactionLinkSummary.vue')['default']
TransactionListItem: typeof import('./src/components/TransactionListItem.vue')['default']
TransactionReceive: typeof import('./src/components/Transactions/TransactionReceive.vue')['default']
TransactionResultLink: typeof import('./src/components/GddSend/TransactionResultLink.vue')['default']
TransactionResultSendError: typeof import('./src/components/GddSend/TransactionResultSendError.vue')['default']
TransactionResultSendSuccess: typeof import('./src/components/GddSend/TransactionResultSendSuccess.vue')['default']
TransactionSend: typeof import('./src/components/Transactions/TransactionSend.vue')['default']
TypeIcon: typeof import('./src/components/TransactionRows/TypeIcon.vue')['default']
UserCard: typeof import('./src/components/UserSettings/UserCard.vue')['default']
UserData: typeof import('./src/components/UserSettings/UserData.vue')['default']
UserGMSLocation: typeof import('./src/components/UserSettings/UserGMSLocation.vue')['default']
UserGMSLocationFormat: typeof import('./src/components/UserSettings/UserGMSLocationFormat.vue')['default']
UserLanguage: typeof import('./src/components/UserSettings/UserLanguage.vue')['default']
UserName: typeof import('./src/components/UserSettings/UserName.vue')['default']
UserNamingFormat: typeof import('./src/components/UserSettings/UserNamingFormat.vue')['default']
UserNewsletter: typeof import('./src/components/UserSettings/UserNewsletter.vue')['default']
UserPassword: typeof import('./src/components/UserSettings/UserPassword.vue')['default']
UserSettingsSwitch: typeof import('./src/components/UserSettings/UserSettingsSwitch.vue')['default']
}
export interface ComponentCustomProperties {
vBToggle: typeof import('bootstrap-vue-next')['vBToggle']
}
}

69
frontend/index.html Normal file
View File

@ -0,0 +1,69 @@
<!--
=========================================================
* Gradido Konto - v1.6.1
=========================================================
* Product Page: https://www.gradido.net
* Copyright 2021 Gradido-Akademie - (https://www.gradido.net)
* Coded by www.gradido.net
=========================================================
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-->
<!DOCTYPE html>
<html>
<head>
<meta name="title" lang="de" content="VITE test">
<meta name="title" lang="en" content="VITE test">
<meta name="description" lang="de" content="VITE test">
<meta name="description" lang="en" content="VITE test">
<meta name="keywords" lang="de" content="VITE test">
<meta name="keywords" lang="en" content="VITE test">
<meta name="author" content="VITE test">
<meta name="url" content="VITE test">
<meta name="identifier-URL" content="VITE test">
<!-- <meta name="title" lang="de" content="<%= htmlWebpackPlugin.options.meta.title_de %>">-->
<!-- <meta name="title" lang="en" content="<%= htmlWebpackPlugin.options.meta.title_en %>">-->
<!-- <meta name="description" lang="de" content="<%= htmlWebpackPlugin.options.meta.description_de %>">-->
<!-- <meta name="description" lang="en" content="<%= htmlWebpackPlugin.options.meta.description_en %>">-->
<!-- <meta name="keywords" lang="de" content="<%= htmlWebpackPlugin.options.meta.keywords_de %>">-->
<!-- <meta name="keywords" lang="en" content="<%= htmlWebpackPlugin.options.meta.keywords_en %>">-->
<!-- <meta name="author" content="<%= htmlWebpackPlugin.options.meta.author %>">-->
<!-- <meta name="url" content="<%= htmlWebpackPlugin.options.meta.url %>">-->
<!-- <meta name="identifier-URL" content="<%= htmlWebpackPlugin.options.meta.url %>">-->
<meta charset="utf-8">
<meta name="robots" content="index,follow">
<link rel='canonical' href='VITE test'>
<!-- <link rel='canonical' href='<%= htmlWebpackPlugin.options.meta.url %>'>-->
<link rel="icon" type="image/png" sizes="96x96" href="/favicon.png">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
<!-- <meta property="og:title" content="<%= htmlWebpackPlugin.options.meta.title_de %>" />-->
<!-- <meta property="og:description" content="<%= htmlWebpackPlugin.options.meta.description_de %>" />-->
<!-- <meta property="og:type" content="Website" />-->
<!-- <meta property="og:url" content="<%= htmlWebpackPlugin.options.meta.url %>" />-->
<!-- <meta property="og:image" content="<%= htmlWebpackPlugin.options.meta.url %>/img/gradido-wallet-650-400.jpg" />-->
<!-- <meta lang="de" name="twitter:title" content="<%= htmlWebpackPlugin.options.meta.title_de %>">-->
<!-- <meta lang="en" name="twitter:title" content="<%= htmlWebpackPlugin.options.meta.title_en %>">-->
<!-- <meta lang="de" name="twitter:description" content="<%= htmlWebpackPlugin.options.meta.description_de %>">-->
<!-- <meta lang="en" name="twitter:description" content="<%= htmlWebpackPlugin.options.meta.description_en %>">-->
<!-- <meta name="twitter:image" content="<%= htmlWebpackPlugin.options.meta.url %>/img/gradido-wallet-650-400.jpg">-->
<meta name="twitter:card" content="summary_large_image">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -4,95 +4,101 @@
"private": true,
"scripts": {
"start": "node run/server.js",
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"postbuild": "find build -type f -regex '.*\\.\\(html\\|js\\|css\\|svg\\|json\\)' -exec gzip -9 -k {} +",
"dev": "yarn run serve",
"analyse-bundle": "yarn build && webpack-bundle-analyzer build/webpack.stats.json",
"lint": "eslint --max-warnings=0 --ext .js,.vue,.json .",
"stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'",
"test": "cross-env TZ=UTC jest",
"test": "echo Tests are temporarly disabled for migration time",
"locales": "scripts/sort.sh"
},
"dependencies": {
"@babel/core": "^7.13.13",
"@babel/node": "^7.13.13",
"@babel/preset-env": "^7.13.12",
"@vue/cli-plugin-unit-jest": "^4.5.12",
"@vee-validate/i18n": "^4.13.2",
"@vee-validate/rules": "^4.13.2",
"@vee-validate/yup": "^4.13.2",
"@vitejs/plugin-vue": "3.2.0",
"@vue/apollo-composable": "^4.0.2",
"@vue/apollo-option": "^4.0.0",
"@vue/compat": "^3.4.31",
"@vue/test-utils": "^1.1.3",
"apollo-boost": "^0.4.9",
"autoprefixer": "^10.4.19",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^27.3.1",
"babel-preset-vue": "^2.0.2",
"bootstrap": "^4.5.3",
"bootstrap-vue": "^2.21.2",
"bootstrap": "^5.3.3",
"bootstrap-vue-next": "^0.23.3",
"clipboard-polyfill": "^4.0.0-rc1",
"date-fns": "^2.29.3",
"es6-promise": "^4.1.1",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.1.0",
"eslint-config-standard": "^16.0.2",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.3.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-vue": "^7.8.0",
"express": "^4.17.1",
"flatpickr": "^4.5.7",
"flush-promises": "^1.0.2",
"graphql": "^15.5.1",
"graphql": "^16.9.0",
"graphql-tag": "^2.12.6",
"identity-obj-proxy": "^3.0.0",
"jest": "^26.6.3",
"jest-canvas-mock": "^2.5.0",
"jwt-decode": "^3.1.2",
"portal-vue": "^2.1.7",
"prettier": "^2.2.1",
"portal-vue": "^3.0.0",
"qrcanvas-vue": "2.1.1",
"regenerator-runtime": "^0.13.7",
"uuid": "^9.0.0",
"vee-validate": "^3.4.5",
"vue": "2.6.12",
"vue-apollo": "^3.0.7",
"vee-validate": "^4.13.2",
"vite": "3.2.10",
"vite-plugin-commonjs": "^0.10.1",
"vue": "3.4.31",
"vue-apollo": "^3.1.2",
"vue-avatar": "^2.3.3",
"vue-flatpickr-component": "^8.1.2",
"vue-focus": "^2.1.0",
"vue-i18n": "^8.22.4",
"vue-i18n": "^9.13.1",
"vue-jest": "^3.0.7",
"vue-loading-overlay": "^3.4.2",
"vue-router": "^3.0.6",
"vue-router": "^4.4.0",
"vue-timer-hook": "^1.0.84",
"vue-timers": "^2.0.4",
"vue2-transitions": "^0.2.3",
"vuex": "^3.6.0",
"vuex-persistedstate": "^4.0.0-beta.3"
"vuex": "^4.1.0",
"vuex-persistedstate": "^4.1.0",
"yup": "^1.4.0"
},
"devDependencies": {
"@apollo/client": "^3.7.4",
"@apollo/client": "^3.10.8",
"@iconify-json/bi": "^1.1.23",
"@intlify/eslint-plugin-vue-i18n": "^1.4.0",
"@vue/cli-plugin-babel": "^3.7.0",
"@vue/cli-plugin-eslint": "^3.7.0",
"@vue/cli-service": "^3.7.0",
"@vue/compiler-sfc": "^3.4.35",
"@vue/eslint-config-prettier": "^4.0.1",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.0",
"babel-plugin-transform-require-context": "^0.1.1",
"cross-env": "^7.0.3",
"dotenv-webpack": "^7.0.3",
"eslint": "8.57.0",
"eslint-config-prettier": "8.10.0",
"eslint-config-standard": "^16.0.3",
"eslint-loader": "^4.0.2",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^25.2.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-vue": "8.7.1",
"mock-apollo-client": "^1.2.1",
"postcss": "^8.4.8",
"postcss-html": "^1.3.0",
"postcss-scss": "^4.0.3",
"sass": "1.32.13",
"sass-loader": "^10",
"stats-webpack-plugin": "^0.7.0",
"stylelint": "^14.5.3",
"stylelint-config-recommended-vue": "^1.3.0",
"stylelint-config-standard-scss": "^3.0.0",
"vue-cli-plugin-i18n": "^1.0.1",
"vue-html-webpack-plugin": "^3.2.2",
"vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^4.5.0"
"prettier": "^3.3.3",
"stylelint": "16.7.0",
"stylelint-config-recommended-vue": "1.5.0",
"stylelint-config-standard-scss": "13.1.0",
"unplugin-icons": "^0.19.1",
"unplugin-vue-components": "^0.27.3",
"vue-html-webpack-plugin": "^3.2.2"
},
"postcss": {
"plugins": {
@ -111,5 +117,10 @@
"ignore": [
"**/*.spec.js"
]
},
"resolutions": {
"strip-ansi": "6.0.1",
"string-width": "4.2.2",
"wrap-ansi": "7.0.0"
}
}

View File

@ -4,7 +4,7 @@ const path = require('path')
// Host & Port
const hostname = '127.0.0.1'
const port = process.env.PORT || 3000
const port = import.meta.env.PORT || 3000
// Express Server
const app = express()

View File

@ -1,6 +1,7 @@
<template>
<div id="app" ref="app" :class="darkMode ? 'dark-mode' : ''">
<div :class="$route.meta.requiresAuth ? 'appContent' : ''">
<BToastOrchestrator />
<div :class="$route.meta.requiresAuth ? 'app-content' : ''">
<component :is="$route.meta.requiresAuth ? 'DashboardLayout' : 'AuthLayout'" />
<div class="goldrand position-fixed fixed-bottom zindex1000"></div>
</div>
@ -13,43 +14,47 @@ import AuthLayout from '@/layouts/AuthLayout'
export default {
name: 'App',
components: {
DashboardLayout,
AuthLayout,
},
computed: {
darkMode() {
return this.$store.state.darkMode
},
},
components: {
DashboardLayout,
AuthLayout,
},
}
</script>
<style>
@font-face {
font-family: 'WorkSans', sans-serif !important;
src: url(./assets/scss/fonts/WorkSans-VariableFont_wght.ttf) format('truetype');
font-family: WorkSans, sans-serif !important;
src: url('./assets/scss/fonts/WorkSans-VariableFont_wght.ttf') format('truetype');
}
#app {
font-size: 1rem;
font-family: 'WorkSans', sans-serif !important;
font-family: WorkSans, sans-serif !important;
}
.appContent {
.app-content {
min-width: 330px;
max-width: 1320px;
margin-right: auto;
margin-left: auto;
}
.appBoxShadow {
-webkit-box-shadow: 20pt 20pt 50pt 0 #3838384f;
.app-box-shadow {
box-shadow: 20pt 20pt 50pt 0 #3838384f;
}
@media screen and (max-width: 500px) {
@media screen and (width <= 500px) {
#app {
font-size: 0.85rem;
}
}
@media screen and (max-width: 1024px) {
@media screen and (width <= 1024px) {
#app {
padding-left: 15px;
padding-right: 15px;
@ -59,11 +64,11 @@ export default {
.goldrand {
background: linear-gradient(
90deg,
rgba(197, 141, 56, 1) 6%,
rgba(243, 205, 124, 1) 30%,
rgba(219, 176, 86, 1) 54%,
rgba(238, 192, 95, 1) 63%,
rgba(204, 157, 61, 1) 88%
rgb(197 141 56 / 100%) 6%,
rgb(243 205 124 / 100%) 30%,
rgb(219 176 86 / 100%) 54%,
rgb(238 192 95 / 100%) 63%,
rgb(204 157 61 / 100%) 88%
);
height: 13px;
}
@ -77,6 +82,7 @@ export default {
height: 50px;
text-align: left;
}
.dropdown-toggle::after {
float: right;
top: 50%;

View File

@ -978,4 +978,5 @@ $btn-border-radius-sm: $input-border-radius !default;
// Import Bootstrap variable defaults
@import "~bootstrap/scss/variables";
@import "bootstrap/scss/variables";
@import "gradido-custom/functions";

View File

@ -13,8 +13,8 @@ $box-shadow-sm: 0 0 0.5rem rgba($gray-600, 0.075) !default;
$box-shadow: 0 0 2rem 0 rgba($gray-600, 0.15) !default;
$box-shadow-lg: 0 0 3rem rgba($gray-600, 0.175) !default;
$component-active-color: $white !default;
$component-active-bg: theme-color("primary") !default;
$component-active-border-color: theme-color("primary") !default;
$component-active-bg: $primary !default;
$component-active-border-color: $primary !default;
$component-hover-color: $gray-300 !default;
$component-hover-bg: $gray-300 !default;
$component-hover-border-color: $gray-300 !default;

View File

@ -23,7 +23,7 @@ $custom-control-indicator-checked-border-color: $component-active-border-color !
$custom-control-indicator-checked-box-shadow: $custom-control-indicator-box-shadow !default;
// $custom-control-indicator-checked-disabled-bg: rgb(theme-color("primary") 0.5) !default;
$custom-control-indicator-checked-disabled-bg: theme-color("primary") !default;
$custom-control-indicator-checked-disabled-bg: $primary !default;
$custom-control-indicator-disabled-bg: $gray-200 !default;
$custom-control-label-disabled-color: $gray-600 !default;

View File

@ -2,4 +2,4 @@
$custom-toggle-width: 50px !default;
$custom-toggle-slider-bg: $gray-200 !default;
$custom-toggle-checked-bg: theme-color("primary") !default;
$custom-toggle-checked-bg: $primary !default;

View File

@ -12,9 +12,9 @@ $datepicker-disabled-cell-color: $gray-300 !default;
$datepicker-disabled-old-new-color: $gray-500 !default;
$datepicker-header-cell-border-radius: $border-radius !default;
$datepicker-active-color: $white !default;
$datepicker-active-background: theme-color("primary") !default;
$datepicker-active-background: $primary !default;
$datepicker-active-box-shadow: none !default;
$datepicker-range-background: theme-color("primary") !default;
$datepicker-range-background: $primary !default;
$datepicker-range-cell-focused-background: color.adjust($datepicker-range-background, $lightness: -5%);
$datepicker-range-color: $white !default;
$datepicker-range-highlighted-bg: $gray-200 !default;

View File

@ -3,7 +3,7 @@
$footer-padding-y: 2.5rem;
$footer-padding-x: 0;
$footer-link-font-size: 0.85rem !default;
$footer-bg: theme-color("secondary") !default;
$footer-bg: $secondary !default;
$footer-color: $gray-600 !default;
$footer-link-color: $gray-600 !default;
$footer-link-hover-color: $gray-700 !default;

View File

@ -2,6 +2,6 @@
@use "sass:color";
$form-feedback-valid-bg: color.adjust($success, $lightness: 15%) !default;
$form-feedback-valid-color: theme-color("success") !default;
$form-feedback-valid-color: $success !default;
$form-feedback-invalid-bg: color.adjust($warning, $lightness: 15%) !default;
$form-feedback-invalid-color: theme-color("warning") !default;
$form-feedback-invalid-color: $warning !default;

View File

@ -0,0 +1,3 @@
@function theme-color($key: "primary") {
@return #172b4d;
}

View File

@ -4,17 +4,17 @@
$nav-link-padding-y: 0.25rem !default;
$nav-link-padding-x: 0.75rem !default;
$nav-link-color: $gray-700 !default;
$nav-link-hover-color: theme-color("primary") !default;
$nav-link-hover-color: $primary !default;
$nav-link-disabled-color: $gray-600 !default;
$nav-pills-padding-y: 0.75rem !default;
$nav-pills-padding-x: 1rem !default;
$nav-pills-space-x: 1rem !default;
$nav-pills-bg: $white !default;
$nav-pills-border-width: 1px !default;
$nav-pills-border-color: theme-color("primary") !default;
$nav-pills-border-color: $primary !default;
$nav-pills-border-radius: $border-radius !default;
$nav-pills-link-color: theme-color("primary") !default;
$nav-pills-link-hover-color: color.adjust(theme-color("primary"), $lightness: -5%) !default;
$nav-pills-link-active-color: color-yiq(theme-color("primary")) !default;
$nav-pills-link-active-bg: theme-color("primary") !default;
$nav-pills-link-color: $primary !default;
$nav-pills-link-hover-color: color.adjust($primary, $lightness: -5%) !default;
$nav-pills-link-active-color: color-yiq($primary) !default;
$nav-pills-link-active-bg: $primary !default;
$nav-pills-box-shadow: $btn-box-shadow !default;

View File

@ -8,7 +8,7 @@ $noui-target-box-shadow: inset 0 1px 2px rgb(90 97 105 / 10%) !default;
$noui-slider-connect-bg: $primary !default;
$noui-slider-connect-disabled-bg: #b2b2b2 !default;
$noui-handle-width: 15px !default;
$noui-handle-bg: theme-color("primary") !default;
$noui-handle-bg: $primary !default;
$noui-handle-border: 0 !default;
$noui-handle-border-radius: 100% !default;
$noui-origin-border-radius: 2px !default;

View File

@ -17,10 +17,10 @@ $table-head-font-weight: $font-weight-bold !default;
$table-head-text-transform: uppercase !default;
$table-head-letter-spacing: 1px !default;
$table-body-font-size: 0.8125rem !default;
$table-dark-bg: theme-color("default") !default;
$table-dark-bg: $default !default;
$table-dark-accent-bg: rgba($white, 0.05) !default;
$table-dark-hover-bg: rgba($white, 0.075) !default;
$table-dark-border-color: color.adjust(theme-color("default"), $lightness: 7%) !default;
$table-dark-border-color: color.adjust($default, $lightness: 7%) !default;
$table-dark-color: $body-bg !default;
$table-dark-head-bg: color.adjust(theme-color("default"), $lightness: 4%) !default;
$table-dark-head-color: color.adjust(theme-color("default"), $lightness: 35%) !default;
$table-dark-head-bg: color.adjust($default, $lightness: 4%) !default;
$table-dark-head-color: color.adjust($default, $lightness: 35%) !default;

View File

@ -11,7 +11,7 @@ body {
.bg-gradient {
background: rgb(4 112 6);
background: linear-gradient(90deg, rgb(4 112 6 / 100%) 22%, rgb(197 141 56 / 100%) 98%);
background: linear-gradient(90deg, rgb(4 112 6 / 100%) 22%, rgb(197 141 56 / 100%) 98%) !important;
color: white;
}
@ -64,18 +64,18 @@ a:hover,
}
.btn-gradido {
display: inline-block;
padding: 0.6em 3em;
display: inline-block !important;
padding: 0.6em 3em !important;
letter-spacing: 0.05em;
color: #fff;
transition: all 0.5s ease;
color: #fff !important;
transition: all 0.5s ease !important;
background: rgb(249 205 105);
background: linear-gradient(135deg, rgb(249 205 105 / 100%) 2%, rgb(197 141 56 / 100%) 55%);
box-shadow: rgb(0 0 0 / 40%) 0 30px 90px;
border-radius: 26px;
padding-right: 50px;
padding-left: 50px;
border-style: none;
border-radius: 26px !important;
padding-right: 50px !important;
padding-left: 50px !important;
border-style: none !important;
}
.btn-gradido:hover {
@ -88,17 +88,17 @@ a:hover,
}
.btn-gradido-disable {
padding: 0.6em 3em;
padding: 0.6em 3em !important;
letter-spacing: 0.05em;
color: #fff;
transition: all 0.5s ease;
color: #fff !important;
transition: all 0.5s ease !important;
background: rgb(97 97 97);
background: linear-gradient(135deg, rgb(180 180 180 / 100%) 46%, rgb(180 180 180 / 100%) 99%);
box-shadow: rgb(0 0 0 / 40%) 0 30px 90px;
border-radius: 26px;
padding-right: 50px;
padding-left: 50px;
border-style: none;
border-radius: 26px !important;
padding-right: 50px !important;
padding-left: 50px !important;
border-style: none !important;
}
.btn-gradido-disable:hover {
@ -328,3 +328,8 @@ a:hover,
box-shadow: 0 0 0 0 rgb(204 169 44 / 0%);
}
}
.transparent-button {
background-color: transparent;
border: none;
}

View File

@ -1,55 +1,60 @@
// Bootstrap (4.5.3) functions
@import "~bootstrap/scss/functions";
@import "custom/variables";
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
// @import "~bootstrap/scss/variables"; wird am
// ende der custom/variables angehangen
// Bootstrap (4.5.3) mixins
@import "~bootstrap/scss/mixins";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
// Bootstrap (4.5.3) components
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/custom-forms";
@import "bootstrap/scss/alert";
@import "bootstrap/scss/badge";
@import "bootstrap/scss/dropdown";
@import "bootstrap/scss/button-group";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/card";
@import "bootstrap/scss/carousel";
@import "bootstrap/scss/close";
// @import "bootstrap/scss/code";
// @import "bootstrap/scss/custom-forms";
// @import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/forms";
@import "bootstrap/scss/forms";
// @import "~bootstrap/scss/functions";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/input-group";
@import "~bootstrap/scss/list-group";
@import "bootstrap/scss/grid";
// @import "bootstrap/scss/input-group";
@import "bootstrap/scss/list-group";
// @import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/print";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/toasts";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/bootstrap-grid";
@import "~bootstrap/scss/bootstrap-reboot";
@import "~bootstrap/scss/bootstrap";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/nav";
@import "bootstrap/scss/navbar";
@import "bootstrap/scss/pagination";
// @import "bootstrap/scss/print";
@import "bootstrap/scss/progress";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/tables";
@import "bootstrap/scss/toasts";
@import "bootstrap/scss/tooltip";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/type";
@import "bootstrap/scss/utilities";
// @import "bootstrap/scss/variables";
@import "bootstrap/scss/bootstrap-grid";
@import "bootstrap/scss/bootstrap-reboot";
@import "bootstrap/scss/bootstrap";
// Bootstrap-vue (2.21.1) scss
@import "~bootstrap-vue/src/index";
@import "custom/variables";
@import "gradido-template";
@import "gradido-template-dark";

View File

@ -1,19 +1,19 @@
<template>
<div>
<b-carousel :interval="13000">
<b-carousel-slide img-src="/img/template/Foto_01_2400_small.jpg">
<BCarousel :interval="13000" ride="carousel">
<BCarouselSlide img-src="/img/template/Foto_01_2400_small.jpg">
<div class="caption-first-text">{{ $t('auth.left.gratitude') }}</div>
<div class="caption-second-text">{{ $t('auth.left.oneGratitude') }}</div>
</b-carousel-slide>
<b-carousel-slide img-src="/img/template/Foto_02_2400_small.jpg">
</BCarouselSlide>
<BCarouselSlide img-src="/img/template/Foto_02_2400_small.jpg">
<div class="caption-first-text">{{ $t('auth.left.dignity') }}</div>
<div class="caption-second-text">{{ $t('auth.left.oneDignity') }}</div>
</b-carousel-slide>
<b-carousel-slide img-src="/img/template/Foto_03_2400_small.jpg">
</BCarouselSlide>
<BCarouselSlide img-src="/img/template/Foto_03_2400_small.jpg">
<div class="caption-first-text">{{ $t('auth.left.donation') }}</div>
<div class="caption-second-text">{{ $t('auth.left.oneDonation') }}</div>
</b-carousel-slide>
</b-carousel>
</BCarouselSlide>
</BCarousel>
</div>
</template>
@ -27,7 +27,7 @@ export default {
.carousel-caption {
color: #fff;
top: 317px;
text-shadow: 2px 2px 8px #000000;
text-shadow: 2px 2px 8px #000;
font-size: xx-large;
}
@ -48,10 +48,7 @@ export default {
.carousel-inner {
height: 100%;
border-radius: 0% 49% 49% 0% / 0% 51% 49% 0%;
-webkit-border-radius: 0% 49% 49% 0% / 0% 51% 49% 0%;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<footer class="footer">
<b-row class="mt-lg-7 mt-md-6 mt-4">
<b-col class="col-12 col-md-12 col-lg-6">
<BRow class="mt-lg-7 mt-md-6 mt-4">
<BCol class="col-12 col-md-12 col-lg-6">
<div
class="d-flex justify-content-center justify-content-md-center justify-content-lg-start ml-3"
>
@ -14,8 +14,8 @@
</b-nav-item>
</b-nav>
</div>
</b-col>
<b-col class="col-12 col-md-12 col-lg-6 mt-4 mb-4 mt-lg-0 mb-lg-0">
</BCol>
<BCol class="col-12 col-md-12 col-lg-6 mt-4 mb-4 mt-lg-0 mb-lg-0">
<div class="text-center ml-3 ml-lg-0 text-lg-right pt-1">
{{ $t('followUs') }}
<b-link href="https://www.facebook.com/groups/Gradido/" target="_blank">
@ -43,8 +43,8 @@
</svg>
</b-link>
</div>
</b-col>
</b-row>
</BCol>
</BRow>
</footer>
</template>

View File

@ -1,29 +1,24 @@
<template>
<div class="auth-header position-sticky">
<b-navbar :toggleable="false" class="pr-4">
<b-navbar-brand class="d-none d-lg-block">
<b-img
class="position-absolute ml--3 mt-lg--2 mt-3 p-2 zindex1000"
:src="logo"
width="200"
alt="Logo"
/>
<b-img
<BNavbar :toggleable="false" :container="false" class="d-flex">
<BNavbarBrand class="d-none d-lg-block">
<BImg class="position-absolute ml--3 mt-lg--2 p-2" :src="logo" width="200" alt="Logo" />
<BImg
class="mt--3 ml--3"
:src="background_header"
width="230"
alt="Background Image"
></b-img>
</b-navbar-brand>
<b-img class="sheet-img position-absolute d-block d-lg-none zindex1000" :src="sheet"></b-img>
<b-collapse id="nav-collapse" is-nav class="ml-5">
<b-navbar-nav class="ml-auto d-none d-lg-flex" right>
<b-nav-item :to="register" class="authNavbar ml-lg-5">{{ $t('signup') }}</b-nav-item>
></BImg>
</BNavbarBrand>
<BImg class="sheet-img position-absolute d-block d-lg-none zindex1000" :src="sheet"></BImg>
<BCollapse id="nav-collapse" is-nav>
<BNavbarNav class="ml-auto d-none d-lg-flex" right>
<BNavItem :to="register" class="auth-navbar ml-lg-5">{{ $t('signup') }}</BNavItem>
<span class="d-none d-lg-block mt-1">{{ $t('math.pipe') }}</span>
<b-nav-item :to="login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<BNavItem :to="login" class="auth-navbar">{{ $t('signin') }}</BNavItem>
</BNavbarNav>
</BCollapse>
</BNavbar>
</div>
</template>
@ -43,18 +38,32 @@ export default {
}
</script>
<style lang="scss">
.authNavbar > .nav-link {
<style scoped lang="scss">
.auth-navbar {
display: flex;
align-content: center;
> * {
text-decoration: none;
}
}
.auth-navbar > :deep(.nav-link) {
color: #0e79bc !important;
}
.authNavbar > .router-link-exact-active {
.auth-navbar > :deep(.router-link-exact-active) {
color: #383838 !important;
}
.auth-header {
font-family: 'Open Sans', sans-serif !important;
height: 150px;
z-index: 1;
}
:deep(#nav-collapse) {
justify-content: flex-end;
}
.sheet-img {
@ -63,18 +72,19 @@ export default {
max-width: 64%;
}
@media screen and (max-width: 1024px) {
@media screen and (width <= 1024px) {
.auth-header {
height: 100px;
}
}
@media screen and (max-width: 768px) {
@media screen and (width <= 768px) {
.auth-header {
height: 70px;
}
}
@media screen and (max-width: 450px) {
@media screen and (width <= 450px) {
.sheet-img {
top: -15px;
right: 0%;

View File

@ -2,9 +2,9 @@
<div class="navbar-small">
<b-navbar class="navi">
<b-navbar-nav>
<b-nav-item :to="register" class="authNavbar">{{ $t('signup') }}</b-nav-item>
<b-nav-item :to="register" class="auth-navbar">{{ $t('signup') }}</b-nav-item>
<span class="mt-1">{{ $t('math.pipe') }}</span>
<b-nav-item :to="login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
<b-nav-item :to="login" class="auth-navbar">{{ $t('signin') }}</b-nav-item>
</b-navbar-nav>
</b-navbar>
</div>
@ -20,7 +20,7 @@ export default {
</script>
<style scoped>
.navi {
margin-left: 0px;
padding-left: 0px;
margin-left: 0;
padding-left: 0;
}
</style>

View File

@ -5,14 +5,15 @@
<label>{{ $t('gdd_per_link.copy-link') }}</label>
<div
class="pointer text-center bg-secondary gradido-border-radius p-3"
@click="copyLink"
data-test="copyLink"
@click="copyLink"
>
{{ link }}
<div>
<b-button class="p-4">
<b-icon icon="link45deg"></b-icon>
</b-button>
<BButton class="p-4">
<IBiLink45deg />
<!-- <b-icon icon="link45deg"></b-icon>-->
</BButton>
</div>
</div>
</div>
@ -26,7 +27,8 @@
{{ linkText }}
<div>
<b-button class="p-4">
<b-icon icon="link45deg"></b-icon>
<IBiLink45deg />
<!-- <b-icon icon="link45deg"></b-icon>-->
</b-button>
</div>
</div>

View File

@ -1,92 +1,172 @@
<template>
<div class="community-switch">
<div v-if="!validCommunityIdentifier">
<b-dropdown no-flip :text="value.name">
<b-dropdown-item
<BDropdown no-flip :text="modelValue?.name">
<BDropdownItem
v-for="community in communities"
@click.prevent="updateCommunity(community)"
:key="community.id"
:title="community.description"
:active="value.uuid === community.uuid"
:active="modelValue?.uuid === community.uuid"
@click.prevent="updateCommunity(community)"
>
{{ community.name }}
</b-dropdown-item>
</b-dropdown>
</BDropdownItem>
</BDropdown>
</div>
<div v-else class="mb-4 mt-2">
<b-row>
<b-col class="font-weight-bold" :title="value.description">{{ value.name }}</b-col>
</b-row>
<BRow>
<BCol class="font-weight-bold" :title="modelValue.description">{{ modelValue.name }}</BCol>
</BRow>
</div>
</div>
</template>
<script>
import { selectCommunities } from '@/graphql/queries'
<!--<script>-->
<!--import { selectCommunities } from '@/graphql/queries'-->
export default {
name: 'CommunitySwitch',
props: {
value: {
type: Object,
},
},
data() {
return {
communities: [],
validCommunityIdentifier: false,
}
},
methods: {
updateCommunity(community) {
this.$emit('input', community)
},
setDefaultCommunity() {
// when we already get an identifier via url we choose this if the community exist
if (this.communityIdentifier && this.communities.length >= 1) {
const foundCommunity = this.communities.find((community) => {
if (
community.uuid === this.communityIdentifier ||
community.name === this.communityIdentifier
) {
this.validCommunityIdentifier = true
return true
}
return false
})
if (foundCommunity) {
this.updateCommunity(foundCommunity)
return
}
this.toastError('invalid community identifier in url')
}
if (this.validCommunityIdentifier && !this.communityIdentifier) {
this.validCommunityIdentifier = false
}
// set default community, the only one which isn't foreign
// we assume it is only one entry with foreign = false
if (this.value.uuid === '' && this.communities.length) {
const foundCommunity = this.communities.find((community) => !community.foreign)
if (foundCommunity) {
this.updateCommunity(foundCommunity)
}
}
},
},
apollo: {
communities: {
query: selectCommunities,
},
},
computed: {
communityIdentifier() {
return this.$route.params && this.$route.params.communityIdentifier
},
},
updated() {
this.setDefaultCommunity()
},
mounted() {
this.setDefaultCommunity()
<!--export default {-->
<!-- name: 'CommunitySwitch',-->
<!-- props: {-->
<!-- value: {-->
<!-- type: Object,-->
<!-- },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- communities: [],-->
<!-- validCommunityIdentifier: false,-->
<!-- }-->
<!-- },-->
<!-- methods: {-->
<!-- updateCommunity(community) {-->
<!-- this.$emit('input', community)-->
<!-- },-->
<!-- setDefaultCommunity() {-->
<!-- // when we already get an identifier via url we choose this if the community exist-->
<!-- if (this.communityIdentifier && this.communities.length >= 1) {-->
<!-- const foundCommunity = this.communities.find((community) => {-->
<!-- if (-->
<!-- community.uuid === this.communityIdentifier ||-->
<!-- community.name === this.communityIdentifier-->
<!-- ) {-->
<!-- this.validCommunityIdentifier = true-->
<!-- return true-->
<!-- }-->
<!-- return false-->
<!-- })-->
<!-- if (foundCommunity) {-->
<!-- this.updateCommunity(foundCommunity)-->
<!-- return-->
<!-- }-->
<!-- this.toastError('invalid community identifier in url')-->
<!-- }-->
<!-- if (this.validCommunityIdentifier && !this.communityIdentifier) {-->
<!-- this.validCommunityIdentifier = false-->
<!-- }-->
<!-- // set default community, the only one which isn't foreign-->
<!-- // we assume it is only one entry with foreign = false-->
<!-- if (this.value.uuid === '' && this.communities.length) {-->
<!-- const foundCommunity = this.communities.find((community) => !community.foreign)-->
<!-- if (foundCommunity) {-->
<!-- this.updateCommunity(foundCommunity)-->
<!-- }-->
<!-- }-->
<!-- },-->
<!-- },-->
<!-- apollo: {-->
<!-- communities: {-->
<!-- query: selectCommunities,-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- communityIdentifier() {-->
<!-- return this.$route.params && this.$route.params.communityIdentifier-->
<!-- },-->
<!-- },-->
<!-- updated() {-->
<!-- this.setDefaultCommunity()-->
<!-- },-->
<!-- mounted() {-->
<!-- this.setDefaultCommunity()-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch, onMounted, onUpdated } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import { useRoute } from 'vue-router'
import { selectCommunities } from '@/graphql/queries'
import { useAppToast } from '@/composables/useToast'
const props = defineProps({
modelValue: {
type: Object,
default: () => ({}),
},
})
const emit = defineEmits(['update:modelValue'])
const route = useRoute()
const { toastError } = useAppToast()
const communities = ref([])
const validCommunityIdentifier = ref(false)
const { result } = useQuery(selectCommunities)
watch(result, (data) => {
if (data) {
communities.value = data.communities
setDefaultCommunity()
}
})
const communityIdentifier = computed(() => route.params.communityIdentifier)
function updateCommunity(community) {
emit('update:modelValue', community)
}
function setDefaultCommunity() {
if (communityIdentifier.value && communities.value.length >= 1) {
const foundCommunity = communities.value.find((community) => {
if (
community.uuid === communityIdentifier.value ||
community.name === communityIdentifier.value
) {
validCommunityIdentifier.value = true
return true
}
return false
})
if (foundCommunity) {
updateCommunity(foundCommunity)
return
}
toastError('invalid community identifier in url')
}
if (validCommunityIdentifier.value && !communityIdentifier.value) {
validCommunityIdentifier.value = false
}
if (props.modelValue?.uuid === '' && communities.value.length) {
const foundCommunity = communities.value.find((community) => !community.foreign)
if (foundCommunity) {
updateCommunity(foundCommunity)
}
}
}
onMounted(setDefaultCommunity)
onUpdated(setDefaultCommunity)
</script>
<style scoped lang="scss">
.community-switch {
:deep(button) {
background-color: $secondary;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<footer class="footer m-4 p-4 bg-transparent">
<b-row align-v="center" class="mt-4 justify-content-lg-between">
<b-col>
<BRow align-v="center" class="mt-4 justify-content-lg-between">
<BCol>
<div class="copyright text-center text-lg-center text-muted">
{{ $t('footer.copyright.year', { year }) }}
<a
@ -23,18 +23,18 @@
{{ $t('footer.short_hash', { shortHash }) }}
</a>
</div>
</b-col>
</b-row>
<b-row align-v="center" class="justify-content-lg-between">
<b-col>
<b-nav class="nav-footer justify-content-center">
<b-nav-item :href="`https://gradido.net/${$i18n.locale}/impressum/`" target="_blank">
</BCol>
</BRow>
<BRow align-v="center" class="justify-content-lg-between">
<BCol>
<BNav class="nav-footer justify-content-center">
<BNavItem :href="`https://gradido.net/${$i18n.locale}/impressum/`" target="_blank">
{{ $t('footer.imprint') }}
</b-nav-item>
<b-nav-item :href="`https://gradido.net/${$i18n.locale}/datenschutz/`" target="_blank">
</BNavItem>
<BNavItem :href="`https://gradido.net/${$i18n.locale}/datenschutz/`" target="_blank">
{{ $t('footer.privacy_policy') }}
</b-nav-item>
<b-nav-item
</BNavItem>
<BNavItem
:href="
$i18n.locale === 'de'
? 'https://docs.google.com/document/d/1jZp-DiiMPI9ZPNXmjsvOQ1BtnfDFfx8BX7CDmA8KKjY/edit?usp=sharing'
@ -43,13 +43,13 @@
target="_blank"
>
{{ $t('footer.whitepaper') }}
</b-nav-item>
<b-nav-item :href="`mailto:${supportEmail}`" target="_blank">
</BNavItem>
<BNavItem :href="`mailto:${supportEmail}`" target="_blank">
{{ $t('navigation.support') }}
</b-nav-item>
</b-nav>
</b-col>
</b-row>
</BNavItem>
</BNav>
</BCol>
</BRow>
</footer>
</template>
<script>

View File

@ -2,77 +2,134 @@
<div class="contribution-messages-formular">
<small class="pl-2 pt-3">{{ $t('form.reply') }}</small>
<div>
<b-form @submit.prevent="onSubmit" @reset="onReset">
<b-form-textarea
<BForm @submit.prevent="onSubmit" @reset="onReset">
<BFormTextarea
id="textarea"
v-model="form.text"
:model-value="form.text"
:placeholder="$t('form.memo')"
rows="3"
></b-form-textarea>
<b-row class="mt-4 mb-4">
<b-col>
<b-button type="reset" variant="secondary">{{ $t('form.cancel') }}</b-button>
</b-col>
<b-col class="text-right">
<b-button type="submit" variant="gradido" :disabled="disabled">
@update:model-value="form.text = $event"
></BFormTextarea>
<BRow class="mt-4 mb-4">
<BCol>
<BButton type="reset" variant="secondary">{{ $t('form.cancel') }}</BButton>
</BCol>
<BCol class="text-right">
<BButton type="submit" variant="gradido" :disabled="disabled">
{{ $t('form.reply') }}
</b-button>
</b-col>
</b-row>
</b-form>
</BButton>
</BCol>
</BRow>
</BForm>
</div>
</div>
</template>
<script>
import { createContributionMessage } from '../../graphql/mutations.js'
<!--<script>-->
<!--import { createContributionMessage } from '../../graphql/mutations.js'-->
export default {
name: 'ContributionMessagesFormular',
props: {
contributionId: {
type: Number,
required: true,
},
},
data() {
return {
form: {
text: '',
},
isSubmitting: false,
}
},
methods: {
onSubmit() {
this.isSubmitting = true
this.$apollo
.mutate({
mutation: createContributionMessage,
variables: {
contributionId: this.contributionId,
message: this.form.text,
},
})
.then((result) => {
this.$emit('get-list-contribution-messages', false)
this.$emit('update-status', this.contributionId)
this.form.text = ''
this.toastSuccess(this.$t('message.reply'))
this.isSubmitting = false
})
.catch((error) => {
this.toastError(error.message)
this.isSubmitting = false
})
},
onReset() {
this.form.text = ''
},
},
computed: {
disabled() {
return this.form.text === '' || this.isSubmitting
},
<!--export default {-->
<!-- name: 'ContributionMessagesFormular',-->
<!-- props: {-->
<!-- contributionId: {-->
<!-- type: Number,-->
<!-- required: true,-->
<!-- },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- form: {-->
<!-- text: '',-->
<!-- },-->
<!-- isSubmitting: false,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- disabled() {-->
<!-- return this.form.text === '' || this.isSubmitting-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- onSubmit() {-->
<!-- this.isSubmitting = true-->
<!-- this.$apollo-->
<!-- .mutate({-->
<!-- mutation: createContributionMessage,-->
<!-- variables: {-->
<!-- contributionId: this.contributionId,-->
<!-- message: this.form.text,-->
<!-- },-->
<!-- })-->
<!-- .then((result) => {-->
<!-- this.$emit('get-list-contribution-messages', false)-->
<!-- this.$emit('update-status', this.contributionId)-->
<!-- this.form.text = ''-->
<!-- this.toastSuccess(this.$t('message.reply'))-->
<!-- this.isSubmitting = false-->
<!-- })-->
<!-- .catch((error) => {-->
<!-- this.toastError(error.message)-->
<!-- this.isSubmitting = false-->
<!-- })-->
<!-- },-->
<!-- onReset() {-->
<!-- this.form.text = ''-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed } from 'vue'
import { useMutation } from '@vue/apollo-composable'
import { useI18n } from 'vue-i18n'
import { createContributionMessage } from '@/graphql/mutations.js'
import { useAppToast } from '@/composables/useToast'
const props = defineProps({
contributionId: {
type: Number,
required: true,
},
})
const emit = defineEmits(['get-list-contribution-messages', 'update-status'])
const { t } = useI18n()
const { toastSuccess, toastError } = useAppToast()
const { mutate: createContributionMessageMutation } = useMutation(createContributionMessage)
const form = ref({
text: '',
})
const isSubmitting = ref(false)
const disabled = computed(() => {
return form.value.text === '' || isSubmitting.value
})
async function onSubmit() {
isSubmitting.value = true
try {
await createContributionMessageMutation({
contributionId: props.contributionId,
message: form.value.text,
})
emit('get-list-contribution-messages', false)
emit('update-status', props.contributionId)
form.value.text = ''
toastSuccess(t('message.reply'))
} catch (error) {
toastError(error.message)
} finally {
isSubmitting.value = false
}
}
function onReset() {
form.value.text = ''
}
</script>

View File

@ -1,24 +1,24 @@
<template>
<div class="contribution-messages-list">
<div>
<div v-for="message in messages" v-bind:key="message.id" class="mt-3">
<div v-for="message in messages" :key="message.id" class="mt-3">
<contribution-messages-list-item :message="message" />
</div>
</div>
<div>
<contribution-messages-formular
v-if="['PENDING', 'IN_PROGRESS'].includes(status)"
:contributionId="contributionId"
v-on="$listeners"
:contribution-id="contributionId"
v-bind="$attrs"
@update-status="updateStatus"
/>
</div>
<div v-b-toggle="'collapse' + String(contributionId)" class="text-center pointer clearboth">
<b-button variant="outline-primary" block class="mb-3">
<b-icon icon="arrow-up-short"></b-icon>
<BButton variant="outline-primary" block class="mb-3">
<IBiArrowUpShort />
{{ $t('form.close') }}
</b-button>
</BButton>
</div>
</div>
</template>

View File

@ -1,43 +1,43 @@
<template>
<div class="contribution-messages-list-item">
<div v-if="message.type === 'HISTORY'">
<b-row class="mb-3 border border-197 p-1">
<b-col cols="10">
<BRow class="mb-3 border border-197 p-1">
<BCol cols="10">
<small>{{ $d(new Date(message.createdAt), 'short') }}</small>
<div class="font-weight-bold" data-test="username" v-if="isNotModerator">
<div v-if="isNotModerator" class="font-weight-bold" data-test="username">
{{ storeName.username }} {{ $t('contribution.isEdited') }}
</div>
<div class="font-weight-bold" data-test="moderator-name" v-else>
<div v-else class="font-weight-bold" data-test="moderator-name">
{{ $t('community.moderator') }} {{ $t('contribution.isEdited') }}
</div>
<div class="small">
{{ $t('contribution.oldContribution') }}
</div>
<parse-message v-bind="message" data-test="message" class="p-2"></parse-message>
</b-col>
<b-col cols="2">
</BCol>
<BCol cols="2">
<avatar :username="storeName.username" :initials="storeName.initials"></avatar>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
<div v-else-if="isNotModerator" class="text-right pr-4 pr-lg-0 is-not-moderator">
<b-row class="mb-3">
<b-col cols="10">
<BRow class="mb-3">
<BCol cols="10">
<div class="font-weight-bold" data-test="username">{{ storeName.username }}</div>
<div class="small" data-test="date">{{ $d(new Date(message.createdAt), 'short') }}</div>
<parse-message v-bind="message" data-test="message"></parse-message>
</b-col>
<b-col cols="2">
</BCol>
<BCol cols="2">
<avatar :username="storeName.username" :initials="storeName.initials"></avatar>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
<div v-else>
<b-row class="mb-3 bg-f5 p-2 is-moderator">
<b-col cols="2">
<BRow class="mb-3 bg-f5 p-2 is-moderator">
<BCol cols="2">
<avatar :username="moderationName.username" :initials="moderationName.initials"></avatar>
</b-col>
<b-col cols="10">
</BCol>
<BCol cols="10">
<div class="font-weight-bold">
<span data-test="username">{{ moderationName.username }}</span>
<span class="ml-2 text-success small" data-test="moderator">
@ -47,8 +47,8 @@
<div class="small" data-test="date">{{ $d(new Date(message.createdAt), 'short') }}</div>
<parse-message v-bind="message" data-test="message"></parse-message>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</div>
</template>

View File

@ -1,14 +1,14 @@
<template>
<div class="mt-1">
<span v-for="({ type, text }, index) in parsedMessage" :key="index">
<b-link v-if="type === 'link'" :href="text" target="_blank">{{ text }}</b-link>
<span v-else-if="type === 'date'">
<span v-for="({ type: messageType, text }, index) in parsedMessage" :key="index">
<b-link v-if="messageType === 'link'" :href="text" target="_blank">{{ text }}</b-link>
<span v-else-if="messageType === 'date'">
{{ $d(new Date(text), 'short') }}
<br />
</span>
<span v-else-if="type === 'amount'">
<span v-else-if="messageType === 'amount'">
<br />
{{ text | GDD }}
{{ $filters.GDD(text) }}
</span>
<span v-else>{{ text }}</span>
</span>
@ -16,7 +16,8 @@
</template>
<script>
const LINK_REGEX_PATTERN = /(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*))/i
const LINK_REGEX_PATTERN =
/(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*))/i
export default {
name: 'ParseMessage',
@ -27,7 +28,7 @@ export default {
},
type: {
type: String,
reuired: true,
required: true,
},
},
computed: {

View File

@ -1,26 +1,29 @@
<template>
<div class="contribution-form">
<b-form
<BForm
ref="form"
class="form-style p-3 bg-white app-box-shadow gradido-border-radius"
@submit.prevent="submit"
class="form-style p-3 bg-white appBoxShadow gradido-border-radius"
>
<label>{{ $t('contribution.selectDate') }}</label>
<b-form-datepicker
<BFormInput
id="contribution-date"
v-model="form.date"
:model-value="date"
:state="dataFieldMeta.valid"
:locale="$i18n.locale"
:max="maximalDate"
:max="getMaximalDate"
:min="minimalDate"
class="mb-4 bg-248"
reset-value=""
:label-no-date-selected="$t('contribution.noDateSelected')"
required
:no-flip="true"
type="date"
@update:model-value="date = $event"
>
<template #nav-prev-year><span></span></template>
<template #nav-next-year><span></span></template>
</b-form-datepicker>
</BFormInput>
<div v-if="showMessage" class="p-3" data-test="contribtion-message">
{{ noOpenCreation }}
@ -28,50 +31,46 @@
<div v-else>
<input-textarea
id="contribution-memo"
v-model="form.memo"
:name="$t('form.message')"
name="memo"
:label="$t('contribution.activity')"
:placeholder="$t('contribution.yourActivity')"
:rules="{ required: true, min: 5, max: 255 }"
/>
<input-hour
v-model="form.hours"
:name="$t('form.hours')"
name="hours"
:label="$t('form.hours')"
placeholder="0.25"
:rules="{
required: true,
min: 0.25,
max: validMaxTime,
gddCreationTime: [0.25, validMaxTime],
gddCreationTime: { min: 0.25, max: validMaxTime },
}"
:validMaxTime="validMaxTime"
@updateAmount="updateAmount"
></input-hour>
:valid-max-time="validMaxTime"
/>
<input-amount
id="contribution-amount"
v-model="form.amount"
:name="$t('form.amount')"
name="amount"
:label="$t('form.amount')"
placeholder="20"
:rules="{ required: true, gddSendAmount: [20, validMaxGDD] }"
:rules="{ required: true, gddSendAmount: { min: 20, max: validMaxGDD } }"
typ="ContributionForm"
></input-amount>
/>
<b-row class="mt-5">
<b-col cols="12" lg="6">
<b-button
<BRow class="mt-5">
<BCol cols="12" lg="6">
<BButton
block
type="reset"
variant="secondary"
@click="reset"
data-test="button-cancel"
@click="fullFormReset"
>
{{ $t('form.cancel') }}
</b-button>
</b-col>
<b-col cols="12" lg="6" class="text-right mt-4 mt-lg-0">
<b-button
</BButton>
</BCol>
<BCol cols="12" lg="6" class="text-right mt-4 mt-lg-0">
<BButton
block
type="submit"
variant="gradido"
@ -79,109 +78,137 @@
data-test="button-submit"
>
{{ form.id ? $t('form.change') : $t('contribution.submit') }}
</b-button>
</b-col>
</b-row>
</BButton>
</BCol>
</BRow>
</div>
</b-form>
</BForm>
</div>
</template>
<script>
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import InputHour from '@/components/Inputs/InputHour'
import InputAmount from '@/components/Inputs/InputAmount'
import InputTextarea from '@/components/Inputs/InputTextarea'
import { useField, useForm } from 'vee-validate'
export default {
name: 'ContributionForm',
components: {
InputHour,
InputAmount,
InputTextarea,
const props = defineProps({
modelValue: { type: Object, required: true },
isThisMonth: { type: Boolean, required: true },
minimalDate: { type: Date, required: true },
maxGddLastMonth: { type: Number, required: true },
maxGddThisMonth: { type: Number, required: true },
})
const emit = defineEmits(['update-contribution', 'set-contribution'])
const { t } = useI18n()
const form = ref({ ...props.modelValue })
const {
values: formValues,
meta: formMeta,
resetForm,
defineField,
setFieldValue,
} = useForm({
initialValues: {
date: props.modelValue.date,
memo: props.modelValue.memo,
hours: props.modelValue.hours,
amount: props.modelValue.amount,
},
props: {
value: { type: Object, required: true },
isThisMonth: { type: Boolean, required: true },
minimalDate: { type: Date, required: true },
maxGddLastMonth: { type: Number, required: true },
maxGddThisMonth: { type: Number, required: true },
})
const [date, dateProps] = defineField('date')
const { meta: dataFieldMeta } = useField('date', 'required')
const showMessage = computed(() => {
if (props.maxGddThisMonth <= 0 && props.maxGddLastMonth <= 0) return true
if (props.modelValue.date)
return (
(props.isThisMonth && props.maxGddThisMonth <= 0) ||
(!props.isThisMonth && props.maxGddLastMonth <= 0)
)
return false
})
const disabled = computed(() => {
return (
!formMeta.value.valid ||
(props.isThisMonth && parseInt(form.value.amount) > parseInt(props.maxGddThisMonth)) ||
(!props.isThisMonth && parseInt(form.value.amount) > parseInt(props.maxGddLastMonth))
)
})
const validMaxGDD = computed(() => {
return Number(props.isThisMonth ? props.maxGddThisMonth : props.maxGddLastMonth)
})
const validMaxTime = computed(() => {
return Number(validMaxGDD.value / 20)
})
const noOpenCreation = computed(() => {
if (props.maxGddThisMonth <= 0 && props.maxGddLastMonth <= 0) {
return t('contribution.noOpenCreation.allMonth')
}
if (props.isThisMonth && props.maxGddThisMonth <= 0) {
return t('contribution.noOpenCreation.thisMonth')
}
if (!props.isThisMonth && props.maxGddLastMonth <= 0) {
return t('contribution.noOpenCreation.lastMonth')
}
return ''
})
const getMaximalDate = computed(() => {
return new Date().toISOString().slice(0, 10)
})
watch(
() => formValues.hours,
() => {
updateAmount(formValues.hours)
},
data() {
return {
minlength: 5,
maxlength: 255,
maximalDate: new Date(),
form: this.value, // includes 'id' and time
}
},
methods: {
updateAmount(hours) {
this.form.amount = (hours * 20).toFixed(2).toString()
)
function updateAmount(hours) {
setFieldValue('amount', (hours * 20).toFixed(2).toString())
}
function submit() {
const dataToSave = { ...formValues }
let emitOption = 'set-contribution'
if (props.modelValue.id) {
dataToSave.id = props.modelValue.id
emitOption = 'update-contribution'
}
emit(emitOption, dataToSave)
fullFormReset()
}
function fullFormReset() {
resetForm({
values: {
id: null,
date: '',
memo: '',
hours: 0,
amount: '',
},
submit() {
this.$emit(this.form.id ? 'update-contribution' : 'set-contribution', { ...this.form })
this.reset()
},
reset() {
this.$refs.form.reset()
this.form.id = null
this.form.date = ''
this.form.memo = ''
this.form.hours = 0
this.form.amount = ''
},
},
computed: {
showMessage() {
if (this.maxGddThisMonth <= 0 && this.maxGddLastMonth <= 0) return true
if (this.form.date)
return (
(this.isThisMonth && this.maxGddThisMonth <= 0) ||
(!this.isThisMonth && this.maxGddLastMonth <= 0)
)
return false
},
disabled() {
return (
this.form.date === '' ||
this.form.memo.length < this.minlength ||
this.form.memo.length > this.maxlength ||
this.form.amount === '' ||
parseInt(this.form.amount) <= 0 ||
parseInt(this.form.amount) > 1000 ||
(this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddThisMonth)) ||
(!this.isThisMonth && parseInt(this.form.amount) > parseInt(this.maxGddLastMonth))
)
},
validMaxGDD() {
return Number(this.isThisMonth ? this.maxGddThisMonth : this.maxGddLastMonth)
},
validMaxTime() {
return Number(this.validMaxGDD / 20)
},
noOpenCreation() {
if (this.maxGddThisMonth <= 0 && this.maxGddLastMonth <= 0) {
return this.$t('contribution.noOpenCreation.allMonth')
}
if (this.isThisMonth && this.maxGddThisMonth <= 0) {
return this.$t('contribution.noOpenCreation.thisMonth')
}
if (!this.isThisMonth && this.maxGddLastMonth <= 0) {
return this.$t('contribution.noOpenCreation.lastMonth')
}
return ''
},
},
watch: {
value() {
return (this.form = this.value)
},
},
})
}
</script>
<style>
.form-style {
min-height: 410px;
}
span.errors {
color: red;
}

View File

@ -31,8 +31,7 @@ describe('ContributionList', () => {
{
id: 2,
date: '05/04/2022',
memo:
'Ich habe 50 Stunden den Nachbarkindern bei ihren Hausaufgaben geholfen und Nachhilfeunterricht gegeben.',
memo: 'Ich habe 50 Stunden den Nachbarkindern bei ihren Hausaufgaben geholfen und Nachhilfeunterricht gegeben.',
amount: '1000',
},
],

View File

@ -1,40 +1,40 @@
<template>
<div class="contribution-list">
<div class="mb-3" v-for="item in items" :key="item.id + 'a'">
<div v-for="item in items" :key="item.id + 'a'" class="mb-3">
<contribution-list-item
v-if="item.status === 'IN_PROGRESS'"
v-bind="item"
@closeAllOpenCollapse="$emit('closeAllOpenCollapse')"
:contributionId="item.id"
:allContribution="allContribution"
:contribution-id="item.id"
:all-contribution="allContribution"
@close-all-open-collapse="$emit('close-all-open-collapse')"
@update-contribution-form="updateContributionForm"
@delete-contribution="deleteContribution"
@update-status="updateStatus"
/>
</div>
<div class="mb-3" v-for="item2 in items" :key="item2.id">
<div v-for="item2 in items" :key="item2.id" class="mb-3">
<contribution-list-item
v-if="item2.status !== 'IN_PROGRESS'"
v-bind="item2"
@closeAllOpenCollapse="$emit('closeAllOpenCollapse')"
:contributionId="item2.id"
:allContribution="allContribution"
:contribution-id="item2.id"
:all-contribution="allContribution"
@close-all-open-collapse="$emit('close-all-open-collapse')"
@update-contribution-form="updateContributionForm"
@delete-contribution="deleteContribution"
@update-status="updateStatus"
/>
</div>
<b-pagination
<BPagination
v-if="isPaginationVisible"
v-model="currentPage"
class="mt-3"
pills
size="lg"
v-model="currentPage"
:per-page="pageSize"
:total-rows="contributionCount"
align="center"
:hide-ellipsis="true"
></b-pagination>
/>
</div>
</template>
<script>
@ -71,6 +71,16 @@ export default {
messages: [],
}
},
computed: {
isPaginationVisible() {
return this.showPagination && this.pageSize < this.contributionCount
},
},
watch: {
currentPage() {
this.updateListContributions()
},
},
methods: {
updateListContributions() {
this.$emit('update-list-contributions', {
@ -89,15 +99,5 @@ export default {
this.$emit('update-status', id)
},
},
computed: {
isPaginationVisible() {
return this.showPagination && this.pageSize < this.contributionCount
},
},
watch: {
currentPage() {
this.updateListContributions()
},
},
}
</script>

View File

@ -1,11 +1,11 @@
<template>
<div>
<div
class="contribution-list-item bg-white appBoxShadow gradido-border-radius pt-3 px-3"
class="contribution-list-item bg-white app-box-shadow gradido-border-radius pt-3 px-3"
:class="status === 'IN_PROGRESS' && !allContribution ? 'pulse border border-205' : ''"
>
<b-row>
<b-col cols="3" lg="2" md="2">
<BRow>
<BCol cols="3" lg="2" md="2">
<avatar
v-if="firstName"
:username="username.username"
@ -13,19 +13,30 @@
color="#fff"
class="font-weight-bold"
></avatar>
<b-avatar v-else :icon="icon" :variant="variant" size="3em"></b-avatar>
</b-col>
<b-col>
<BAvatar v-else rounded="lg" :variant="variant" size="4em">
<IBiTrash v-if="deletedAt" />
<IBiXCircle v-else-if="deniedAt" />
<IBiCheck v-else-if="confirmedAt" />
<IBiQuestion v-else-if="status === 'IN_PROGRESS'" />
<IBiBellFill v-else style="width: 36px; height: 36px" />
</BAvatar>
</BCol>
<BCol>
<div v-if="firstName" class="mr-3 font-weight-bold">
{{ firstName }} {{ lastName }}
<b-icon :icon="icon" :variant="variant"></b-icon>
<IBiTrash v-if="deletedAt" />
<IBiXCircle v-else-if="deniedAt" />
<IBiCheck v-else-if="confirmedAt" />
<IBiQuestion v-else-if="status === 'IN_PROGRESS'" />
<IBiBellFill v-else />
<!-- <b-icon :icon="icon" :variant="variant"></b-icon>-->
</div>
<div class="small">
{{ $d(new Date(contributionDate), 'short') }}
</div>
<div class="mt-3 font-weight-bold">{{ $t('contributionText') }}</div>
<div class="mb-3 text-break word-break">{{ memo }}</div>
<div class="mt-2 mb-2 small" v-if="updatedBy > 0">
<div v-if="updatedBy > 0" class="mt-2 mb-2 small">
{{ $t('moderatorChangedMemo') }}
</div>
<div
@ -35,42 +46,43 @@
>
{{ $t('contribution.alert.answerQuestion') }}
</div>
</b-col>
<b-col cols="9" lg="3" offset="3" offset-md="0" offset-lg="0">
</BCol>
<BCol cols="9" lg="3" offset="3" offset-md="0" offset-lg="0">
<div class="small">
{{ $t('creation') }} {{ $t('(') }}{{ amount / 20 }} {{ $t('h') }}{{ $t(')') }}
</div>
<div v-if="status === 'DENIED' && allContribution" class="font-weight-bold">
<b-icon icon="x-circle" variant="danger"></b-icon>
<!-- <b-icon icon="x-circle" variant="danger"></b-icon>-->
<IBiXCircle />
{{ $t('contribution.alert.denied') }}
</div>
<div v-if="status === 'DELETED'" class="small">
{{ $t('contribution.deleted') }}
</div>
<div v-else class="font-weight-bold">{{ amount | GDD }}</div>
</b-col>
<b-col cols="12" md="1" lg="1" class="text-right align-items-center">
<div v-else class="font-weight-bold">{{ $filters.GDD(amount) }}</div>
</BCol>
<BCol cols="12" md="1" lg="1" class="text-right align-items-center">
<div v-if="messagesCount > 0 && !moderatorId" @click="visible = !visible">
<collapse-icon class="text-right" :visible="visible" />
</div>
</b-col>
</b-row>
<b-row
</BCol>
</BRow>
<BRow
v-if="(!['CONFIRMED', 'DELETED'].includes(status) && !allContribution) || messagesCount > 0"
class="p-2"
>
<b-col cols="3" class="mr-auto text-center">
<BCol cols="3" class="mr-auto text-center">
<div
v-if="!['CONFIRMED', 'DELETED'].includes(status) && !allContribution && !moderatorId"
class="test-delete-contribution pointer mr-3"
@click="deleteContribution({ id })"
>
<b-icon icon="trash"></b-icon>
<IBiTrash />
<div>{{ $t('delete') }}</div>
</div>
</b-col>
<b-col cols="3" class="text-center">
</BCol>
<BCol cols="3" class="text-center">
<div
v-if="!['CONFIRMED', 'DELETED'].includes(status) && !allContribution && !moderatorId"
class="test-edit-contribution pointer mr-3"
@ -83,183 +95,351 @@
})
"
>
<b-icon icon="pencil"></b-icon>
<IBiPencil />
<div>{{ $t('edit') }}</div>
</div>
</b-col>
<b-col cols="6" class="text-center">
</BCol>
<BCol cols="6" class="text-center">
<div v-if="messagesCount > 0 && !moderatorId" class="pointer" @click="visible = !visible">
<b-icon icon="chat-dots"></b-icon>
<IBiChatDots />
<div>{{ $t('moderatorChat') }}</div>
</div>
</b-col>
</b-row>
</BCol>
</BRow>
<div v-else class="pb-3"></div>
<b-collapse :id="collapsId" class="mt-2" v-model="visible">
<BCollapse :id="collapsId" :model-value="visible" class="mt-2">
<contribution-messages-list
:messages="messages_get"
:messages="messagesGet"
:status="status"
:contributionId="contributionId"
:contribution-id="contributionId"
@get-list-contribution-messages="getListContributionMessages"
@update-status="updateStatus"
/>
</b-collapse>
</BCollapse>
</div>
</div>
</template>
<script>
<!--<script>-->
<!--import Avatar from 'vue-avatar'-->
<!--import CollapseIcon from '../TransactionRows/CollapseIcon'-->
<!--import ContributionMessagesList from '@/components/ContributionMessages/ContributionMessagesList'-->
<!--import { listContributionMessages } from '../../graphql/queries.js'-->
<!--export default {-->
<!-- name: 'ContributionListItem',-->
<!-- components: {-->
<!-- Avatar,-->
<!-- CollapseIcon,-->
<!-- ContributionMessagesList,-->
<!-- },-->
<!-- props: {-->
<!-- id: {-->
<!-- type: Number,-->
<!-- },-->
<!-- amount: {-->
<!-- type: String,-->
<!-- },-->
<!-- memo: {-->
<!-- type: String,-->
<!-- },-->
<!-- firstName: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- },-->
<!-- lastName: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- },-->
<!-- createdAt: {-->
<!-- type: String,-->
<!-- },-->
<!-- contributionDate: {-->
<!-- type: String,-->
<!-- },-->
<!-- deletedAt: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- },-->
<!-- confirmedBy: {-->
<!-- type: Number,-->
<!-- required: false,-->
<!-- },-->
<!-- confirmedAt: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- },-->
<!-- deniedBy: {-->
<!-- type: Number,-->
<!-- required: false,-->
<!-- },-->
<!-- deniedAt: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- },-->
<!-- updatedBy: {-->
<!-- type: Number,-->
<!-- required: false,-->
<!-- },-->
<!-- status: {-->
<!-- type: String,-->
<!-- required: false,-->
<!-- default: '',-->
<!-- },-->
<!-- messagesCount: {-->
<!-- type: Number,-->
<!-- required: false,-->
<!-- },-->
<!-- contributionId: {-->
<!-- type: Number,-->
<!-- required: true,-->
<!-- },-->
<!-- allContribution: {-->
<!-- type: Boolean,-->
<!-- required: false,-->
<!-- default: false,-->
<!-- },-->
<!-- moderatorId: {-->
<!-- type: Number,-->
<!-- required: false,-->
<!-- default: 0,-->
<!-- },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- inProcess: true,-->
<!-- messages_get: [],-->
<!-- visible: false,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- icon() {-->
<!-- if (this.deletedAt) return 'trash'-->
<!-- if (this.deniedAt) return 'x-circle'-->
<!-- if (this.confirmedAt) return 'check'-->
<!-- if (this.status === 'IN_PROGRESS') return 'question'-->
<!-- return `<IBiBellFill />`-->
<!-- },-->
<!-- variant() {-->
<!-- if (this.deletedAt) return 'danger'-->
<!-- if (this.deniedAt) return 'warning'-->
<!-- if (this.confirmedAt) return 'success'-->
<!-- if (this.status === 'IN_PROGRESS') return '205'-->
<!-- return 'primary'-->
<!-- },-->
<!-- date() {-->
<!-- return this.createdAt-->
<!-- },-->
<!-- collapsId() {-->
<!-- return 'collapse' + String(this.id)-->
<!-- },-->
<!-- username() {-->
<!-- return {-->
<!-- username: `${this.firstName} ${this.lastName}`,-->
<!-- initials: `${this.firstName[0]}${this.lastName[0]}`,-->
<!-- }-->
<!-- },-->
<!-- },-->
<!-- watch: {-->
<!-- visible() {-->
<!-- if (this.visible) this.getListContributionMessages()-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- deleteContribution(item) {-->
<!-- this.$bvModal.msgBoxConfirm(this.$t('contribution.delete')).then(async (value) => {-->
<!-- if (value) this.$emit('delete-contribution', item)-->
<!-- })-->
<!-- },-->
<!-- getListContributionMessages(closeCollapse = true) {-->
<!-- if (closeCollapse) {-->
<!-- this.$emit('closeAllOpenCollapse')-->
<!-- }-->
<!-- this.$apollo-->
<!-- .query({-->
<!-- query: listContributionMessages,-->
<!-- variables: {-->
<!-- contributionId: this.contributionId,-->
<!-- },-->
<!-- fetchPolicy: 'no-cache',-->
<!-- })-->
<!-- .then((result) => {-->
<!-- this.messages_get = result.data.listContributionMessages.messages-->
<!-- })-->
<!-- .catch((error) => {-->
<!-- this.toastError(error.message)-->
<!-- })-->
<!-- },-->
<!-- updateStatus(id) {-->
<!-- this.$emit('update-status', id)-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch } from 'vue'
import Avatar from 'vue-avatar'
import CollapseIcon from '../TransactionRows/CollapseIcon'
import ContributionMessagesList from '@/components/ContributionMessages/ContributionMessagesList'
import { listContributionMessages } from '../../graphql/queries.js'
import { useAppToast } from '@/composables/useToast'
import { useI18n } from 'vue-i18n'
import { useQuery } from '@vue/apollo-composable'
export default {
name: 'ContributionListItem',
components: {
Avatar,
CollapseIcon,
ContributionMessagesList,
const props = defineProps({
id: {
type: Number,
},
props: {
id: {
type: Number,
},
amount: {
type: String,
},
memo: {
type: String,
},
firstName: {
type: String,
required: false,
},
lastName: {
type: String,
required: false,
},
createdAt: {
type: String,
},
contributionDate: {
type: String,
},
deletedAt: {
type: String,
required: false,
},
confirmedBy: {
type: Number,
required: false,
},
confirmedAt: {
type: String,
required: false,
},
deniedBy: {
type: Number,
required: false,
},
deniedAt: {
type: String,
required: false,
},
updatedBy: {
type: Number,
required: false,
},
status: {
type: String,
required: false,
default: '',
},
messagesCount: {
type: Number,
required: false,
},
contributionId: {
type: Number,
required: true,
},
allContribution: {
type: Boolean,
required: false,
default: false,
},
moderatorId: {
type: Number,
required: false,
default: 0,
},
amount: {
type: String,
},
data() {
return {
inProcess: true,
messages_get: [],
visible: false,
memo: {
type: String,
},
firstName: {
type: String,
required: false,
},
lastName: {
type: String,
required: false,
},
createdAt: {
type: String,
},
contributionDate: {
type: String,
},
deletedAt: {
type: String,
required: false,
},
confirmedBy: {
type: Number,
required: false,
},
confirmedAt: {
type: String,
required: false,
},
deniedBy: {
type: Number,
required: false,
},
deniedAt: {
type: String,
required: false,
},
updatedBy: {
type: Number,
required: false,
},
status: {
type: String,
required: false,
default: '',
},
messagesCount: {
type: Number,
required: false,
},
contributionId: {
type: Number,
required: true,
},
allContribution: {
type: Boolean,
required: false,
default: false,
},
moderatorId: {
type: Number,
required: false,
default: 0,
},
})
const { toastError, toastSuccess } = useAppToast()
const { t } = useI18n()
const inProcess = ref(true)
const messagesGet = ref([])
const visible = ref(false)
const icon = computed(() => {
if (props.deletedAt) return 'trash'
if (props.deniedAt) return 'x-circle'
if (props.confirmedAt) return 'check'
if (props.status === 'IN_PROGRESS') return 'question'
return `<IBiBellFill />`
})
const variant = computed(() => {
if (props.deletedAt) return 'danger'
if (props.deniedAt) return 'warning'
if (props.confirmedAt) return 'success'
if (props.status === 'IN_PROGRESS') return '205'
return 'primary'
})
const date = computed(() => props.createdAt)
const collapsId = computed(() => 'collapse' + String(props.id))
const username = computed(() => ({
username: `${props.firstName} ${props.lastName}`,
initials: `${props.firstName[0]}${props.lastName[0]}`,
}))
watch(
() => visible.value,
() => {
if (visible.value) getListContributionMessages()
},
)
function deleteContribution(item) {
if (window.confirm(t('contribution.delete'))) {
emit('delete-contribution', item)
}
}
const { onResult, result, loading, error, refetch } = useQuery(listContributionMessages, {
contributionId: props.contributionId,
})
function getListContributionMessages(closeCollapse = true) {
if (closeCollapse) {
emit('close-all-open-collapse')
}
refetch({
contributionId: props.contributionId,
}).catch((err) => {
toastError(err.message)
})
}
onResult((resultValue) => {
if (resultValue.data) {
messagesGet.value.length = 0
resultValue.data.listContributionMessages.messages.forEach((message) => {
messagesGet.value.push(message)
})
}
})
watch(
() => visible.value,
() => {
if (visible.value) {
getListContributionMessages()
}
},
computed: {
icon() {
if (this.deletedAt) return 'trash'
if (this.deniedAt) return 'x-circle'
if (this.confirmedAt) return 'check'
if (this.status === 'IN_PROGRESS') return 'question'
return 'bell-fill'
},
variant() {
if (this.deletedAt) return 'danger'
if (this.deniedAt) return 'warning'
if (this.confirmedAt) return 'success'
if (this.status === 'IN_PROGRESS') return '205'
return 'primary'
},
date() {
return this.createdAt
},
collapsId() {
return 'collapse' + String(this.id)
},
username() {
return {
username: `${this.firstName} ${this.lastName}`,
initials: `${this.firstName[0]}${this.lastName[0]}`,
}
},
},
methods: {
deleteContribution(item) {
this.$bvModal.msgBoxConfirm(this.$t('contribution.delete')).then(async (value) => {
if (value) this.$emit('delete-contribution', item)
})
},
getListContributionMessages(closeCollapse = true) {
if (closeCollapse) {
this.$emit('closeAllOpenCollapse')
}
this.$apollo
.query({
query: listContributionMessages,
variables: {
contributionId: this.contributionId,
},
fetchPolicy: 'no-cache',
})
.then((result) => {
this.messages_get = result.data.listContributionMessages.messages
})
.catch((error) => {
this.toastError(error.message)
})
},
updateStatus(id) {
this.$emit('update-status', id)
},
},
watch: {
visible() {
if (this.visible) this.getListContributionMessages()
},
},
)
function updateStatus(id) {
emit('update-status', id)
}
const emit = defineEmits(['delete-contribution', 'close-all-open-collapse', 'update-status'])
</script>

View File

@ -37,7 +37,7 @@ describe('OpenCreationsAmount', () => {
})
it('renders the component', () => {
expect(wrapper.find('div.appBoxShadow').exists()).toBe(true)
expect(wrapper.find('div.app-box-shadow').exists()).toBe(true)
})
it('renders two dates', () => {

View File

@ -1,34 +1,34 @@
<template>
<div class="bg-white appBoxShadow gradido-border-radius p-3">
<div class="bg-white app-box-shadow gradido-border-radius p-3">
<div class="pl-3">
<b-row class="small">
<b-col>{{ $t('time.months') }}</b-col>
<b-col class="d-none d-md-inline">{{ $t('status') }}</b-col>
<b-col class="d-none d-md-inline text-center">{{ $t('submitted') }}</b-col>
<b-col class="text-center">{{ $t('openHours') }}</b-col>
</b-row>
<BRow class="small">
<BCol>{{ $t('time.months') }}</BCol>
<BCol class="d-none d-md-inline">{{ $t('status') }}</BCol>
<BCol class="d-none d-md-inline text-center">{{ $t('submitted') }}</BCol>
<BCol class="text-center">{{ $t('openHours') }}</BCol>
</BRow>
<b-row class="font-weight-bold pt-3">
<b-col>{{ $d(new Date(minimalDate), 'monthAndYear') }}</b-col>
<b-col class="d-none d-md-inline">
<BRow class="font-weight-bold pt-3">
<BCol>{{ $d(new Date(minimalDate), 'monthAndYear') }}</BCol>
<BCol class="d-none d-md-inline">
{{ maxGddLastMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
</b-col>
<b-col class="d-none d-md-inline text-197 text-center">
</BCol>
<BCol class="d-none d-md-inline text-197 text-center">
{{ hoursSubmittedLastMonth }} {{ $t('h') }}
</b-col>
<b-col class="text-4 text-center">{{ hoursAvailableLastMonth }} {{ $t('h') }}</b-col>
</b-row>
</BCol>
<BCol class="text-4 text-center">{{ hoursAvailableLastMonth }} {{ $t('h') }}</BCol>
</BRow>
<b-row class="font-weight-bold">
<b-col>{{ $d(new Date(), 'monthAndYear') }}</b-col>
<b-col class="d-none d-md-inline">
<BRow class="font-weight-bold">
<BCol>{{ $d(new Date(), 'monthAndYear') }}</BCol>
<BCol class="d-none d-md-inline">
{{ maxGddThisMonth > 0 ? $t('contribution.submit') : $t('maxReached') }}
</b-col>
<b-col class="d-none d-md-inline text-197 text-center">
</BCol>
<BCol class="d-none d-md-inline text-197 text-center">
{{ hoursSubmittedThisMonth }} {{ $t('h') }}
</b-col>
<b-col class="text-4 text-center">{{ hoursAvailableThisMonth }} {{ $t('h') }}</b-col>
</b-row>
</BCol>
<BCol class="text-4 text-center">{{ hoursAvailableThisMonth }} {{ $t('h') }}</BCol>
</BRow>
</div>
</div>
</template>

View File

@ -11,17 +11,18 @@
@reset-transaction-link-list="resetTransactionLinkList"
/>
<div class="mb-3">
<b-button
class="test-button-load-more"
<BButton
v-if="!pending && transactionLinks.length < transactionLinkCount"
class="test-button-load-more"
block
variant="outline-primary"
@click="loadMoreLinks"
>
{{ buttonText }}
</b-button>
</BButton>
<div class="text-center">
<b-icon v-if="pending" icon="three-dots" animation="cylon" font-scale="4"></b-icon>
<!-- <b-icon v-if="pending" icon="three-dots" animation="cylon" font-scale="4"></b-icon>-->
<IBiThreeDots v-if="pending" animation="cylon" font-scale="4" />
</div>
</div>
</div>
@ -46,6 +47,14 @@ export default {
pageSize: { type: Number, default: 5 },
pending: { type: Boolean, default: false },
},
computed: {
buttonText() {
const i = this.transactionLinkCount - this.transactionLinks.length
if (i === 1) return this.$t('link-load', 0)
if (i <= this.pageSize) return this.$t('link-load', 1, { n: i })
return this.$t('link-load', 2, { n: this.pageSize })
},
},
methods: {
resetTransactionLinkList() {
this.$emit('input', 0)
@ -54,13 +63,5 @@ export default {
this.$emit('input', this.value + 1)
},
},
computed: {
buttonText() {
const i = this.transactionLinkCount - this.transactionLinks.length
if (i === 1) return this.$tc('link-load', 0)
if (i <= this.pageSize) return this.$tc('link-load', 1, { n: i })
return this.$tc('link-load', 2, { n: this.pageSize })
},
},
}
</script>

View File

@ -11,7 +11,7 @@
</template>
<script>
export default {
name: 'DecayInformation-StartBlock',
name: 'DecayInformationStartBlock',
props: {
memo: {
type: String,

View File

@ -1,31 +1,32 @@
<template>
<div class="decayinformation-decay">
<div class="mb-3">
<b-icon icon="droplet-half" class="mr-2" />
<!-- <b-icon icon="droplet-half" class="mr-2" />-->
<IBiDropletHalf class="mr-2" />
<b>{{ $t('decay.calculation_decay') }}</b>
</div>
<b-row>
<b-col>
<b-row>
<b-col cols="12" lg="4" md="4">
<BRow>
<BCol>
<BRow>
<BCol cols="12" lg="4" md="4">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col offset="1" offset-md="0" offset-lg="0">
</BCol>
<BCol offset="1" offset-md="0" offset-lg="0">
<div>
{{ previousBalance | GDD }}
{{ $filters.GDD(previousBalance) }}
{{ decay === '0' ? $t('math.minus') : '' }}
{{ decay | GDD }} {{ $t('math.equal') }}
<b>{{ balance | GDD }}</b>
{{ $filters.GDD(decay) }} {{ $t('math.equal') }}
<b>{{ $filters.GDD(balance) }}</b>
</div>
</b-col>
</b-row>
</b-col>
</b-row>
</BCol>
</BRow>
</BCol>
</BRow>
</div>
</template>
<script>
export default {
name: 'DecayInformation-Decay',
name: 'DecayInformationDecay',
props: {
balance: {
type: String,

View File

@ -1,75 +1,75 @@
<template>
<div class="decayinformation-startblock">
<b-row>
<b-col cols="12">
<BRow>
<BCol cols="12">
<div class="text-center pb-3">
<b-icon icon="droplet-half" class="mr-2" />
<b>{{ $t('decay.Starting_block_decay') }}</b>
</div>
</b-col>
</b-row>
<b-row>
<b-col offset="1" cols="11">
<b-row>
<b-col cols="5">
</BCol>
</BRow>
<BRow>
<BCol offset="1" cols="11">
<BRow>
<BCol cols="5">
<div class="text-right">
<div>{{ $t('decay.decay_introduced') }}</div>
</div>
</b-col>
<b-col cols="5">
</BCol>
<BCol cols="5">
<div>
<span v-if="decay.start">
{{ $d(new Date(decay.start), 'long') }}
</span>
</div>
</b-col>
</b-row>
<duration-row :decayStart="decay.start" :decayEnd="decay.end" />
</BCol>
</BRow>
<duration-row :decay-start="decay.start" :decay-end="decay.end" />
<!-- Decay-->
<b-row>
<b-col cols="5" class="text-right">{{ $t('decay.decay') }}</b-col>
<b-col cols="7">{{ decay.decay | GDD }}</b-col>
</b-row>
</b-col>
</b-row>
<BRow>
<BCol cols="5" class="text-right">{{ $t('decay.decay') }}</BCol>
<BCol cols="7">{{ $filters.GDD(decay.decay) }}</BCol>
</BRow>
</BCol>
</BRow>
<hr class="mt-3 mb-3" />
<b-row>
<b-col class="text-center pb-3">
<BRow>
<BCol class="text-center pb-3">
<b>{{ $t('decay.calculation_total') }}</b>
</b-col>
</b-row>
</BCol>
</BRow>
<!-- Type-->
<b-row>
<b-col offset="1" cols="11">
<b-row>
<BRow>
<BCol offset="1" cols="11">
<BRow>
<!-- eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys-->
<b-col cols="5" class="text-right">{{ $t(`decay.types.${typeId.toLowerCase()}`) }}</b-col>
<b-col cols="7">{{ amount | GDD }}</b-col>
</b-row>
<BCol cols="5" class="text-right">{{ $t(`decay.types.${typeId.toLowerCase()}`) }}</BCol>
<BCol cols="7">{{ $filters.GDD(amount) }}</BCol>
</BRow>
<!-- Decay-->
<b-row>
<b-col cols="5" class="text-right">{{ $t('decay.decay') }}</b-col>
<b-col cols="7">{{ decay.decay | GDD }}</b-col>
</b-row>
<BRow>
<BCol cols="5" class="text-right">{{ $t('decay.decay') }}</BCol>
<BCol cols="7">{{ $filters.GDD(decay.decay) }}</BCol>
</BRow>
<!-- Total-->
<b-row>
<b-col cols="5" class="text-right">{{ $t('decay.total') }}</b-col>
<b-col cols="7">
<b>{{ (Number(amount) + Number(decay.decay)) | GDD }}</b>
</b-col>
</b-row>
</b-col>
</b-row>
<BRow>
<BCol cols="5" class="text-right">{{ $t('decay.total') }}</BCol>
<BCol cols="7">
<b>{{ $filters.GDD(Number(amount) + Number(decay.decay)) }}</b>
</BCol>
</BRow>
</BCol>
</BRow>
</div>
</template>
<script>
import DurationRow from '@/components/TransactionRows/DurationRow'
export default {
name: 'DecayInformation-StartBlock',
name: 'DecayInformationStartBlock',
components: {
DurationRow,
},

View File

@ -5,77 +5,77 @@
<div class="">{{ memo }}</div>
</div>
<div class="mb-3">
<b-icon icon="droplet-half" class="mr-2" />
<IBiDropletHalf class="mr-2" />
<b>{{ $t('decay.calculation_decay') }}</b>
</div>
<b-row>
<b-col>
<b-row>
<b-col cols="6" lg="4" md="6" sm="6">
<BRow>
<BCol>
<BRow>
<BCol cols="6" lg="4" md="6" sm="6">
<div>{{ $t('decay.last_transaction') }}</div>
</b-col>
<b-col offset="0" class="text-right mr-0">
</BCol>
<BCol offset="0" class="text-right mr-0">
<div>
<span>
{{ $d(new Date(decay.start), 'long') }}
</span>
</div>
</b-col>
</b-row>
<duration-row :decayStart="decay.start" :decayEnd="decay.end" />
</BCol>
</BRow>
<duration-row :decay-start="decay.start" :decay-end="decay.end" />
<!-- Previous Balance -->
<b-row class="mt-2">
<b-col cols="6" lg="4" md="6" sm="6">
<BRow class="mt-2">
<BCol cols="6" lg="4" md="6" sm="6">
<div>{{ $t('decay.old_balance') }}</div>
</b-col>
<b-col offset="0" class="text-right mr-0">
{{ previousBalance | GDD }}
</b-col>
</b-row>
</BCol>
<BCol offset="0" class="text-right mr-0">
{{ $filters.GDD(previousBalance) }}
</BCol>
</BRow>
<!-- Decay-->
<b-row class="mt-0">
<b-col cols="6" lg="3" md="6" sm="6">
<BRow class="mt-0">
<BCol cols="6" lg="3" md="6" sm="6">
<div>{{ $t('decay.decay') }}</div>
</b-col>
<b-col offset="0" class="text-right mr-0">
{{ decay.decay | GDD }}
</b-col>
</b-row>
</b-col>
</b-row>
</BCol>
<BCol offset="0" class="text-right mr-0">
{{ $filters.GDD(decay.decay) }}
</BCol>
</BRow>
</BCol>
</BRow>
<!-- Type-->
<b-row>
<b-col>
<b-row class="mb-2">
<BRow>
<BCol>
<BRow class="mb-2">
<!-- eslint-disable @intlify/vue-i18n/no-dynamic-keys-->
<b-col cols="6" lg="3" md="6" sm="6">
<BCol cols="6" lg="3" md="6" sm="6">
{{ $t(`decay.types.${typeId.toLowerCase()}`) }}
</b-col>
</BCol>
<!-- eslint-enable @intlify/vue-i18n/no-dynamic-keys-->
<b-col offset="0" class="text-right mr-0">
{{ amount | GDD }}
</b-col>
</b-row>
<BCol offset="0" class="text-right mr-0">
{{ $filters.GDD(amount) }}
</BCol>
</BRow>
<!-- Total-->
<b-row class="border-top pt-2">
<b-col cols="6" lg="3" md="6" sm="6">
<BRow class="border-top pt-2">
<BCol cols="6" lg="3" md="6" sm="6">
<div>{{ $t('decay.new_balance') }}</div>
</b-col>
<b-col offset="0" class="text-right mr-0">
<b>{{ balance | GDD }}</b>
</b-col>
</b-row>
</b-col>
</b-row>
</BCol>
<BCol offset="0" class="text-right mr-0">
<b>{{ $filters.GDD(balance) }}</b>
</BCol>
</BRow>
</BCol>
</BRow>
</div>
</template>
<script>
import DurationRow from '@/components/TransactionRows/DurationRow'
export default {
name: 'DecayInformation-Long',
name: 'DecayInformationLong',
components: {
DurationRow,
},

View File

@ -1,11 +1,11 @@
<template>
<div class="decayinformation-short">
<span v-if="decay">{{ decay | GDD }}</span>
<span v-if="decay">{{ $filters.GDD(decay) }}</span>
</div>
</template>
<script>
export default {
name: 'DecayInformation-Short',
name: 'DecayInformationShort',
props: {
decay: {
type: String,

View File

@ -5,16 +5,16 @@
v-else-if="isStartBlock"
:amount="amount"
:decay="decay"
:typeId="typeId"
:type-id="typeId"
/>
<decay-information-long
v-else
:amount="amount"
:decay="decay"
:typeId="typeId"
:type-id="typeId"
:memo="memo"
:balance="balance"
:previousBalance="previousBalance"
:previous-balance="previousBalance"
/>
</div>
</template>

View File

@ -1,48 +1,48 @@
<template>
<div class="transaction-confirm-link">
<div class="bg-white appBoxShadow gradido-border-radius p-3">
<div class="bg-white app-box-shadow gradido-border-radius p-3">
<div class="h3 mb-4">{{ $t('gdd_per_link.header') }}</div>
<b-row class="mt-5">
<b-col offset="2">
<BRow class="mt-5">
<BCol offset="2">
<div class="mt-3 h5">{{ $t('form.memo') }}</div>
<div>{{ memo }}</div>
</b-col>
<b-col cols="3">
</BCol>
<BCol cols="3">
<div class="small">{{ $t('send_gdd') }}</div>
<div>{{ amount | GDD }}</div>
</b-col>
</b-row>
<div>{{ $filters.GDD(amount) }}</div>
</BCol>
</BRow>
<b-row class="mt-5 pr-3 text-color-gdd-yellow h3">
<b-col cols="2" class="text-right">
<BRow class="mt-5 pr-3 text-color-gdd-yellow h3">
<BCol cols="2" class="text-right">
<b-icon class="text-color-gdd-yellow" icon="droplet-half"></b-icon>
</b-col>
<b-col>{{ $t('advanced-calculation') }}</b-col>
</b-row>
<b-row class="pr-3" offset="2">
<b-col offset="2">{{ $t('form.current_balance') }}</b-col>
<b-col>{{ balance | GDD }}</b-col>
</b-row>
<b-row class="pr-3">
<b-col offset="2">
</BCol>
<BCol>{{ $t('advanced-calculation') }}</BCol>
</BRow>
<BRow class="pr-3" offset="2">
<BCol offset="2">{{ $t('form.current_balance') }}</BCol>
<BCol>{{ $filters.GDD(balance) }}</BCol>
</BRow>
<BRow class="pr-3">
<BCol offset="2">
<strong>{{ $t('form.your_amount') }}</strong>
</b-col>
<b-col class="borderbottom">
<strong>{{ (amount * -1) | GDD }}</strong>
</b-col>
</b-row>
<b-row class="pr-3">
<b-col offset="2">{{ $t('form.new_balance') }}</b-col>
<b-col>{{ (balance - amount) | GDD }}</b-col>
</b-row>
<b-row class="mt-5">
<b-col cols="12" md="6" lg="6">
<b-button block @click="$emit('on-back')" class="mb-3 mb-md-0 mb-lg-0">
</BCol>
<BCol class="borderbottom">
<strong>{{ $filters.GDD(amount * -1) }}</strong>
</BCol>
</BRow>
<BRow class="pr-3">
<BCol offset="2">{{ $t('form.new_balance') }}</BCol>
<BCol>{{ $filters.GDD(balance - amount) }}</BCol>
</BRow>
<BRow class="mt-5">
<BCol cols="12" md="6" lg="6">
<BButton block class="mb-3 mb-md-0 mb-lg-0" @click="$emit('on-back')">
{{ $t('back') }}
</b-button>
</b-col>
<b-col cols="12" md="6" lg="6" class="text-lg-right">
<b-button
</BButton>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-lg-right">
<BButton
block
class="send-button"
variant="gradido"
@ -50,9 +50,9 @@
@click="$emit('send-transaction')"
>
{{ $t('form.generate_now') }}
</b-button>
</b-col>
</b-row>
</BButton>
</BCol>
</BRow>
</div>
</div>
</template>
@ -83,8 +83,9 @@ export default {
.gray-background {
background-color: #ecebe6a3 !important;
}
.borderbottom {
border-bottom: 1px solid rgb(70, 65, 65);
border-bottom: 1px solid rgb(70 65 65);
border-bottom-style: double;
}
</style>

View File

@ -1,72 +1,73 @@
<template>
<div class="transaction-confirm-send">
<div class="bg-white appBoxShadow gradido-border-radius p-3">
<div class="bg-white app-box-shadow gradido-border-radius p-3">
<div class="h3 mb-4">{{ $t('form.send_check') }}</div>
<b-row class="mt-5">
<b-col cols="12">
<b-row class="mt-3">
<b-col class="h5">{{ $t('form.recipientCommunity') }}</b-col>
<b-col>{{ targetCommunity.name }}</b-col>
</b-row>
<b-row>
<b-col class="h5">{{ $t('form.recipient') }}</b-col>
<b-col>{{ userName ? userName : identifier }}</b-col>
</b-row>
<b-row>
<b-col class="h5">{{ $t('form.amount') }}</b-col>
<b-col>{{ amount | GDD }}</b-col>
</b-row>
<b-row>
<b-col class="h5">{{ $t('form.memo') }}</b-col>
<b-col>{{ memo }}</b-col>
</b-row>
</b-col>
</b-row>
<BRow class="mt-5">
<BCol cols="12">
<BRow class="mt-3">
<BCol class="h5">{{ $t('form.recipientCommunity') }}</BCol>
<BCol>{{ targetCommunity.name }}</BCol>
</BRow>
<BRow>
<BCol class="h5">{{ $t('form.recipient') }}</BCol>
<BCol>{{ userName ? userName : identifier }}</BCol>
</BRow>
<BRow>
<BCol class="h5">{{ $t('form.amount') }}</BCol>
<BCol>{{ $filters.GDD(amount) }}</BCol>
</BRow>
<BRow>
<BCol class="h5">{{ $t('form.memo') }}</BCol>
<BCol>{{ memo }}</BCol>
</BRow>
</BCol>
</BRow>
<b-row class="mt-5 text-color-gdd-yellow h3">
<b-col cols="2" class="text-right">
<b-icon class="text-color-gdd-yellow" icon="droplet-half"></b-icon>
</b-col>
<b-col>{{ $t('advanced-calculation') }}</b-col>
</b-row>
<b-row class="pr-3" offset="2">
<b-col offset="2">{{ $t('form.current_balance') }}</b-col>
<b-col>{{ balance | GDD }}</b-col>
</b-row>
<b-row class="pr-3">
<b-col offset="2">
<BRow class="mt-5 text-color-gdd-yellow h3">
<BCol cols="2" class="text-right">
<!-- <b-icon class="text-color-gdd-yellow" icon="droplet-half"></b-icon>-->
<IBiDropletHalf />
</BCol>
<BCol>{{ $t('advanced-calculation') }}</BCol>
</BRow>
<BRow class="pr-3" offset="2">
<BCol offset="2">{{ $t('form.current_balance') }}</BCol>
<BCol>{{ $filters.GDD(balance) }}</BCol>
</BRow>
<BRow class="pr-3">
<BCol offset="2">
<strong>{{ $t('form.your_amount') }}</strong>
</b-col>
<b-col class="borderbottom">
<strong>{{ (amount * -1) | GDD }}</strong>
</b-col>
</b-row>
<b-row class="pr-3">
<b-col offset="2">{{ $t('form.new_balance') }}</b-col>
<b-col>{{ (balance - amount) | GDD }}</b-col>
</b-row>
<b-row class="mt-5">
<b-col cols="12" md="6" lg="6">
<b-button block @click="$emit('on-back')" class="mb-3 mb-md-0 mb-lg-0">
</BCol>
<BCol class="borderbottom">
<strong>{{ $filters.GDD(amount * -1) }}</strong>
</BCol>
</BRow>
<BRow class="pr-3">
<BCol offset="2">{{ $t('form.new_balance') }}</BCol>
<BCol>{{ $filters.GDD(balance - amount) }}</BCol>
</BRow>
<BRow class="mt-5">
<BCol cols="12" md="6" lg="6">
<BButton block class="mb-3 mb-md-0 mb-lg-0" @click="$emit('on-back')">
{{ $t('back') }}
</b-button>
</b-col>
<b-col cols="12" md="6" lg="6" class="text-lg-right">
<b-button
</BButton>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-lg-right">
<BButton
block
variant="gradido"
:disabled="disabled"
@click="$emit('send-transaction'), (disabled = true)"
>
{{ $t('form.send_now') }}
</b-button>
</b-col>
</b-row>
</BButton>
</BCol>
</BRow>
</div>
</div>
</template>
<script>
import { COMMUNITY_NAME } from '@/config'
import CONFIG from '@/config'
export default {
name: 'TransactionConfirmationSend',
@ -79,7 +80,7 @@ export default {
targetCommunity: {
type: Object,
default: function () {
return { uuid: '', name: COMMUNITY_NAME }
return { uuid: '', name: CONFIG.COMMUNITY_NAME }
},
},
},
@ -94,8 +95,9 @@ export default {
.gray-background {
background-color: #ecebe6a3 !important;
}
.borderbottom {
border-bottom: 1px solid rgb(70, 65, 65);
border-bottom: 1px solid rgb(70 65 65);
border-bottom-style: double;
}
</style>

View File

@ -1,7 +1,7 @@
import { mount } from '@vue/test-utils'
import TransactionForm from './TransactionForm'
import flushPromises from 'flush-promises'
import { SEND_TYPES } from '@/pages/Send'
import { SEND_TYPES } from '@/utils/sendTypes'
import { createMockClient } from 'mock-apollo-client'
import VueApollo from 'vue-apollo'
import { user, selectCommunities as selectCommunitiesQuery } from '@/graphql/queries'

View File

@ -1,276 +1,379 @@
<template>
<div class="transaction-form">
<b-row>
<b-col cols="12">
<b-card class="appBoxShadow gradido-border-radius" body-class="p-4">
<validation-observer v-slot="{ handleSubmit }" ref="formValidator">
<b-form role="form" @submit.prevent="handleSubmit(onSubmit)" @reset="onReset">
<b-form-radio-group v-model="radioSelected">
<b-row class="mb-4">
<b-col cols="12" lg="6">
<b-row class="bg-248 gradido-border-radius pt-lg-2 mr-lg-2">
<b-col cols="10" @click="radioSelected = sendTypes.send" class="pointer">
{{ $t('send_gdd') }}
</b-col>
<b-col cols="2">
<b-form-radio
name="shipping"
size="lg"
:value="sendTypes.send"
stacked
class="custom-radio-button pointer"
></b-form-radio>
</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>
</b-form-radio-group>
<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>
<BRow>
<BCol cols="12">
<BCard class="app-box-shadow gradido-border-radius" body-class="p-4">
<BForm role="form" @submit.prevent="onSubmit" @reset="onReset">
<BFormRadioGroup
name="shipping"
:model-value="radioSelected"
@update:model-value="radioSelected = $event"
>
<BRow class="mb-4">
<BCol cols="12" lg="6">
<BRow class="bg-248 gradido-border-radius pt-lg-2 mr-lg-2">
<BFormRadio
name="shipping"
size="sm"
reverse
:value="SEND_TYPES.send"
class="custom-radio-button pointer"
>
{{ $t('send_gdd') }}
</BFormRadio>
</BRow>
</BCol>
<BCol>
<BRow class="bg-248 gradido-border-radius pt-lg-2 ml-lg-2 mt-2 mt-lg-0">
<BFormRadio
name="shipping"
:value="SEND_TYPES.link"
size="sm"
reverse
class="custom-radio-button"
>
{{ $t('send_per_link') }}
</BFormRadio>
</BRow>
</BCol>
</BRow>
</BFormRadioGroup>
<div v-if="radioSelected === SEND_TYPES.link" class="mt-4 mb-4">
<h2 class="alert-heading">{{ $t('gdd_per_link.header') }}</h2>
<div>
{{ $t('gdd_per_link.choose-amount') }}
</div>
<b-row>
<b-col>
<b-row>
<b-col class="mb-4" cols="12" v-if="radioSelected === sendTypes.send">
<b-row>
<b-col>{{ $t('form.recipientCommunity') }}</b-col>
</b-row>
<b-row>
<b-col class="font-weight-bold">
<community-switch
v-model="form.targetCommunity"
:disabled="isBalanceDisabled"
/>
</b-col>
</b-row>
</b-col>
<b-col cols="12" v-if="radioSelected === sendTypes.send">
<div v-if="!userIdentifier">
<input-identifier
:name="$t('form.recipient')"
:label="$t('form.recipient')"
:placeholder="$t('form.identifier')"
v-model="form.identifier"
</div>
<BRow>
<BCol>
<BRow>
<BCol v-if="radioSelected === SEND_TYPES.send" class="mb-4" cols="12">
<BRow>
<BCol>{{ $t('form.recipientCommunity') }}</BCol>
</BRow>
<BRow>
<BCol class="font-weight-bold">
<community-switch
:disabled="isBalanceDisabled"
@onValidation="onValidation"
:model-value="targetCommunity"
@update:model-value="targetCommunity = $event"
/>
</div>
<div v-else class="mb-4">
<b-row>
<b-col>{{ $t('form.recipient') }}</b-col>
</b-row>
<b-row>
<b-col class="font-weight-bold">{{ userName }}</b-col>
</b-row>
</div>
</b-col>
<b-col cols="12" lg="6">
<input-amount
v-model="form.amount"
:name="$t('form.amount')"
:label="$t('form.amount')"
:placeholder="'0.01'"
:rules="{ required: true, gddSendAmount: [0.01, balance] }"
typ="TransactionForm"
</BCol>
</BRow>
</BCol>
<BCol v-if="radioSelected === SEND_TYPES.send" cols="12">
<div v-if="!userIdentifier">
<input-identifier
name="identifier"
:label="$t('form.recipient')"
:placeholder="$t('form.identifier')"
:disabled="isBalanceDisabled"
></input-amount>
</b-col>
</b-row>
</b-col>
</b-row>
/>
</div>
<div v-else class="mb-4">
<BRow>
<BCol>{{ $t('form.recipient') }}</BCol>
</BRow>
<BRow>
<BCol class="font-weight-bold">{{ userName }}</BCol>
</BRow>
</div>
</BCol>
<BCol cols="12" lg="6">
<input-amount
name="amount"
:label="$t('form.amount')"
:placeholder="'0.01'"
:rules="{ required: true, gddSendAmount: { min: 0.01, max: balance } }"
:disabled="isBalanceDisabled"
></input-amount>
</BCol>
</BRow>
</BCol>
</BRow>
<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-3">
<b-col cols="12" md="6" lg="6">
<b-button
block
type="reset"
variant="secondary"
@click="onReset"
class="mb-3 mb-md-0 mb-lg-0"
>
{{ $t('form.reset') }}
</b-button>
</b-col>
<b-col cols="12" md="6" lg="6" class="text-lg-right">
<b-button block type="submit" variant="gradido">
{{ $t('form.check_now') }}
</b-button>
</b-col>
</b-row>
</b-form>
</validation-observer>
</b-card>
</b-col>
</b-row>
<BRow>
<BCol>
<input-textarea
name="memo"
:label="$t('form.message')"
:placeholder="$t('form.message')"
:rules="{ required: true, min: 5, max: 255 }"
:disabled="isBalanceDisabled"
/>
</BCol>
</BRow>
<div v-if="!!isBalanceDisabled" class="text-danger mt-5">
{{ $t('form.no_gdd_available') }}
</div>
<BRow v-else class="test-buttons mt-3">
<BCol cols="12" md="6" lg="6">
<BButton
block
type="reset"
variant="secondary"
class="mb-3 mb-md-0 mb-lg-0"
@click="onReset"
>
{{ $t('form.reset') }}
</BButton>
</BCol>
<BCol cols="12" md="6" lg="6" class="text-lg-right">
<BButton block type="submit" variant="gradido">
{{ $t('form.check_now') }}
</BButton>
</BCol>
</BRow>
</BForm>
</BCard>
</BCol>
</BRow>
</div>
</template>
<script>
import { SEND_TYPES } from '@/pages/Send'
<!--<script>-->
<!--import { SEND_TYPES } from '@/utils/sendTypes'-->
<!--import InputIdentifier from '@/components/Inputs/InputIdentifier'-->
<!--import InputAmount from '@/components/Inputs/InputAmount'-->
<!--import InputTextarea from '@/components/Inputs/InputTextarea'-->
<!--import CommunitySwitch from '@/components/CommunitySwitch.vue'-->
<!--import { user } from '@/graphql/queries'-->
<!--import CONFIG from '@/config'-->
<!--export default {-->
<!-- name: 'TransactionForm',-->
<!-- components: {-->
<!-- InputIdentifier,-->
<!-- InputAmount,-->
<!-- InputTextarea,-->
<!-- CommunitySwitch,-->
<!-- },-->
<!-- props: {-->
<!-- balance: { type: Number, default: 0 },-->
<!-- identifier: { type: String, default: '' },-->
<!-- amount: { type: Number, default: 0 },-->
<!-- memo: { type: String, default: '' },-->
<!-- selected: { type: String, default: 'send' },-->
<!-- targetCommunity: {-->
<!-- type: Object,-->
<!-- default: function () {-->
<!-- return { uuid: '', name: CONFIG.COMMUNITY_NAME }-->
<!-- },-->
<!-- },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- form: {-->
<!-- identifier: this.identifier,-->
<!-- amount: this.amount ? String(this.amount) : '',-->
<!-- memo: this.memo,-->
<!-- targetCommunity: this.targetCommunity,-->
<!-- },-->
<!-- radioSelected: this.selected,-->
<!-- userName: '',-->
<!-- recipientCommunity: { uuid: '', name: '' },-->
<!-- }-->
<!-- },-->
<!-- methods: {-->
<!-- onValidation() {-->
<!-- this.$refs.formValidator.validate()-->
<!-- },-->
<!-- onSubmit() {-->
<!-- if (this.userIdentifier) this.form.identifier = this.userIdentifier.identifier-->
<!-- this.$emit('set-transaction', {-->
<!-- selected: this.radioSelected,-->
<!-- identifier: this.form.identifier,-->
<!-- amount: Number(this.form.amount.replace(',', '.')),-->
<!-- memo: this.form.memo,-->
<!-- userName: this.userName,-->
<!-- targetCommunity: this.form.targetCommunity,-->
<!-- })-->
<!-- },-->
<!-- onReset(event) {-->
<!-- event.preventDefault()-->
<!-- this.form.identifier = ''-->
<!-- this.form.amount = ''-->
<!-- this.form.memo = ''-->
<!-- this.form.targetCommunity = { uuid: '', name: COMMUNITY_NAME }-->
<!-- this.$refs.formValidator.validate()-->
<!-- this.$router.replace('/send')-->
<!-- },-->
<!-- },-->
<!-- apollo: {-->
<!-- UserName: {-->
<!-- query() {-->
<!-- return user-->
<!-- },-->
<!-- fetchPolicy: 'network-only',-->
<!-- variables() {-->
<!-- return this.userIdentifier-->
<!-- },-->
<!-- skip() {-->
<!-- return !this.userIdentifier-->
<!-- },-->
<!-- update({ user }) {-->
<!-- this.userName = `${user.firstName} ${user.lastName}`-->
<!-- },-->
<!-- error({ message }) {-->
<!-- this.toastError(message)-->
<!-- },-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- disabled() {-->
<!-- if (-->
<!-- this.form.identifier.length > 5 &&-->
<!-- parseInt(this.form.amount) <= parseInt(this.balance) &&-->
<!-- this.form.memo.length > 5 &&-->
<!-- this.form.memo.length <= 255-->
<!-- ) {-->
<!-- return false-->
<!-- }-->
<!-- return true-->
<!-- },-->
<!-- isBalanceDisabled() {-->
<!-- return this.balance <= 0 ? 'disabled' : false-->
<!-- },-->
<!-- sendTypes() {-->
<!-- return SEND_TYPES-->
<!-- },-->
<!-- userIdentifier() {-->
<!-- if (-->
<!-- this.$route.params &&-->
<!-- this.$route.params.userIdentifier &&-->
<!-- this.$route.params.communityIdentifier-->
<!-- ) {-->
<!-- return {-->
<!-- identifier: this.$route.params.userIdentifier,-->
<!-- communityIdentifier: this.$route.params.communityIdentifier,-->
<!-- }-->
<!-- }-->
<!-- return null-->
<!-- },-->
<!-- },-->
<!-- mounted() {-->
<!-- if (this.form.identifier !== '') this.$refs.formValidator.validate()-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useQuery } from '@vue/apollo-composable'
import { useForm } from 'vee-validate'
import { SEND_TYPES } from '@/utils/sendTypes'
import InputIdentifier from '@/components/Inputs/InputIdentifier'
import InputAmount from '@/components/Inputs/InputAmount'
import InputTextarea from '@/components/Inputs/InputTextarea'
import CommunitySwitch from '@/components/CommunitySwitch.vue'
import { user } from '@/graphql/queries'
import { COMMUNITY_NAME } from '@/config'
import CONFIG from '@/config'
import { useAppToast } from '@/composables/useToast'
export default {
name: 'TransactionForm',
components: {
InputIdentifier,
InputAmount,
InputTextarea,
CommunitySwitch,
const props = defineProps({
balance: { type: Number, default: 0 },
identifier: { type: String, default: '' },
amount: { type: Number, default: 0 },
memo: { type: String, default: '' },
selected: { type: String, default: 'send' },
targetCommunity: {
type: Object,
default: () => ({ uuid: '', name: CONFIG.COMMUNITY_NAME }),
},
props: {
balance: { type: Number, default: 0 },
identifier: { type: String, default: '' },
amount: { type: Number, default: 0 },
memo: { type: String, default: '' },
selected: { type: String, default: 'send' },
targetCommunity: {
type: Object,
default: function () {
return { uuid: '', name: COMMUNITY_NAME }
},
},
})
const emit = defineEmits(['set-transaction'])
const route = useRoute()
const router = useRouter()
const { toastError } = useAppToast()
const radioSelected = ref(props.selected)
const userName = ref('')
const recipientCommunity = ref({ uuid: '', name: '' })
// Use vee-validate's useForm
const { handleSubmit, resetForm, defineField } = useForm({
initialValues: {
identifier: props.identifier,
amount: props.amount ? String(props.amount) : '',
memo: props.memo,
targetCommunity: props.targetCommunity,
},
data() {
})
const [targetCommunity, targetCommunityProps] = defineField('targetCommunity')
const userIdentifier = computed(() => {
if (route.params.userIdentifier && route.params.communityIdentifier) {
return {
form: {
identifier: this.identifier,
amount: this.amount ? String(this.amount) : '',
memo: this.memo,
targetCommunity: this.targetCommunity,
},
radioSelected: this.selected,
userName: '',
recipientCommunity: { uuid: '', name: '' },
identifier: route.params.userIdentifier,
communityIdentifier: route.params.communityIdentifier,
}
}
return null
})
const isBalanceDisabled = computed(() => props.balance <= 0)
const { result: userResult, error: userError } = useQuery(
user,
() => userIdentifier.value,
() => ({ enabled: !!userIdentifier.value }),
)
watch(
() => userResult.value?.user,
(user) => {
if (user) {
userName.value = `${user.firstName} ${user.lastName}`
}
},
methods: {
onValidation() {
this.$refs.formValidator.validate()
},
onSubmit() {
if (this.userIdentifier) this.form.identifier = this.userIdentifier.identifier
this.$emit('set-transaction', {
selected: this.radioSelected,
identifier: this.form.identifier,
amount: Number(this.form.amount.replace(',', '.')),
memo: this.form.memo,
userName: this.userName,
targetCommunity: this.form.targetCommunity,
})
},
onReset(event) {
event.preventDefault()
this.form.identifier = ''
this.form.amount = ''
this.form.memo = ''
this.form.targetCommunity = { uuid: '', name: COMMUNITY_NAME }
this.$refs.formValidator.validate()
this.$router.replace('/send')
},
},
apollo: {
UserName: {
query() {
return user
},
fetchPolicy: 'network-only',
variables() {
return this.userIdentifier
},
skip() {
return !this.userIdentifier
},
update({ user }) {
this.userName = `${user.firstName} ${user.lastName}`
},
error({ message }) {
this.toastError(message)
},
},
},
computed: {
disabled() {
if (
this.form.identifier.length > 5 &&
parseInt(this.form.amount) <= parseInt(this.balance) &&
this.form.memo.length > 5 &&
this.form.memo.length <= 255
) {
return false
}
return true
},
isBalanceDisabled() {
return this.balance <= 0 ? 'disabled' : false
},
sendTypes() {
return SEND_TYPES
},
userIdentifier() {
if (
this.$route.params &&
this.$route.params.userIdentifier &&
this.$route.params.communityIdentifier
) {
return {
identifier: this.$route.params.userIdentifier,
communityIdentifier: this.$route.params.communityIdentifier,
}
}
return null
},
},
mounted() {
if (this.form.identifier !== '') this.$refs.formValidator.validate()
},
{ immediate: true },
)
watch(userError, (error) => {
if (error) {
toastError(error.message)
}
})
const onSubmit = handleSubmit((formValues) => {
if (userIdentifier.value) formValues.identifier = userIdentifier.value.identifier
emit('set-transaction', {
selected: radioSelected.value,
...formValues,
amount: Number(formValues.amount.replace(',', '.')),
userName: userName.value,
})
})
function onReset(event) {
event.preventDefault()
resetForm()
radioSelected.value = SEND_TYPES.send
router.replace('/send')
}
// Expose necessary methods and computed properties
// defineExpose({
// sendTypes: SEND_TYPES,
// handleSubmit: onSubmit,
// onReset,
// })
</script>
<style>
span.errors {
color: red;
}
#input-1:focus,
#input-2:focus,
#input-3:focus {
font-weight: bold;
}
.border-radius {
border-radius: 10px;
}

View File

@ -1,19 +1,19 @@
<template>
<div class="bg-white appBoxShadow gradido-border-radius p-5">
<div class="bg-white app-box-shadow gradido-border-radius p-5">
<div class="h3 mb-4">{{ $t('gdd_per_link.created') }}</div>
<clipboard-copy
:link="link"
:amount="amount"
:memo="memo"
:validUntil="validUntil"
:valid-until="validUntil"
></clipboard-copy>
<label>{{ $t('qrCode') }}</label>
<div class="text-center">
<div><figure-qr-code :link="link" /></div>
<div>
<b-button variant="secondary" @click="$emit('on-back')" class="mt-4" data-test="close-btn">
<BButton variant="secondary" class="mt-4" data-test="close-btn" @click="$emit('on-back')">
{{ $t('form.close') }}
</b-button>
</BButton>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<template>
<div class="bg-white appBoxShadow gradido-border-radius p-4">
<div class="bg-white app-box-shadow gradido-border-radius p-4">
<div>
<div class="gradido-font-15rem">{{ $t('form.sorry') }}</div>
<hr />
@ -7,21 +7,21 @@
<div class="test-send_transaction_error">{{ $t('form.send_transaction_error') }}</div>
<hr />
<div class="test-receiver-not-found" v-if="errorResult === 'recipient not known'">
<div v-if="errorResult === 'recipient not known'" class="test-receiver-not-found">
{{ $t('transaction.receiverNotFound') }}
</div>
<div
class="test-receiver-not-found"
v-if="errorResult === 'GraphQL error: The recipient account was deleted'"
class="test-receiver-not-found"
>
{{ $t('transaction.receiverDeleted') }}
</div>
<div v-else>{{ errorResult }}</div>
</div>
<p class="text-center mt-5">
<b-button variant="secondary" @click="$emit('on-back')">
<BButton variant="secondary" @click="$emit('on-back')">
{{ $t('form.close') }}
</b-button>
</BButton>
</p>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<div class="bg-white appBoxShadow gradido-border-radius p-3">
<div class="bg-white app-box-shadow gradido-border-radius p-3">
<div class="p-4" data-test="send-transaction-success-text">
{{ $t('form.thx') }}
<hr />

View File

@ -113,8 +113,7 @@ describe('GddTransactionList', () => {
balance: '31.76099091058520945292',
previousBalance: '30.76',
balanceDate: '2022-02-28T13:55:47',
memo:
'Um den Kessel schlingt den Reihn, Werft die Eingeweid hinein. Kröte du, die Nacht und Tag Unterm kalten Steine lag,',
memo: 'Um den Kessel schlingt den Reihn, Werft die Eingeweid hinein. Kröte du, die Nacht und Tag Unterm kalten Steine lag,',
linkedUser: {
firstName: 'Bibi',
lastName: 'Bloxberg',
@ -133,8 +132,7 @@ describe('GddTransactionList', () => {
balance: '10',
previousBalance: '31.75',
balanceDate: '2022-02-23T10:55:30',
memo:
'Monatlanges Gift sog ein, In den Topf zuerst hinein… (William Shakespeare, Die Hexen aus Macbeth)',
memo: 'Monatlanges Gift sog ein, In den Topf zuerst hinein… (William Shakespeare, Die Hexen aus Macbeth)',
linkedUser: {
firstName: 'Bibi',
lastName: 'Bloxberg',

View File

@ -2,21 +2,23 @@
<div class="gdd-transaction-list">
<div class="list-group">
<div v-if="!transactions" class="test-no-transactionlist text-right">
<b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>
<!-- <b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>-->
<IBiExclamationTriangle class="mr-2" />
<small>
{{ $t('error.no-transactionlist') }}
</small>
</div>
<div v-if="transactionCount < 0" class="test-empty-transactionlist text-right">
<b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>
<!-- <b-icon icon="exclamation-triangle" class="mr-2" variant="danger"></b-icon>-->
<IBiExclamationTriangle class="mr-2" />
<small>{{ $t('error.empty-transactionlist') }}</small>
</div>
<div v-for="({ id, typeId }, index) in transactions" :key="`l1-` + id">
<transaction-list-item
v-if="typeId === 'DECAY'"
:typeId="typeId"
class="pointer bg-white appBoxShadow gradido-border-radius px-4 pt-2 test-list-group-item"
:type-id="typeId"
class="pointer bg-white app-box-shadow gradido-border-radius px-4 pt-2 test-list-group-item"
>
<template #DECAY>
<transaction-decay v-bind="transactions[index]" />
@ -27,8 +29,8 @@
<div v-for="({ id, typeId }, index) in transactions" :key="`l2-` + id">
<transaction-list-item
v-if="typeId !== 'DECAY'"
:typeId="typeId"
class="pointer mb-3 bg-white appBoxShadow gradido-border-radius p-3 test-list-group-item"
:type-id="typeId"
class="pointer mb-3 bg-white app-box-shadow gradido-border-radius p-3 test-list-group-item"
>
<template #SEND>
<transaction-send v-bind="transactions[index]" />
@ -45,7 +47,7 @@
<template #LINK_SUMMARY>
<transaction-link-summary
v-bind="transactions[index]"
:transactionLinkCount="transactionLinkCount"
:transaction-link-count="transactionLinkCount"
@update-transactions="updateTransactions"
/>
</template>
@ -55,10 +57,10 @@
</div>
<b-pagination
v-if="isPaginationVisible"
v-model="currentPage"
class="mt-3"
pills
size="lg"
v-model="currentPage"
:per-page="pageSize"
:total-rows="transactionCount"
align="center"
@ -81,7 +83,7 @@ import TransactionCreation from '@/components/Transactions/TransactionCreation'
import TransactionLinkSummary from '@/components/Transactions/TransactionLinkSummary'
export default {
name: 'gdd-transaction-list',
name: 'GddTransactionList',
components: {
TransactionListItem,
TransactionDecay,
@ -91,7 +93,7 @@ export default {
TransactionLinkSummary,
},
props: {
transactions: { default: () => [] },
transactions: { type: Array, default: () => [] },
pageSize: { type: Number, default: 25 },
timestamp: { type: Number, default: 0 },
transactionCount: { type: Number, default: 0 },
@ -104,15 +106,6 @@ export default {
currentPage: 1,
}
},
methods: {
updateTransactions() {
this.$emit('update-transactions', {
currentPage: this.currentPage,
pageSize: this.pageSize,
})
window.scrollTo(0, 0)
},
},
computed: {
isPaginationVisible() {
return this.showPagination && this.pageSize < this.transactionCount
@ -127,6 +120,15 @@ export default {
handler: 'updateTransactions',
},
},
methods: {
updateTransactions() {
this.$emit('update-transactions', {
currentPage: this.currentPage,
pageSize: this.pageSize,
})
window.scrollTo(0, 0)
},
},
}
</script>
@ -135,8 +137,9 @@ collaps-icon {
width: 95%;
position: absolute;
}
.el-table .cell {
padding-left: 0px;
padding-right: 0px;
padding-left: 0;
padding-right: 0;
}
</style>

View File

@ -1,15 +1,15 @@
<template>
<div>
<b-list-group>
<b-list-group-item v-if="count > 5">
<BListGroup>
<BListGroupItem v-if="count > 5">
<!-- eslint-disable @intlify/vue-i18n/no-v-html -->
<router-link
to="/transactions"
v-html="$t('transaction.show_all', { count })"
></router-link>
<!-- eslint-enable @intlify/vue-i18n/no-v-html -->
</b-list-group-item>
</b-list-group>
</BListGroupItem>
</BListGroup>
</div>
</template>
@ -17,7 +17,7 @@
export default {
name: 'GddTransactionListFooter',
props: {
count: { count: Number },
count: { type: Number },
},
}
</script>

View File

@ -4,40 +4,40 @@
<div v-if="transactionGdtCount === 0" class="text-center">
{{ $t('gdt.no-transactions') }}
<hr />
<b-button class="gdt-funding" :href="link" target="_blank">
<BButton class="gdt-funding" :href="link" target="_blank">
{{ $t('gdt.funding') }}
</b-button>
</BButton>
</div>
<div v-else-if="transactionGdtCount === -1" class="text-center">
{{ $t('gdt.not-reachable') }}
</div>
<div
v-else
v-for="{ id, amount, date, comment, gdtEntryType, factor, gdt } in transactionsGdt"
v-else
:key="id"
>
<transaction
:id="id"
:amount="amount"
:date="date"
:comment="comment"
:gdtEntryType="gdtEntryType"
:gdt-entry-type="gdtEntryType"
:factor="factor"
:gdt="gdt"
:id="id"
></transaction>
/>
</div>
</div>
<b-pagination
<BPagination
v-if="transactionGdtCount > pageSize"
v-model="currentPage"
class="mt-3"
pills
size="lg"
v-model="currentPage"
:per-page="pageSize"
:total-rows="transactionGdtCount"
align="center"
:hide-ellipsis="true"
></b-pagination>
/>
</div>
</template>
@ -56,7 +56,7 @@ export default {
},
transactionGdtCount: { type: Number, required: true },
pageSize: { type: Number, required: true },
value: { type: Number, required: true },
modelValue: { type: Number, required: true },
},
data() {
return {
@ -66,15 +66,15 @@ export default {
},
watch: {
currentPage() {
if (this.value !== this.currentPage) this.$emit('input', this.currentPage)
if (this.modelValue !== this.currentPage) this.$emit('input', this.currentPage)
},
},
}
</script>
<style>
.el-table .cell {
padding-left: 0px;
padding-right: 0px;
padding-left: 0;
padding-right: 0;
}
.nav-tabs .nav-link.active,

View File

@ -1,97 +1,147 @@
<template>
<div class="input-amount">
<validation-provider
v-if="typ === 'TransactionForm'"
tag="div"
:rules="rules"
:name="name"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="label" :label-for="labelFor" data-test="input-amount">
<b-form-input
v-model="currentValue"
v-bind="ariaInput"
<template v-if="typ === 'TransactionForm'">
<BFormGroup :label="label" :label-for="labelFor" data-test="input-amount">
<BFormInput
:id="labelFor"
v-focus="amountFocused"
:model-value="value"
:class="$route.path === '/send' ? 'bg-248' : ''"
:name="name"
:placeholder="placeholder"
type="text"
:state="validated ? valid : false"
:state="meta.valid"
trim
v-focus="amountFocused"
@focus="amountFocused = true"
@blur="normalizeAmount(valid)"
:disabled="disabled"
autocomplete="off"
></b-form-input>
<b-form-invalid-feedback v-bind="ariaMsg">
{{ errors[0] }}
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
<b-input-group v-else append="GDD" :label="label" :label-for="labelFor">
<b-form-input
v-model="currentValue"
@update:model-value="normalizeAmount($event)"
@focus="amountFocused = true"
@blur="normalizeAmount($event)"
/>
<BFormInvalidFeedback v-if="errorMessage">
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</template>
<BInputGroup v-else append="GDD" :label="label" :label-for="labelFor">
<BFormInput
:id="labelFor"
v-focus="amountFocused"
:model-value="value"
:name="name"
:placeholder="placeholder"
type="text"
readonly
trim
v-focus="amountFocused"
@focus="amountFocused = true"
@blur="normalizeAmount(valid)"
></b-form-input>
</b-input-group>
/>
</BInputGroup>
</div>
</template>
<script>
export default {
name: 'InputAmount',
props: {
rules: {
type: Object,
default: () => {},
},
typ: { type: String, default: 'TransactionForm' },
name: { type: String, required: true, default: 'Amount' },
label: { type: String, required: true, default: 'Amount' },
placeholder: { type: String, required: true, default: 'Amount' },
value: { type: String, required: true },
balance: { type: Number, default: 0.0 },
disabled: { required: false, type: Boolean, default: false },
<!--<script>-->
<!--export default {-->
<!-- name: 'InputAmount',-->
<!-- props: {-->
<!-- rules: {-->
<!-- type: Object,-->
<!-- default: () => {},-->
<!-- },-->
<!-- typ: { type: String, default: 'TransactionForm' },-->
<!-- name: { type: String, required: true, default: 'Amount' },-->
<!-- label: { type: String, required: true, default: 'Amount' },-->
<!-- placeholder: { type: String, required: true, default: 'Amount' },-->
<!-- value: { type: String, required: true },-->
<!-- balance: { type: Number, default: 0.0 },-->
<!-- disabled: { required: false, type: Boolean, default: false },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- currentValue: this.value,-->
<!-- amountValue: 0.0,-->
<!-- amountFocused: false,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- errorMessages() {-->
<!-- return errorMessages-->
<!-- },-->
<!-- labelFor() {-->
<!-- return this.name + '-input-field'-->
<!-- },-->
<!-- },-->
<!-- watch: {-->
<!-- currentValue() {-->
<!-- this.$emit('input', this.currentValue)-->
<!-- },-->
<!-- value() {-->
<!-- if (this.value !== this.currentValue) this.currentValue = this.value-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- normalizeAmount(isValid) {-->
<!-- this.amountFocused = false-->
<!-- if (!isValid) return-->
<!-- this.amountValue = this.currentValue.replace(',', '.')-->
<!-- this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')-->
<!-- },-->
<!-- },-->
<!-- mounted() {-->
<!-- if (this.value !== '') this.normalizeAmount(true)-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch } from 'vue'
import { useField } from 'vee-validate'
import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
const props = defineProps({
rules: {
type: Object,
default: () => ({}),
},
data() {
return {
currentValue: this.value,
amountValue: 0.0,
amountFocused: false,
}
},
computed: {
labelFor() {
return this.name + '-input-field'
},
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
value() {
if (this.value !== this.currentValue) this.currentValue = this.value
},
},
methods: {
normalizeAmount(isValid) {
this.amountFocused = false
if (!isValid) return
this.amountValue = this.currentValue.replace(',', '.')
this.currentValue = this.$n(this.amountValue, 'ungroupedDecimal')
},
},
mounted() {
if (this.value !== '') this.normalizeAmount(true)
typ: { type: String, default: 'TransactionForm' },
name: { type: String, required: true, default: 'Amount' },
label: { type: String, required: true, default: 'Amount' },
placeholder: { type: String, required: true, default: 'Amount' },
balance: { type: Number, default: 0.0 },
disabled: { required: false, type: Boolean, default: false },
})
const emit = defineEmits(['update:modelValue'])
const route = useRoute()
const { n } = useI18n()
const { value, meta, errorMessage } = useField(props.name, props.rules)
const amountFocused = ref(false)
const amountValue = ref(0.0)
const labelFor = computed(() => props.name + '-input-field')
watch(value, (newValue) => {
emit('update:modelValue', newValue)
})
watch(
() => props.modelValue,
(newValue) => {
if (newValue !== value.value) value.value = newValue
},
)
const normalizeAmount = (inputValue) => {
const oldValue = value.value
const amountPattern = /^\d+([,.]\d{1,2})?$/
amountFocused.value = false
// if (!meta.valid) return
// if (!amountPattern.test(inputValue)) {
// value.value = oldValue
// } else {
value.value = inputValue.replace(',', '.')
// }
}
</script>

View File

@ -1,79 +1,171 @@
<!--<template>-->
<!-- <validation-provider-->
<!-- tag="div"-->
<!-- :rules="rules"-->
<!-- :name="name"-->
<!-- v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"-->
<!-- >-->
<!-- <BFormGroup :label="label" :label-for="labelFor" data-test="input-email">-->
<!-- <BFormInput-->
<!-- v-model="currentValue"-->
<!-- v-bind="ariaInput"-->
<!-- data-test="input-email"-->
<!-- :id="labelFor"-->
<!-- :name="name"-->
<!-- :placeholder="placeholder"-->
<!-- type="email"-->
<!-- :state="validated ? valid : false"-->
<!-- trim-->
<!-- :class="$route.path === '/send' ? 'bg-248' : ''"-->
<!-- v-focus="emailFocused"-->
<!-- @focus="emailFocused = true"-->
<!-- @blur="normalizeEmail()"-->
<!-- :disabled="disabled"-->
<!-- autocomplete="off"-->
<!-- />-->
<!-- <BFormInvalidFeedback v-bind="ariaMsg">-->
<!-- {{ errors[0] }}-->
<!-- </BFormInvalidFeedback>-->
<!-- </BFormGroup>-->
<!-- </validation-provider>-->
<!--</template>-->
<template>
<validation-provider
tag="div"
:rules="rules"
:name="name"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="label" :label-for="labelFor" data-test="input-email">
<b-form-input
v-model="currentValue"
<div>
<BFormGroup :label="defaultTranslations.label" :label-for="labelFor" data-test="input-email">
<BFormInput
v-bind="ariaInput"
data-test="input-email"
:id="labelFor"
:model-value="value"
:state="meta.valid"
data-test="input-email"
:name="name"
:placeholder="placeholder"
:placeholder="defaultTranslations.placeholder"
type="email"
:state="validated ? valid : false"
trim
:class="$route.path === '/send' ? 'bg-248' : ''"
v-focus="emailFocused"
@focus="emailFocused = true"
@blur="normalizeEmail()"
:disabled="disabled"
autocomplete="off"
></b-form-input>
<b-form-invalid-feedback v-bind="ariaMsg">
{{ errors[0] }}
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
@update:modelValue="normalizeEmail($event)"
/>
<BFormInvalidFeedback v-bind="ariaMsg">
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</div>
</template>
<script>
export default {
name: 'InputEmail',
props: {
rules: {
default: () => {
return {
required: true,
email: true,
}
},
},
name: { type: String, required: true },
label: { type: String, required: true },
placeholder: { type: String, required: true },
value: { type: String, required: true },
disabled: { type: Boolean, required: false, default: false },
<!--<script>-->
<!--export default {-->
<!-- name: 'InputEmail',-->
<!-- props: {-->
<!-- rules: {-->
<!-- default: () => {-->
<!-- return {-->
<!-- required: true,-->
<!-- email: true,-->
<!-- }-->
<!-- },-->
<!-- },-->
<!-- name: { type: String, required: true },-->
<!-- label: { type: String, required: true },-->
<!-- placeholder: { type: String, required: true },-->
<!-- modelValue: { type: String, required: true },-->
<!-- disabled: { type: Boolean, required: false, default: false },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- currentValue: this.modelValue,-->
<!-- emailFocused: false,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- labelFor() {-->
<!-- return this.name + '-input-field'-->
<!-- },-->
<!-- },-->
<!-- watch: {-->
<!-- currentValue() {-->
<!-- this.$emit('input', this.currentValue)-->
<!-- },-->
<!-- modelValue() {-->
<!-- if (this.modelValue !== this.currentValue) {-->
<!-- this.currentValue = this.modelValue-->
<!-- }-->
<!-- this.$emit('onValidation')-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- normalizeEmail() {-->
<!-- this.emailFocused = false-->
<!-- this.currentValue = this.currentValue.trim()-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, watch, computed, defineProps, defineEmits } from 'vue'
import { useField } from 'vee-validate'
import { useI18n } from 'vue-i18n'
import * as yup from 'yup'
// rules: {
// type: [String, Object, Function],
// default: () => ({
// required: true,
// email: true,
// }),
// },
// Define props with default values
const props = defineProps({
name: {
type: String,
default: 'email',
},
data() {
return {
currentValue: this.value,
emailFocused: false,
}
label: {
type: String,
default: 'Email',
},
computed: {
labelFor() {
return this.name + '-input-field'
},
placeholder: {
type: String,
default: 'Email',
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
value() {
if (this.value !== this.currentValue) {
this.currentValue = this.value
}
this.$emit('onValidation')
},
},
methods: {
normalizeEmail() {
this.emailFocused = false
this.currentValue = this.currentValue.trim()
},
disabled: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['onValidation'])
// Use the useField hook for validation
const { value, errorMessage, errors, validate, meta } = useField(() => props.name, 'required|email')
const { t } = useI18n()
// const emailFocused = ref(false)
const defaultTranslations = computed(() => ({
label: props.label ?? t('form.email'),
placeholder: props.placeholder ?? t('form.email'),
}))
const normalizeEmail = (emailAddress) => {
// TODO trigger blur on bootstrap input
// emailFocused.value = false
value.value = emailAddress.trim()
validate()
}
// Computed properties for ARIA attributes and labelFor
const ariaInput = computed(() => ({
'aria-invalid': errorMessage ? 'true' : false,
'aria-describedby': `${props.name}-feedback`,
}))
const ariaMsg = computed(() => ({
id: `${props.name}-feedback`,
}))
const labelFor = computed(() => `${props.name}-input-field`)
</script>

View File

@ -1,61 +1,113 @@
<template>
<!-- <div class="input-hour">-->
<!-- <validation-provider-->
<!-- tag="div"-->
<!-- :rules="rules"-->
<!-- :name="name"-->
<!-- v-slot="{ valid, validated, ariaInput }"-->
<!-- >-->
<!-- <b-form-group :label="label" :label-for="labelFor">-->
<!-- <b-form-input-->
<!-- v-model="currentValue"-->
<!-- v-bind="ariaInput"-->
<!-- :id="labelFor"-->
<!-- :name="name"-->
<!-- :placeholder="placeholder"-->
<!-- type="number"-->
<!-- :state="validated ? valid : false"-->
<!-- step="0.25"-->
<!-- min="0"-->
<!-- :max="validMaxTime"-->
<!-- class="bg-248"-->
<!-- ></b-form-input>-->
<!-- </b-form-group>-->
<!-- </validation-provider>-->
<!-- </div>-->
<div class="input-hour">
<validation-provider
tag="div"
:rules="rules"
:name="name"
v-slot="{ valid, validated, ariaInput }"
>
<b-form-group :label="label" :label-for="labelFor">
<b-form-input
v-model="currentValue"
v-bind="ariaInput"
:id="labelFor"
:name="name"
:placeholder="placeholder"
type="number"
:state="validated ? valid : false"
step="0.25"
min="0"
:max="validMaxTime"
class="bg-248"
></b-form-input>
</b-form-group>
</validation-provider>
<BFormGroup :label="label" :label-for="labelFor">
<BFormInput
:id="labelFor"
:model-value="currentValue"
:name="name"
:placeholder="placeholder"
type="number"
:state="meta.valid"
step="0.25"
min="0"
:max="validMaxTime"
class="bg-248"
@update:model-value="currentValue = $event"
/>
<BFormInvalidFeedback v-if="errorMessage">
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</div>
</template>
<script>
export default {
name: 'InputHour',
props: {
rules: {
type: Object,
default: () => {},
},
name: { type: String, required: true },
label: { type: String, required: true },
placeholder: { type: String, required: true },
value: { type: Number, required: true, default: 0 },
validMaxTime: { type: Number, required: true },
<!--<script>-->
<!--export default {-->
<!-- name: 'InputHour',-->
<!-- props: {-->
<!-- rules: {-->
<!-- type: Object,-->
<!-- default: () => {},-->
<!-- },-->
<!-- name: { type: String, required: true },-->
<!-- label: { type: String, required: true },-->
<!-- placeholder: { type: String, required: true },-->
<!-- value: { type: Number, required: true, default: 0 },-->
<!-- validMaxTime: { type: Number, required: true },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- currentValue: 0,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- labelFor() {-->
<!-- return this.name + '-input-field'-->
<!-- },-->
<!-- },-->
<!-- watch: {-->
<!-- currentValue() {-->
<!-- this.$emit('input', Number(this.currentValue))-->
<!-- },-->
<!-- value() {-->
<!-- if (this.value !== this.currentValue) this.currentValue = this.value-->
<!-- this.$emit('updateAmount', this.currentValue)-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch } from 'vue'
import { useField } from 'vee-validate'
const props = defineProps({
rules: {
type: Object,
default: () => ({}),
},
data() {
return {
currentValue: 0,
}
name: {
type: String,
required: true,
},
computed: {
labelFor() {
return this.name + '-input-field'
},
label: {
type: String,
required: true,
},
watch: {
currentValue() {
this.$emit('input', Number(this.currentValue))
},
value() {
if (this.value !== this.currentValue) this.currentValue = this.value
this.$emit('updateAmount', this.currentValue)
},
placeholder: {
type: String,
required: true,
},
}
validMaxTime: {
type: Number,
required: true,
},
})
const { value: currentValue, errorMessage, meta } = useField(props.name, props.rules)
const labelFor = computed(() => `${props.name}-input-field`)
</script>

View File

@ -1,68 +1,61 @@
<template>
<validation-provider
tag="div"
:rules="rules"
:name="name"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="label" :label-for="labelFor" data-test="input-identifier">
<b-form-input
v-model="currentValue"
v-bind="ariaInput"
:id="labelFor"
:name="name"
:placeholder="placeholder"
type="text"
:state="validated ? valid : false"
trim
class="bg-248"
:disabled="disabled"
autocomplete="off"
></b-form-input>
<b-form-invalid-feedback v-bind="ariaMsg">
{{ errors[0] }}
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
<BFormGroup :label="label" :label-for="labelFor" data-test="input-identifier">
<BFormInput
:id="labelFor"
:model-value="value"
:name="name"
:placeholder="placeholder"
type="text"
:state="meta.valid"
trim
class="bg-248"
:disabled="disabled"
autocomplete="off"
@update:model-value="value = $event"
/>
<BFormInvalidFeedback v-if="errorMessage">
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</template>
<script>
export default {
name: 'InputEmail',
props: {
rules: {
default: () => {
return {
required: true,
validIdentifier: true,
}
},
},
name: { type: String, required: true },
label: { type: String, required: true },
placeholder: { type: String, required: true },
value: { type: String, required: true },
disabled: { type: Boolean, required: false, default: false },
<script setup>
import { computed, watch } from 'vue'
import { useField } from 'vee-validate'
const props = defineProps({
rules: {
type: Object,
default: () => ({
required: true,
validIdentifier: true,
}),
},
data() {
return {
currentValue: this.value,
name: { type: String, required: true },
label: { type: String, required: true },
placeholder: { type: String, required: true },
modelValue: { type: String },
disabled: { type: Boolean, required: false, default: false },
})
const emit = defineEmits(['update:modelValue', 'onValidation'])
const { value, meta, errorMessage } = useField(props.name, props.rules, {
initialValue: props.modelValue,
})
const labelFor = computed(() => props.name + '-input-field')
watch(value, (newValue) => {
emit('update:modelValue', newValue)
})
watch(
() => props.modelValue,
(newValue) => {
if (newValue !== value.value) {
value.value = newValue
emit('onValidation')
}
},
computed: {
labelFor() {
return this.name + '-input-field'
},
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
value() {
if (this.value !== this.currentValue) {
this.currentValue = this.value
}
this.$emit('onValidation')
},
},
}
)
</script>

View File

@ -1,87 +1,194 @@
<!--<template>-->
<!-- <validation-provider-->
<!-- tag="div"-->
<!-- :rules="rules"-->
<!-- :name="name"-->
<!-- :bails="!props.showAllErrors"-->
<!-- :immediate="props.immediate"-->
<!-- v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"-->
<!-- >-->
<!-- <BFormGroup :label="label" :label-for="labelFor">-->
<!-- <BInputGroup>-->
<!-- <BFormInput-->
<!-- v-model="currentValue"-->
<!-- v-bind="ariaInput"-->
<!-- :id="labelFor"-->
<!-- :name="name"-->
<!-- :placeholder="props.placeholder"-->
<!-- :type="showPassword ? 'text' : 'password'"-->
<!-- :state="validated ? valid : false"-->
<!-- data-test="password-input-field"-->
<!-- ></BFormInput>-->
<!-- <template #append>-->
<!-- <BButton-->
<!-- variant="outline-light"-->
<!-- @click="toggleShowPassword"-->
<!-- class="border-left-0 rounded-right"-->
<!-- tabindex="-1"-->
<!-- >-->
<!-- &lt;!&ndash; <b-icon :icon="showPassword ? 'eye' : 'eye-slash'" />&ndash;&gt;-->
<!-- {{ showPassword ? 'eye' : 'eye-slash' }}-->
<!-- </BButton>-->
<!-- </template>-->
<!-- <BFormInvalidFeedback v-bind="ariaMsg">-->
<!-- <div v-if="props.showAllErrors">-->
<!-- <span v-for="error in errors" :key="error">-->
<!-- {{ error }}-->
<!-- <br />-->
<!-- </span>-->
<!-- </div>-->
<!-- <div v-else>-->
<!-- {{ errors[0] }}-->
<!-- </div>-->
<!-- </BFormInvalidFeedback>-->
<!-- </BInputGroup>-->
<!-- </BFormGroup>-->
<!-- </validation-provider>-->
<!--</template>-->
<template>
<validation-provider
tag="div"
:rules="rules"
:name="name"
:bails="!showAllErrors"
:immediate="immediate"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="label" :label-for="labelFor">
<b-input-group>
<b-form-input
v-model="currentValue"
v-bind="ariaInput"
<div>
<BFormGroup :label="defaultTranslations.label" :label-for="labelFor">
<BInputGroup>
<BFormInput
:id="labelFor"
:model-value="value"
:name="name"
:placeholder="placeholder"
:placeholder="defaultTranslations.placeholder"
:type="showPassword ? 'text' : 'password'"
:state="validated ? valid : false"
:state="meta.valid"
data-test="password-input-field"
></b-form-input>
<b-input-group-append>
<b-button
v-bind="ariaInput"
@update:modelValue="value = $event"
/>
<template #append>
<BButton
variant="outline-light"
@click="toggleShowPassword"
class="border-left-0 rounded-right"
tabindex="-1"
@click="toggleShowPassword"
>
<b-icon :icon="showPassword ? 'eye' : 'eye-slash'" />
</b-button>
</b-input-group-append>
<b-form-invalid-feedback v-bind="ariaMsg">
<div v-if="showAllErrors">
<span v-for="error in errors" :key="error">
{{ error }}
<br />
</span>
</div>
<div v-else>
{{ errors[0] }}
</div>
</b-form-invalid-feedback>
</b-input-group>
</b-form-group>
</validation-provider>
<IBiEye v-if="showPassword" />
<IBiEyeSlash v-else />
</BButton>
</template>
</BInputGroup>
<BFormInvalidFeedback v-bind="ariaMsg">
<!-- <div v-if="showAllErrors">-->
<!-- <span v-for="error in errors" :key="error">-->
<!-- {{ error }}-->
<!-- <br />-->
<!-- </span>-->
<!-- </div>-->
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</div>
</template>
<script>
export default {
name: 'InputPassword',
props: {
rules: {
default: () => {
return {
required: true,
}
},
},
name: { type: String, default: 'password' },
label: { type: String, default: 'Password' },
placeholder: { type: String, default: 'Password' },
value: { required: true, type: String },
showAllErrors: { type: Boolean, default: false },
immediate: { type: Boolean, default: false },
<!--<script>-->
<!--export default {-->
<!-- name: 'InputPassword',-->
<!-- props: {-->
<!-- rules: {-->
<!-- default: () => {-->
<!-- return {-->
<!-- required: true,-->
<!-- }-->
<!-- },-->
<!-- },-->
<!-- name: { type: String, default: 'password' },-->
<!-- label: { type: String, default: 'Password' },-->
<!-- placeholder: { type: String, default: 'Password' },-->
<!-- value: { required: true, type: String },-->
<!-- showAllErrors: { type: Boolean, default: false },-->
<!-- immediate: { type: Boolean, default: false },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- currentValue: '',-->
<!-- showPassword: false,-->
<!-- }-->
<!-- },-->
<!-- computed: {-->
<!-- labelFor() {-->
<!-- return this.name + '-input-field'-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- toggleShowPassword() {-->
<!-- this.showPassword = !this.showPassword-->
<!-- },-->
<!-- },-->
<!-- watch: {-->
<!-- currentValue() {-->
<!-- this.$emit('input', this.currentValue)-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, watch, defineProps, defineEmits, toRef } from 'vue'
import { useField } from 'vee-validate'
import { useI18n } from 'vue-i18n'
// Define props with default values
const props = defineProps({
name: {
type: String,
default: 'password',
},
data() {
return {
currentValue: '',
showPassword: false,
}
label: {
type: String,
default: 'Password',
},
computed: {
labelFor() {
return this.name + '-input-field'
},
placeholder: {
type: String,
default: 'Password',
},
methods: {
toggleShowPassword() {
this.showPassword = !this.showPassword
},
modelValue: {
type: String,
required: true,
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
showAllErrors: {
type: Boolean,
default: false,
},
immediate: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:modelValue'])
const name = toRef(props, 'name')
// Use the useField hook for validation
const { value, errorMessage, meta } = useField(name, 'required')
const { t } = useI18n()
const defaultTranslations = computed(() => ({
label: props.label ?? t('form.password'),
placeholder: props.placeholder ?? t('form.password'),
}))
// Local state
const showPassword = ref(false)
// Toggle password visibility
const toggleShowPassword = () => {
showPassword.value = !showPassword.value
}
// Computed properties for ARIA attributes and labelFor
const ariaInput = computed(() => ({
'aria-invalid': meta.valid ? false : 'true',
'aria-describedby': `${props.name}-feedback`,
}))
const ariaMsg = computed(() => ({
id: `${props.name}-feedback`,
}))
const labelFor = computed(() => `${props.name}-input-field`)
</script>

View File

@ -1,8 +1,10 @@
<template>
<div>
<b-row class="mb-2">
<b-col>
<BRow class="mb-2">
<BCol>
<input-password
id="new-password-input-field"
:model-value="password"
:rules="{
required: true,
containsLowercaseCharacter: true,
@ -12,75 +14,68 @@
atLeastOneSpecialCharater: true,
noWhitespaceCharacters: true,
}"
id="new-password-input-field"
:label="register ? $t('form.password') : $t('form.password_new')"
:showAllErrors="true"
:show-all-errors="true"
:immediate="true"
:name="createId(register ? $t('form.password') : $t('form.password_new'))"
:placeholder="register ? $t('form.password') : $t('form.password_new')"
v-model="password"
></input-password>
</b-col>
</b-row>
<b-row class="mb-2">
<b-col>
@update:modelValue="password = $event"
/>
</BCol>
</BRow>
<BRow class="mb-2">
<BCol>
<input-password
id="repeat-new-password-input-field"
:model-value="passwordRepeat"
:rules="{
required: true,
samePassword: value.password,
samePassword: password,
}"
id="repeat-new-password-input-field"
:label="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
:immediate="true"
:name="createId(register ? $t('form.passwordRepeat') : $t('form.password_new_repeat'))"
:placeholder="register ? $t('form.passwordRepeat') : $t('form.password_new_repeat')"
v-model="passwordRepeat"
></input-password>
</b-col>
</b-row>
@update:modelValue="passwordRepeat = $event"
/>
</BCol>
</BRow>
</div>
</template>
<script>
<script setup>
import { computed, ref, watch } from 'vue'
import InputPassword from './InputPassword'
import { BCol, BRow } from 'bootstrap-vue-next'
export default {
name: 'InputPasswordConfirm',
components: {
InputPassword,
const password = ref('')
const passwordRepeat = ref('')
defineProps({
modelValue: {
type: Object,
required: true,
},
props: {
value: {
type: Object,
required: true,
},
register: {
type: Boolean,
required: false,
},
},
data() {
return {
password: '',
passwordRepeat: '',
}
},
methods: {
createId(text) {
return text.replace(/ +/g, '-')
},
},
computed: {
passwordObject() {
return { password: this.password, passwordRepeat: this.passwordRepeat }
},
},
watch: {
password() {
this.$emit('input', this.passwordObject)
},
passwordRepeat() {
this.$emit('input', this.passwordObject)
},
register: {
type: Boolean,
required: false,
},
})
const emit = defineEmits(['input'])
const createId = (text) => {
return text.replace(/ +/g, '-')
}
const passwordObject = computed(() => {
return { password: password.value, passwordRepeat: passwordRepeat.value }
})
watch(
[password, passwordRepeat],
() => {
emit('input', passwordObject.value)
},
{ deep: true },
)
</script>

View File

@ -1,62 +1,55 @@
<template>
<validation-provider
tag="div"
:rules="rules"
:name="name"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="label" :label-for="labelFor" data-test="input-textarea">
<b-form-textarea
v-model="currentValue"
v-bind="ariaInput"
<div>
<BFormGroup :label="label" :label-for="labelFor" data-test="input-textarea">
<BFormTextarea
:id="labelFor"
:model-value="currentValue"
class="bg-248"
:name="name"
:placeholder="placeholder"
:state="validated ? valid : false"
:state="meta.valid"
trim
rows="4"
max-rows="4"
:disabled="disabled"
no-resize
></b-form-textarea>
<b-form-invalid-feedback v-bind="ariaMsg">
{{ errors[0] }}
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
@update:modelValue="currentValue = $event"
/>
<BFormInvalidFeedback v-if="errorMessage">
{{ errorMessage }}
</BFormInvalidFeedback>
</BFormGroup>
</div>
</template>
<script>
export default {
name: 'InputTextarea',
props: {
rules: {
type: Object,
default: () => {},
},
name: { type: String, required: true },
label: { type: String, required: true },
placeholder: { type: String, required: true },
value: { type: String, required: true },
disabled: { required: false, type: Boolean, default: false },
<script setup>
import { computed } from 'vue'
import { useField } from 'vee-validate'
const props = defineProps({
rules: {
type: Object,
default: () => ({}),
},
data() {
return {
currentValue: this.value,
}
name: {
type: String,
required: true,
},
computed: {
labelFor() {
return this.name + '-input-field'
},
label: {
type: String,
required: true,
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
value() {
if (this.value !== this.currentValue) this.currentValue = this.value
},
placeholder: {
type: String,
required: true,
},
}
disabled: {
type: Boolean,
default: false,
},
})
const { value: currentValue, errorMessage, meta } = useField(props.name, props.rules)
const labelFor = computed(() => `${props.name}-input-field`)
</script>

View File

@ -1,40 +1,25 @@
<template>
<div class="input-username">
<validation-provider
tag="div"
:rules="rules"
:name="name"
:bails="!showAllErrors"
:immediate="immediate"
vid="username"
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
>
<b-form-group :label="$t('form.username')" :description="$t('settings.usernameInfo')">
<b-input-group>
<b-form-input
v-model="currentValue"
<div>
<BFormGroup :label="$t('form.username')" :description="$t('settings.usernameInfo')">
<BInputGroup>
<BFormInput
v-bind="ariaInput"
:id="labelFor"
:model-value="currentValue"
:name="name"
:placeholder="placeholder"
type="text"
:state="validated ? valid : false"
autocomplete="off"
data-test="username"
></b-form-input>
<b-input-group-append>
<b-button
size="lg"
text="Button"
variant="secondary"
icon="x-lg"
@click="$emit('set-is-edit', false)"
>
<b-icon-x-circle></b-icon-x-circle>
</b-button>
</b-input-group-append>
</b-input-group>
<b-form-invalid-feedback v-bind="ariaMsg">
@update:modelValue="updateValue"
/>
<BButton size="lg" text="Button" variant="secondary" append @click="emitSetIsEdit">
<IBiXCircle style="height: 17px; width: 17px" />
</BButton>
</BInputGroup>
<BFormInvalidFeedback v-bind="ariaMsg">
<div v-if="showAllErrors">
<span v-for="error in errors" :key="error">
{{ error }}
@ -42,47 +27,52 @@
</span>
</div>
<div v-else>
{{ errors[0] }}
{{ errors?.[0] }}
</div>
</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
</BFormInvalidFeedback>
</BFormGroup>
</div>
</div>
</template>
<script>
export default {
name: 'InputUsername',
props: {
isEdit: { type: Boolean, default: false },
rules: {
default: () => {
return {
required: true,
}
},
},
name: { type: String, default: 'username' },
label: { type: String, default: 'Username' },
placeholder: { type: String, default: 'Username' },
value: { required: true, type: String },
showAllErrors: { type: Boolean, default: false },
immediate: { type: Boolean, default: false },
unique: { type: Boolean, required: true },
},
data() {
return {
currentValue: this.value,
}
},
computed: {
labelFor() {
return this.name + '-input-field'
},
},
watch: {
currentValue() {
this.$emit('input', this.currentValue)
},
},
<script setup>
import {
BFormGroup,
BInputGroup,
BFormInput,
BButton,
BFormInvalidFeedback,
} from 'bootstrap-vue-next'
import { ref, computed, watch, defineProps, defineEmits } from 'vue'
import { useForm } from 'vee-validate'
const props = defineProps({
isEdit: { type: Boolean, default: false },
rules: { type: Object, default: () => ({ required: true }) },
name: { type: String, default: 'username' },
label: { type: String, default: 'Username' },
placeholder: { type: String, default: 'Username' },
modelValue: { type: String, required: true },
showAllErrors: { type: Boolean, default: false },
immediate: { type: Boolean, default: false },
unique: { type: Boolean, required: true },
})
const currentValue = ref(props?.modelValue)
const { errors, valid, validated, ariaInput, ariaMsg } = useForm({
initialValues: currentValue.value,
})
const emit = defineEmits(['update:modelValue', 'set-is-edit'])
const labelFor = computed(() => `${props.name}-input-field`)
const emitSetIsEdit = (bool) => {
emit('set-is-edit', bool)
}
const updateValue = (e) => {
currentValue.value = e
emit('update:modelValue', e)
}
</script>

View File

@ -22,7 +22,7 @@
<script>
export default {
name: 'lastName',
name: 'LastName',
props: {
value: { type: String, default: '' },
},

View File

@ -3,8 +3,8 @@
<b-dropdown size="sm" :text="currentLanguage.name + ' - ' + currentLanguage.code">
<b-dropdown-item
v-for="lang in locales"
@click.prevent="saveLocale(lang.code)"
:key="lang.code"
@click.prevent="saveLocale(lang.code)"
>
{{ lang.name }}
</b-dropdown-item>
@ -23,6 +23,9 @@ export default {
currentLanguage: {},
}
},
created() {
this.setCurrentLanguage()
},
methods: {
setLocale(locale) {
this.$store.commit('language', locale)
@ -66,8 +69,5 @@ export default {
this.currentLanguage = object
},
},
created() {
this.setCurrentLanguage()
},
}
</script>

View File

@ -11,15 +11,16 @@
{{ lang.name }}
</span>
</span>
<b-icon icon="caret-down-fill" aria-hidden="true"></b-icon>
<!-- <b-icon icon="caret-down-fill" aria-hidden="true"></b-icon>-->
<IBiCaretDownFill />
</div>
<b-collapse id="collapse-1" class="mt-4">
<BCollapse id="collapse-1" class="mt-4">
<span
v-for="(lang, index) in locales"
@click.prevent="saveLocale(lang.code)"
:key="lang.code"
class="pointer"
:class="$store.state.language === lang.code ? 'c-grey' : 'c-blau'"
@click.prevent="saveLocale(lang.code)"
>
<span v-if="lang.code !== $store.state.language" v-b-toggle.collapse-1 class="locales">
{{ lang.name }}
@ -35,78 +36,152 @@
{{ locales.length - 1 > index ? $t('math.pipe') : '' }}
</span>
</span>
</b-collapse>
</BCollapse>
</div>
</template>
<script>
import locales from '@/locales/'
import { updateUserInfos } from '@/graphql/mutations'
<!--<script>-->
<!--import locales from '@/locales/'-->
<!--import { updateUserInfos } from '@/graphql/mutations'-->
export default {
name: 'LanguageSwitch',
data() {
return {
locales: locales,
currentLanguage: {},
}
},
methods: {
setLocale(locale) {
this.$store.commit('language', locale)
this.currentLanguage = this.getLocaleObject(locale)
},
async saveLocale(locale) {
if (this.$i18n.locale === locale) return
this.setLocale(locale)
if (this.$store.state.gradidoID) {
this.$apollo
.mutate({
mutation: updateUserInfos,
variables: {
locale: locale,
},
})
.then(() => {
this.toastSuccess(this.$t('settings.language.success'))
})
.catch(() => {
this.toastSuccess(this.$t('error'))
})
}
},
getLocaleObject(code) {
return this.locales.find((l) => l.code === code)
},
getNavigatorLanguage() {
const lang = navigator.language
if (lang) return lang.split('-')[0]
return lang
},
setCurrentLanguage() {
let locale = this.$store.state.language || this.getNavigatorLanguage() || 'en'
let object = this.getLocaleObject(locale)
if (!object) {
locale = 'en'
object = this.getLocaleObject(locale)
}
this.setLocale(locale)
this.currentLanguage = object
},
},
computed: {
indexOfSelectedLocale() {
return this.locales.findIndex((element) => element.code === this.$store.state.language)
},
indexOfSecondLastLocale() {
return this.locales.length - 2
},
indexOfLastLocale() {
return this.locales.length - 1
},
},
created() {
this.setCurrentLanguage()
},
<!--export default {-->
<!-- name: 'LanguageSwitch',-->
<!-- data() {-->
<!-- return {-->
<!-- locales: locales,-->
<!-- currentLanguage: {},-->
<!-- }-->
<!-- },-->
<!-- methods: {-->
<!-- setLocale(locale) {-->
<!-- this.$store.commit('language', locale)-->
<!-- this.currentLanguage = this.getLocaleObject(locale)-->
<!-- },-->
<!-- async saveLocale(locale) {-->
<!-- if (this.$i18n.locale === locale) return-->
<!-- this.setLocale(locale)-->
<!-- if (this.$store.state.gradidoID) {-->
<!-- this.$apollo-->
<!-- .mutate({-->
<!-- mutation: updateUserInfos,-->
<!-- variables: {-->
<!-- locale: locale,-->
<!-- },-->
<!-- })-->
<!-- .then(() => {-->
<!-- this.toastSuccess(this.$t('settings.language.success'))-->
<!-- })-->
<!-- .catch(() => {-->
<!-- this.toastSuccess(this.$t('error'))-->
<!-- })-->
<!-- }-->
<!-- },-->
<!-- getLocaleObject(code) {-->
<!-- return this.locales.find((l) => l.code === code)-->
<!-- },-->
<!-- getNavigatorLanguage() {-->
<!-- const lang = navigator.language-->
<!-- if (lang) return lang.split('-')[0]-->
<!-- return lang-->
<!-- },-->
<!-- setCurrentLanguage() {-->
<!-- let locale = this.$store.state.language || this.getNavigatorLanguage() || 'en'-->
<!-- let object = this.getLocaleObject(locale)-->
<!-- if (!object) {-->
<!-- locale = 'en'-->
<!-- object = this.getLocaleObject(locale)-->
<!-- }-->
<!-- this.setLocale(locale)-->
<!-- this.currentLanguage = object-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- indexOfSelectedLocale() {-->
<!-- return this.locales.findIndex((element) => element.code === this.$store.state.language)-->
<!-- },-->
<!-- indexOfSecondLastLocale() {-->
<!-- return this.locales.length - 2-->
<!-- },-->
<!-- indexOfLastLocale() {-->
<!-- return this.locales.length - 1-->
<!-- },-->
<!-- },-->
<!-- created() {-->
<!-- this.setCurrentLanguage()-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { useStore } from 'vuex'
import { useI18n } from 'vue-i18n'
import { useMutation } from '@vue/apollo-composable'
import { useAppToast } from '../composables/useToast'
import locales from '@/locales/'
import { onMounted, ref, computed } from 'vue'
import { updateUserInfos } from '../graphql/mutations'
const store = useStore()
const { t, locale } = useI18n()
const { mutate } = useMutation(updateUserInfos)
const { toastSuccess, toastError } = useAppToast()
const currentLang = ref({})
const getLocaleObject = (localeCode) => {
return locales.find((lang) => lang.code === localeCode)
}
const setLocale = (newLocale) => {
store.commit('language', newLocale)
currentLang.value = getLocaleObject(newLocale)
}
const saveLocale = async (newLocale) => {
if (locale.value === newLocale) return
setLocale(newLocale)
if (store.state.gradidoID) {
try {
await mutate({ locale: newLocale })
toastSuccess(t('settings.language.success'))
} catch (error) {
toastError(t('settings.language.error'))
}
}
}
const getNavigatorLang = () => {
const navigatorLang = navigator.language
if (navigatorLang) return navigatorLang.split('-')[0]
return navigatorLang
}
const setCurrentLanguage = () => {
let newLocale = store.state.language || getNavigatorLang() || 'en'
let langObject = getLocaleObject(newLocale)
if (!langObject) {
newLocale = 'en'
langObject = getLocaleObject(newLocale)
}
setLocale(newLocale)
currentLang.value = langObject
}
const indexOfSelectedLocale = computed(() => {
return locales.findIndex((element) => element.code === store.state.language)
})
const indexOfSecondLastLocale = computed(() => {
return locales.length - 2
})
const indexOfLastLocale = computed(() => {
return locales.length - 1
})
onMounted(() => {
setCurrentLanguage()
})
</script>

View File

@ -10,6 +10,9 @@
<script>
export default {
name: 'LanguageSwitch',
props: {
language: { type: String },
},
data() {
return {
selected: null,
@ -22,12 +25,6 @@ export default {
],
}
},
props: {
language: { type: String },
},
created() {
this.selected = this.$store.state.language
},
computed: {
languageObject() {
return this.selected
@ -38,5 +35,8 @@ export default {
this.$emit('update-language', this.languageObject)
},
},
created() {
this.selected = this.$store.state.language
},
}
</script>

View File

@ -4,11 +4,11 @@
<h1 v-if="amount === ''">{{ $t('gdd_per_link.redeemlink-error') }}</h1>
<h1 v-if="isContributionLink && amount !== ''">
{{ CONFIG.COMMUNITY_NAME }}
{{ $t('contribution-link.thanksYouWith') }} {{ amount | GDD }}
{{ $t('contribution-link.thanksYouWith') }} {{ $filters.GDD(amount) }}
</h1>
<h1 v-if="!isContributionLink && amount !== ''">
{{ user.firstName }}
{{ $t('transaction-link.send_you') }} {{ amount | GDD }}
{{ $t('transaction-link.send_you') }} {{ $filters.GDD(amount) }}
</h1>
<b>{{ memo }}</b>
</b-jumbotron>

View File

@ -1,24 +1,24 @@
<template>
<div class="redeem-logged-out">
<redeem-information v-bind="linkData" :isContributionLink="isContributionLink" />
<redeem-information v-bind="linkData" :is-contribution-link="isContributionLink" />
<b-jumbotron>
<div class="mb-2">
<h2>{{ $t('gdd_per_link.redeem') }}</h2>
</div>
<b-row>
<b-col sm="12" md="6">
<BRow>
<BCol sm="12" md="6">
<p>{{ $t('gdd_per_link.no-account') }}</p>
<b-button variant="primary" :to="register">
{{ $t('gdd_per_link.to-register') }}
</b-button>
</b-col>
<b-col sm="12" md="6" class="mt-4 mt-lg-0">
</BCol>
<BCol sm="12" md="6" class="mt-4 mt-lg-0">
<p>{{ $t('gdd_per_link.has-account') }}</p>
<b-button variant="gradido" :to="login">{{ $t('gdd_per_link.to-login') }}</b-button>
</b-col>
</b-row>
</BCol>
</BRow>
</b-jumbotron>
</div>
</template>
@ -28,10 +28,10 @@ import { authLinks } from '@/mixins/authLinks'
export default {
name: 'RedeemLoggedOut',
mixins: [authLinks],
components: {
RedeemInformation,
},
mixins: [authLinks],
props: {
linkData: { type: Object, required: true },
isContributionLink: { type: Boolean, default: false },

View File

@ -1,6 +1,6 @@
<template>
<div class="redeem-self-creator">
<redeem-information v-bind="linkData" :isContributionLink="isContributionLink" />
<redeem-information v-bind="linkData" :is-contribution-link="isContributionLink" />
<b-jumbotron>
<div class="mb-3 text-center">

View File

@ -1,13 +1,13 @@
<template>
<div class="redeem-valid">
<redeem-information v-bind="linkData" :isContributionLink="isContributionLink" />
<redeem-information v-bind="linkData" :is-contribution-link="isContributionLink" />
<b-jumbotron>
<div class="mb-3 text-center">
<b-button
variant="gradido"
@click="$emit('mutation-link', linkData.amount)"
size="lg"
:disabled="!validLink"
@click="$emit('mutation-link', linkData.amount)"
>
{{ $t('gdd_per_link.redeem') }}
</b-button>

View File

@ -35,7 +35,7 @@
</router-link>
</b-navbar-nav>
</b-navbar>
<!-- <div class="alertBox">
<!-- <div class="alert-box">
<b-alert show dismissible variant="light" class="nav-alert text-dark">
<small>{{ $t('1000thanks') }}</small>
</b-alert>
@ -82,7 +82,7 @@ export default {
height: 150px;
}
.authNavbar > .nav-link {
.auth-navbar > .nav-link {
color: #383838 !important;
}
@ -90,7 +90,7 @@ export default {
font-size: 2.25rem;
}
.authNavbar > .router-link-exact-active {
.auth-navbar > .router-link-exact-active {
color: #0e79bc !important;
}
@ -103,31 +103,36 @@ button.navbar-toggler > span.navbar-toggler-icon {
left: 50%;
max-width: 64%;
}
.alertBox {
.alert-box {
left: 20%;
right: 20%;
position: absolute;
z-index: 1000;
top: 25px;
}
@media screen and (max-width: 1170px) {
@media screen and (width <= 1170px) {
.sheet-img {
left: 20%;
}
.alertBox {
.alert-box {
position: static;
margin-left: 5%;
margin-right: 5%;
z-index: 0;
}
}
@media screen and (max-width: 450px) {
@media screen and (width <= 450px) {
.navbar-element {
z-index: 1000;
position: fixed;
width: 100%;
background-color: #f5f5f5e6;
}
.sheet-img {
left: 5%;
max-width: 61%;

View File

@ -4,74 +4,74 @@
id="side-menu"
ref="sideMenu"
class="gradido-border-radius pt-2 bg-white"
:class="shadow ? 'appBoxShadow' : ''"
:class="shadow ? 'app-box-shadow' : ''"
>
<div class="mb-3 mt-3">
<b-nav vertical class="w-200">
<b-nav-item to="/overview" class="mb-3" active-class="activeRoute">
<b-img src="/img/svg/home.svg" height="20" class="svg-icon" />
<BNav vertical class="w-200">
<BNavItem to="/overview" class="mb-3" active-class="active-route">
<BImg src="/img/svg/home.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('navigation.overview') }}</span>
</b-nav-item>
<b-nav-item to="/send" class="mb-3" active-class="activeRoute">
<b-icon icon="cash" aria-hidden="true"></b-icon>
</BNavItem>
<BNavItem to="/send" class="mb-3" active-class="active-route">
<IBiCash />
<span class="ml-2">{{ $t('navigation.send') }}</span>
</b-nav-item>
<b-nav-item to="/transactions" :class="transactionClass" active-class="activeRoute">
<b-img src="/img/svg/transaction.svg" height="20" class="svg-icon" />
</BNavItem>
<BNavItem to="/transactions" :class="transactionClass" active-class="active-route">
<BImg src="/img/svg/transaction.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('navigation.transactions') }}</span>
</b-nav-item>
<b-nav-item to="/community" class="mb-3" active-class="activeRoute">
<b-img src="/img/svg/community.svg" height="20" class="svg-icon" />
</BNavItem>
<BNavItem to="/community" class="mb-3" active-class="active-route">
<BImg src="/img/svg/community.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('creation') }}</span>
</b-nav-item>
<b-nav-item to="/information" class="mb-3" active-class="activeRoute">
<b-img src="/img/svg/info.svg" height="20" class="svg-icon" />
</BNavItem>
<BNavItem to="/information" class="mb-3" active-class="active-route">
<BImg src="/img/svg/info.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('navigation.info') }}</span>
</b-nav-item>
<b-nav-item to="/circles" v-if="isHumhub" class="mb-3" active-class="activeRoute">
<b-img src="/img/svg/circles.svg" height="20" class="svg-icon" />
</BNavItem>
<BNavItem v-if="isHumhub" to="/circles" class="mb-3" active-class="active-route">
<BImg src="/img/svg/circles.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('navigation.circles') }}</span>
</b-nav-item>
<b-nav-item to="/usersearch" v-if="isGMS" active-class="activeRoute">
<b-img src="/img/loupe.png" height="20" />
</BNavItem>
<BNavItem v-if="isGMS" to="/usersearch" active-class="active-route">
<BImg src="/img/loupe.png" height="20" />
<span class="ml-2">{{ $t('navigation.usersearch') }}</span>
</b-nav-item>
</b-nav>
</BNavItem>
</BNav>
<hr class="m-3" />
<b-nav vertical class="w-100">
<b-nav-item
<BNav vertical class="w-100">
<BNavItem
to="/settings"
class="mb-3"
active-class="activeRoute"
active-class="active-route"
data-test="settings-menu"
>
<b-img src="/img/svg/settings.svg" height="20" class="svg-icon" />
<BImg src="/img/svg/settings.svg" height="20" class="svg-icon" />
<span class="ml-2">{{ $t('navigation.settings') }}</span>
<b-badge v-if="!$store.state.username" variant="warning">
<BBadge v-if="!$store.state.username" variant="warning">
{{ $t('settings.newSettings') }}
</b-badge>
</b-nav-item>
<b-nav-item
class="mb-3 text-light"
</BBadge>
</BNavItem>
<BNavItem
v-if="$store.state.roles && $store.state.roles.length > 0"
class="mb-3 text-light"
active-class="active-route"
@click="$emit('admin')"
active-class="activeRoute"
>
<b-icon icon="shield-check" aria-hidden="true"></b-icon>
<IBiShieldCheck />
<span class="ml-2">
{{ $t('navigation.admin_area') }}
</span>
</b-nav-item>
<b-nav-item
</BNavItem>
<BNavItem
class="font-weight-bold"
@click="$emit('logout')"
active-class="activeRoute"
active-class="active-route"
data-test="logout-menu"
@click="$emit('logout')"
>
<b-img src="/img/svg/logout.svg" height="20" class="svg-icon" />
<BImg src="/img/svg/logout.svg" height="20" class="svg-icon" />
<span class="ml-2 text-205">{{ $t('navigation.logout') }}</span>
</b-nav-item>
</b-nav>
</BNavItem>
</BNav>
</div>
</div>
</div>
@ -87,7 +87,7 @@ export default {
computed: {
transactionClass() {
if (this.$route.path === '/gdt') {
return 'mb-3 activeRoute'
return 'mb-3 active-route'
}
return 'mb-3'
},
@ -102,32 +102,37 @@ export default {
</script>
<style>
.nav-link {
color: rgb(56, 56, 56);
color: rgb(56 56 56);
}
.activeRoute {
.active-route {
font-weight: bold;
color: rgb(2, 2, 1);
border-left: 4px rgb(219, 129, 19) solid;
color: rgb(2 2 1);
border-left: 4px rgb(219 129 19) solid;
}
.svg-icon {
filter: brightness(1) invert(0);
}
.activeRoute .svg-icon {
.active-route .svg-icon {
filter: brightness(0) invert(0);
}
#component-sidebar {
min-width: 200px;
}
@media screen and (min-width: 1025px) {
@media screen and (width >= 1025px) {
#side-menu {
max-width: 180px;
}
#component-sidebar {
min-width: 180px;
}
}
/*
@media screen and (min-width: 1075px) {
#side-menu {

View File

@ -10,7 +10,7 @@
>
<b-img src="img/svg/lines.png" />
<div class="py-2">
<sidebar @admin="$emit('admin')" @logout="$emit('logout')" :shadow="false" />
<sidebar :shadow="false" @admin="$emit('admin')" @logout="$emit('logout')" />
</div>
</b-sidebar>
</div>

View File

@ -1,17 +1,17 @@
<template>
<div class="community-news">
<div v-for="item in News" :key="item.locale">
<b-card
<BCard
v-if="item.locale === $i18n.locale"
class="bg-white appBoxShadow gradido-border-radius"
class="bg-white app-box-shadow gradido-border-radius"
>
<b-card-body>
<b-card-title class="h2">{{ item.text }}</b-card-title>
<BCardBody>
<BCardTitle class="h2">{{ item.text }}</BCardTitle>
<div class="h3">{{ item.date }}</div>
<b-row class="my-5">
<b-col>
<BRow class="my-5">
<BCol>
{{ item.extra }}
<br />
<br />
@ -21,25 +21,25 @@
<span v-if="item.email">
<a :href="'mailto:' + item.email">{{ item.email }}</a>
</span>
</b-col>
</b-row>
</BCol>
</BRow>
<b-row class="my-5">
<b-col cols="12">
<BRow class="my-5">
<BCol cols="12">
<div class="text-lg-right">
<b-button v-if="item.url" variant="gradido" :href="item.url" target="_blank">
<BButton v-if="item.url" variant="gradido" :href="item.url" target="_blank">
{{ item.button }}
</b-button>
</BButton>
<RouterLink v-else-if="item.internUrl" to="/settings/extern">
<b-button variant="gradido">
<BButton variant="gradido">
{{ item.button }}
</b-button>
</BButton>
</RouterLink>
</div>
</b-col>
</b-row>
</b-card-body>
</b-card>
</BCol>
</BRow>
</BCardBody>
</BCard>
</div>
</div>
</template>
@ -60,6 +60,6 @@ export default {
background-position: center;
background-repeat: no-repeat;
background-size: 350px 350px;
background-image: url(/img/svg/Gradido_Blaetter_Mainpage.svg) !important;
background-image: url('/img/svg/Gradido_Blaetter_Mainpage.svg') !important;
}
</style>

View File

@ -2,7 +2,7 @@
<div class="figure-qr-code">
<div class="qrbox">
<div>
<q-r-canvas :options="options" class="canvas mb-3" id="qrcanvas" ref="canvas" />
<q-r-canvas id="qrcanvas" ref="canvas" :options="options" class="canvas mb-3" />
</div>
<a
id="download"
@ -62,6 +62,7 @@ export default {
padding: 20px;
background-color: #fff;
}
.canvas {
width: 90%;
max-width: 300px;

View File

@ -1,6 +1,6 @@
<template>
<div class="session-logout-timeout">
<b-modal
<BModal
id="modalSessionTimeOut"
class="bg-variant-danger"
hide-header-close
@ -8,8 +8,8 @@
hide-footer
no-close-on-backdrop
>
<b-card header-tag="header" footer-tag="footer">
<b-card-text>
<BCard header-tag="header" footer-tag="footer">
<BCardText>
<div class="p-3 h2">{{ $t('session.warningText') }}</div>
<div class="p-3">
{{ $t('session.lightText') }}
@ -19,85 +19,154 @@
<b>{{ tokenExpiresInSeconds }}</b>
{{ $t('time.seconds') }}
</div>
</b-card-text>
<b-row>
<b-col class="text-center">
<b-button size="lg" variant="success" @click="handleOk">
</BCardText>
<BRow>
<BCol class="text-center">
<BButton size="lg" variant="success" @click="handleOk">
{{ $t('session.extend') }}
</b-button>
</b-col>
</b-row>
</b-card>
</BButton>
</BCol>
</BRow>
</BCard>
<template #modal-footer>
<b-button size="sm" variant="danger" @click="$emit('logout')">
<BButton size="sm" variant="danger" @click="$emit('logout')">
{{ $t('navigation.logout') }}
</b-button>
<b-button size="lg" variant="success" @click="handleOk">
</BButton>
<BButton size="lg" variant="success" @click="handleOk">
{{ $t('session.extend') }}
</b-button>
</BButton>
</template>
</b-modal>
</BModal>
</div>
</template>
<script>
<!--<script>-->
<!--import { verifyLogin } from '@/graphql/queries'-->
<!--export default {-->
<!-- name: 'SessionLogoutTimeout',-->
<!-- data() {-->
<!-- return {-->
<!-- now: new Date().getTime(),-->
<!-- }-->
<!-- },-->
<!-- timers: {-->
<!-- tokenExpires: {-->
<!-- time: 15000,-->
<!-- autostart: true,-->
<!-- repeat: true,-->
<!-- immediate: true,-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- tokenExpires() {-->
<!-- this.now = new Date().getTime()-->
<!-- if (this.tokenExpiresInSeconds < 75 && this.timers.tokenExpires.time !== 1000) {-->
<!-- this.timers.tokenExpires.time = 1000-->
<!-- this.$timer.restart('tokenExpires')-->
<!-- this.$bvModal.show('modalSessionTimeOut')-->
<!-- }-->
<!-- if (this.tokenExpiresInSeconds === 0) {-->
<!-- this.$timer.stop('tokenExpires')-->
<!-- this.$emit('logout')-->
<!-- }-->
<!-- },-->
<!-- handleOk(bvModalEvent) {-->
<!-- bvModalEvent.preventDefault()-->
<!-- this.$apollo-->
<!-- .query({-->
<!-- query: verifyLogin,-->
<!-- fetchPolicy: 'network-only',-->
<!-- })-->
<!-- .then((result) => {-->
<!-- this.timers.tokenExpires.time = 15000-->
<!-- this.$timer.restart('tokenExpires')-->
<!-- this.$bvModal.hide('modalSessionTimeOut')-->
<!-- })-->
<!-- .catch(() => {-->
<!-- this.$timer.stop('tokenExpires')-->
<!-- this.$emit('logout')-->
<!-- })-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- tokenExpiresInSeconds() {-->
<!-- const remainingSecs = Math.floor(-->
<!-- (new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000,-->
<!-- )-->
<!-- return remainingSecs <= 0 ? 0 : remainingSecs-->
<!-- },-->
<!-- },-->
<!-- beforeDestroy() {-->
<!-- this.$timer.stop('tokenExpires')-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
// TODO to be checked and fixed
import { ref, computed, onBeforeUnmount } from 'vue'
import { useStore } from 'vuex'
import { useLazyQuery } from '@vue/apollo-composable'
import { useTimer } from 'vue-timer-hook' // TODO change to updated version
import { useModal } from 'bootstrap-vue-next'
import { verifyLogin } from '@/graphql/queries'
export default {
name: 'SessionLogoutTimeout',
data() {
return {
now: new Date().getTime(),
}
},
timers: {
tokenExpires: {
time: 15000,
autostart: true,
repeat: true,
immediate: true,
},
},
methods: {
tokenExpires() {
this.now = new Date().getTime()
if (this.tokenExpiresInSeconds < 75 && this.timers.tokenExpires.time !== 1000) {
this.timers.tokenExpires.time = 1000
this.$timer.restart('tokenExpires')
this.$bvModal.show('modalSessionTimeOut')
}
if (this.tokenExpiresInSeconds === 0) {
this.$timer.stop('tokenExpires')
this.$emit('logout')
}
},
handleOk(bvModalEvent) {
bvModalEvent.preventDefault()
this.$apollo
.query({
query: verifyLogin,
fetchPolicy: 'network-only',
})
.then((result) => {
this.timers.tokenExpires.time = 15000
this.$timer.restart('tokenExpires')
this.$bvModal.hide('modalSessionTimeOut')
})
.catch(() => {
this.$timer.stop('tokenExpires')
this.$emit('logout')
})
},
},
computed: {
tokenExpiresInSeconds() {
const remainingSecs = Math.floor(
(new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000,
)
return remainingSecs <= 0 ? 0 : remainingSecs
},
},
beforeDestroy() {
this.$timer.stop('tokenExpires')
},
const store = useStore()
const { result, load: verifyLoginQuery } = useLazyQuery(verifyLogin)
const { modal } = useModal()
const timer = useTimer()
const emit = defineEmits(['logout'])
const now = ref(new Date().getTime())
const tokenExpiresInSeconds = computed(() => {
const remainingSecs = Math.floor(
(new Date(store.state.tokenTime * 1000).getTime() - now.value) / 1000,
)
return remainingSecs <= 0 ? 0 : remainingSecs
})
const handleOk = async () => {
return 'OK'
}
// const tokenExpires = () => {
// now.value = new Date().getTime()
// if (tokenExpiresInSeconds.value < 75 && timer.value.tokenExpires.time !== 1000) {
// timer.value.tokenExpires.time = 1000
// restartTimer('tokenExpires')
// modal.show('modalSessionTimeOut')
// }
// if (tokenExpiresInSeconds.value === 0) {
// stopTimer('tokenExpires')
// emit('logout')
// }
// }
//
// const handleOk = (bvModalEvent) => {
// bvModalEvent.preventDefault()
// verifyLoginQuery({
// fetchPolicy: 'network-only',
// })
// .then(() => {
// timer.value.tokenExpires.time = 15000
// restartTimer('tokenExpires')
// modal.hide('modalSessionTimeOut')
// })
// .catch(() => {
// stopTimer('tokenExpires')
// emit('logout')
// })
// }
//
// // Start the timer
// startTimer('tokenExpires', {
// time: 15000,
// callback: tokenExpires,
// immediate: true,
// })
//
// onBeforeUnmount(() => {
// stopTimer('tokenExpires')
// })
</script>

View File

@ -1,23 +1,23 @@
<template>
<div class="community-member mt-3 mt-lg-0">
<div class="community-member mt-3 mt-lg-0 position-relative">
<div class="text-center bg-gradient">
<b-badge class="position-absolute mt--2 ml--5 px-3 bg-gradient">
<BBadge class="position-absolute mt--2 ml--5 px-3 bg-gradient">
{{ $t('member') }}
</b-badge>
</BBadge>
</div>
<div
class="community-member bg-white appBoxShadow gradido-border-radius p-4 border border-success"
class="community-member bg-white app-box-shadow gradido-border-radius p-4 border border-success"
>
<b-row>
<b-col cols="9">
<BRow>
<BCol cols="9">
<div class="h4">{{ $t('community.communityMember') }}</div>
<div>{{ CONFIG.COMMUNITY_NAME }}</div>
</b-col>
<b-col cols="3" align-self="end" class="border-left border-light">
<b-icon icon="people"></b-icon>
</BCol>
<BCol cols="3" align-self="end" class="border-left border-light">
<IBiPeople />
{{ totalUsers }}
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</div>
</template>

View File

@ -1,88 +1,134 @@
<template>
<div class="gdd-amount translucent-color-opacity">
<div class="text-center">
<b-badge
<BBadge
v-if="badgeShow"
class="position-absolute mt--2 ml--4 px-3 zindex1"
:class="showStatus ? 'bg-gradient' : ''"
:variant="showStatus ? '' : 'light'"
>
{{ $t('GDD') }}
</b-badge>
</BBadge>
</div>
<div
class="wallet-amount bg-white appBoxShadow gradido-border-radius p-4 border"
class="wallet-amount bg-white app-box-shadow gradido-border-radius p-4 border"
:class="
showStatus || path === '/overview'
? 'gradido-global-border-color-accent'
: 'border-light opacity-05'
"
>
<b-row>
<b-col class="h4">{{ $t('gddKonto') }}</b-col>
</b-row>
<BRow>
<BCol class="h4">{{ $t('gddKonto') }}</BCol>
</BRow>
<b-row>
<b-col cols="9">
<b-icon
icon="layers"
class="mr-3 gradido-global-border-color-accent d-none d-lg-inline"
></b-icon>
<BRow>
<BCol cols="9">
<!-- <b-icon-->
<!-- icon="layers"-->
<!-- class="mr-3 gradido-global-border-color-accent d-none d-lg-inline"-->
<!-- ></b-icon>-->
<IBiLayers />
<span v-if="hideAmount" class="font-weight-bold gradido-global-color-accent">
{{ $t('asterisks') }}
</span>
<span v-else class="font-weight-bold gradido-global-color-accent">
{{ balance | GDD }}
{{ $filters.GDD(balance) }}
</span>
</b-col>
<b-col cols="3" class="border-left border-light">
<b-icon
:icon="hideAmount ? 'eye-slash' : 'eye'"
class="mr-3 gradido-global-border-color-accent pointer hover-icon"
@click="updateHideAmountGDD"
></b-icon>
</b-col>
</b-row>
</BCol>
<BCol cols="3" class="border-left border-light">
<!-- <b-icon-->
<!-- :icon="hideAmount ? 'eye-slash' : 'eye'"-->
<!-- class="mr-3 gradido-global-border-color-accent pointer hover-icon"-->
<!-- @click="updateHideAmountGDD"-->
<!-- ></b-icon>-->
<button class="transparent-button" @click="updateHideAmountGDD">
<IBiEyeSlash v-if="hideAmount" />
<IBiEye v-else />
</button>
</BCol>
</BRow>
</div>
</div>
</template>
<script>
import { updateUserInfos } from '@/graphql/mutations'
<!--<script>-->
<!--import { updateUserInfos } from '@/graphql/mutations'-->
export default {
name: 'GddAmount',
props: {
path: { type: String, required: false, default: '' },
balance: { type: Number, required: true },
badgeShow: { type: Boolean, default: true },
showStatus: { type: Boolean, default: false },
},
computed: {
hideAmount() {
return this.$store.state.hideAmountGDD
},
},
methods: {
async updateHideAmountGDD() {
this.$apollo
.mutate({
mutation: updateUserInfos,
variables: {
hideAmountGDD: !this.hideAmount,
},
})
.then(() => {
this.$store.commit('hideAmountGDD', !this.hideAmount)
if (!this.hideAmount) {
this.toastSuccess(this.$t('settings.showAmountGDD'))
} else {
this.toastSuccess(this.$t('settings.hideAmountGDD'))
}
})
.catch((error) => {
this.toastError(error.message)
})
},
},
<!--export default {-->
<!-- name: 'GddAmount',-->
<!-- props: {-->
<!-- path: { type: String, required: false, default: '' },-->
<!-- balance: { type: Number, required: true },-->
<!-- badgeShow: { type: Boolean, default: true },-->
<!-- showStatus: { type: Boolean, default: false },-->
<!-- },-->
<!-- computed: {-->
<!-- hideAmount() {-->
<!-- return this.$store.state.hideAmountGDD-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- async updateHideAmountGDD() {-->
<!-- this.$apollo-->
<!-- .mutate({-->
<!-- mutation: updateUserInfos,-->
<!-- variables: {-->
<!-- hideAmountGDD: !this.hideAmount,-->
<!-- },-->
<!-- })-->
<!-- .then(() => {-->
<!-- this.$store.commit('hideAmountGDD', !this.hideAmount)-->
<!-- if (!this.hideAmount) {-->
<!-- this.toastSuccess(this.$t('settings.showAmountGDD'))-->
<!-- } else {-->
<!-- this.toastSuccess(this.$t('settings.hideAmountGDD'))-->
<!-- }-->
<!-- })-->
<!-- .catch((error) => {-->
<!-- this.toastError(error.message)-->
<!-- })-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useMutation } from '@vue/apollo-composable'
import { useI18n } from 'vue-i18n'
import { updateUserInfos } from '@/graphql/mutations'
import { useAppToast } from '../../../composables/useToast'
const props = defineProps({
path: { type: String, required: false, default: '' },
balance: { type: Number, required: true },
badgeShow: { type: Boolean, default: true },
showStatus: { type: Boolean, default: false },
})
const store = useStore()
const { mutate } = useMutation(updateUserInfos)
const { t } = useI18n()
const { toastSuccess, toastError } = useAppToast()
const hideAmount = computed(() => store.state.hideAmountGDD)
const updateHideAmountGDD = async () => {
try {
await mutate({
hideAmountGDD: !hideAmount.value,
})
store.commit('hideAmountGDD', !hideAmount.value)
if (!hideAmount.value) {
toastSuccess(t('settings.showAmountGDD'))
} else {
toastSuccess(t('settings.hideAmountGDD'))
}
} catch (error) {
toastError(error.message)
}
}
</script>

View File

@ -1,82 +1,127 @@
<template>
<div class="gdt-amount mt-3 mt-lg-0">
<div class="text-center">
<b-badge
<BBadge
v-if="badgeShow"
class="position-absolute mt--2 ml--4 px-3 zindex1"
:class="showStatus ? 'bg-gradient' : ''"
:variant="showStatus ? '' : 'light'"
>
{{ $t('GDT') }}
</b-badge>
</BBadge>
</div>
<div
class="wallet-amount bg-white appBoxShadow gradido-border-radius p-4 border"
class="wallet-amount bg-white app-box-shadow gradido-border-radius p-4 border"
:class="showStatus ? 'gradido-global-border-color-accent' : 'border-light opacity-05'"
>
<b-row>
<b-col class="h4">{{ $t('gdt.gdtKonto') }}</b-col>
</b-row>
<b-row>
<b-col cols="9">
<b-icon
icon="layers"
class="mr-3 gradido-global-border-color-accent d-none d-lg-inline"
></b-icon>
<BRow>
<BCol class="h4">{{ $t('gdt.gdtKonto') }}</BCol>
</BRow>
<BRow>
<BCol cols="9">
<!-- <b-icon-->
<!-- icon="layers"-->
<!-- class="mr-3 gradido-global-border-color-accent d-none d-lg-inline"-->
<!-- ></b-icon>-->
<IBiLayers />
<span v-if="hideAmount" class="font-weight-bold gradido-global-color-accent">
{{ $t('asterisks') }}
{{ t('asterisks') }}
</span>
<span v-else class="font-weight-bold gradido-global-color-accent">
{{ $n(GdtBalance, 'decimal') }} {{ $t('GDT') }}
{{ n(gdtBalance, 'decimal') }} {{ t('GDT') }}
</span>
</b-col>
<b-col cols="3" class="border-left border-light">
<b-icon
:icon="hideAmount ? 'eye-slash' : 'eye'"
class="mr-3 gradido-global-border-color-accent pointer hover-icon"
@click="updateHideAmountGDT"
></b-icon>
</b-col>
</b-row>
</BCol>
<BCol cols="3" class="border-left border-light">
<!-- <b-icon-->
<!-- :icon="hideAmount ? 'eye-slash' : 'eye'"-->
<!-- class="mr-3 gradido-global-border-color-accent pointer hover-icon"-->
<!-- @click="updateHideAmountGDT"-->
<!-- ></b-icon>-->
<button class="transparent-button" @click="updateHideAmountGDT">
<IBiEyeSlash v-if="hideAmount" />
<IBiEye v-else />
</button>
</BCol>
</BRow>
</div>
</div>
</template>
<script>
import { updateUserInfos } from '@/graphql/mutations'
<!--<script>-->
<!--import { updateUserInfos } from '@/graphql/mutations'-->
export default {
name: 'GdtAmount',
props: {
GdtBalance: { type: Number, required: true },
badgeShow: { type: Boolean, default: true },
showStatus: { type: Boolean, default: false },
},
computed: {
hideAmount() {
return this.$store.state.hideAmountGDT
},
},
methods: {
async updateHideAmountGDT() {
this.$apollo
.mutate({
mutation: updateUserInfos,
variables: {
hideAmountGDT: !this.hideAmount,
},
})
.then(() => {
this.$store.commit('hideAmountGDT', !this.hideAmount)
if (!this.hideAmount) {
this.toastSuccess(this.$t('settings.showAmountGDT'))
} else {
this.toastSuccess(this.$t('settings.hideAmountGDT'))
}
})
.catch((error) => {
this.toastError(error.message)
})
},
},
<!--export default {-->
<!-- name: 'GdtAmount',-->
<!-- props: {-->
<!-- GdtBalance: { type: Number, required: true },-->
<!-- badgeShow: { type: Boolean, default: true },-->
<!-- showStatus: { type: Boolean, default: false },-->
<!-- },-->
<!-- computed: {-->
<!-- hideAmount() {-->
<!-- return this.$store.state.hideAmountGDT-->
<!-- },-->
<!-- },-->
<!-- methods: {-->
<!-- async updateHideAmountGDT() {-->
<!-- this.$apollo-->
<!-- .mutate({-->
<!-- mutation: updateUserInfos,-->
<!-- variables: {-->
<!-- hideAmountGDT: !this.hideAmount,-->
<!-- },-->
<!-- })-->
<!-- .then(() => {-->
<!-- this.$store.commit('hideAmountGDT', !this.hideAmount)-->
<!-- if (!this.hideAmount) {-->
<!-- this.toastSuccess(this.$t('settings.showAmountGDT'))-->
<!-- } else {-->
<!-- this.toastSuccess(this.$t('settings.hideAmountGDT'))-->
<!-- }-->
<!-- })-->
<!-- .catch((error) => {-->
<!-- this.toastError(error.message)-->
<!-- })-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useMutation } from '@vue/apollo-composable'
import { useI18n } from 'vue-i18n'
import { updateUserInfos } from '@/graphql/mutations'
import { useAppToast } from '../../../composables/useToast'
const props = defineProps({
gdtBalance: { type: Number, required: true },
badgeShow: { type: Boolean, default: true },
showStatus: { type: Boolean, default: false },
})
const store = useStore()
const { mutate } = useMutation(updateUserInfos)
const { t, n } = useI18n()
const { toastSuccess, toastError } = useAppToast()
const hideAmount = computed(() => store.state.hideAmountGDT)
const updateHideAmountGDT = async () => {
try {
await mutate({
hideAmountGDT: !hideAmount.value,
})
store.commit('hideAmountGDT', !hideAmount.value)
if (!hideAmount.value) {
toastSuccess(t('settings.showAmountGDT'))
} else {
toastSuccess(t('settings.hideAmountGDT'))
}
} catch (error) {
toastError(error.message)
}
}
</script>

View File

@ -1,18 +1,38 @@
<template>
<div class="nav-community">
<div class="bg-209 rounded-26 d-flex bd-highlight mx-xl-6 mx-lg-5 shadow">
<b-btn to="contribute" active-class="btn-active svg-icon-active" block variant="link">
<div
class="nav-community__btn-wrapper bg-209 rounded-26 d-flex bd-highlight mx-xl-6 mx-lg-5 shadow justify-content-between"
>
<BButton
to="contribute"
active-class="btn-active svg-icon-active"
block
variant="link"
class="nav-community__btn"
>
<b-img src="/img/svg/write.svg" height="20" class="svg-icon" />
{{ $t('community.submitContribution') }}
</b-btn>
<b-btn to="contributions" active-class="btn-active svg-icon-active" block variant="link">
</BButton>
<BButton
to="contributions"
active-class="btn-active svg-icon-active"
block
variant="link"
class="nav-community__btn"
>
<b-img src="/img/svg/my_profil.svg" height="20" class="svg-icon" />
{{ $t('community.myContributions') }}
</b-btn>
<b-btn to="community" active-class="btn-active svg-icon-active" block variant="link">
</BButton>
<BButton
to="community"
active-class="btn-active svg-icon-active"
block
variant="link"
class="nav-community__btn"
>
<b-img src="/img/svg/community.svg" height="20" class="svg-icon" />
{{ $t('community.community') }}
</b-btn>
</BButton>
</div>
</div>
</template>
@ -21,26 +41,33 @@ export default {
name: 'NavCommunity',
}
</script>
<style scoped>
.btn {
background-color: rgb(209, 209, 209);
color: black;
padding-right: 0px;
padding-left: 0px;
}
.btn-block + .btn-block {
margin-top: 0rem;
<style scoped lang="scss">
.nav-community__btn-wrapper {
> :deep(*) {
width: calc(100% / 3);
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
font-size: 14px;
text-wrap: nowrap;
color: black;
}
}
.svg-icon {
:deep(.svg-icon) {
filter: brightness(0) invert(0);
}
.btn-active {
:deep(.router-link-active) {
background-color: rgb(23 141 129);
color: white;
font-weight: bold;
padding: 0.625rem 1.25rem;
border-radius: 25px;
}
.btn-active .svg-icon {
:deep(.router-link-active .svg-icon) {
filter: brightness(0) invert(1);
}
</style>

View File

@ -1,11 +1,11 @@
<template>
<div class="last-contributions d-none d-lg-block">
<b-row class="mb-5">
<b-col class="h3">{{ $t('contribution.lastContribution') }}</b-col>
<b-col cols="1" class="text-right">
<BRow class="mb-5">
<BCol class="h3">{{ $t('contribution.lastContribution') }}</BCol>
<BCol cols="1" class="text-right">
<b-icon icon="three-dots-vertical"></b-icon>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</template>
<script>

View File

@ -1,24 +1,24 @@
<template>
<div class="rightside-last-transactions d-none d-lg-block">
<b-row class="mb-3">
<b-col class="h3">{{ $t('transaction.lastTransactions') }}</b-col>
<!-- <b-col cols="1" class="text-right">
<BRow class="mb-3">
<BCol class="h3">{{ $t('transaction.lastTransactions') }}</BCol>
<!-- <BCol cols="1" class="text-right">
<b-icon icon="three-dots-vertical"></b-icon>
</b-col> -->
</b-row>
</BCol> -->
</BRow>
<div v-for="(transaction, index) in transactions" :key="transaction.id">
<b-row
align-v="center"
<BRow
v-if="
index <= 8 &&
transaction.typeId !== 'DECAY' &&
transaction.typeId !== 'LINK_SUMMARY' &&
transaction.typeId !== 'CREATION'
"
align-v="center"
class="mb-4"
>
<b-col cols="auto">
<BCol cols="auto">
<div class="align-items-center">
<avatar
:size="72"
@ -27,25 +27,25 @@
:initials="`${transaction.linkedUser.firstName[0]}${transaction.linkedUser.lastName[0]}`"
></avatar>
</div>
</b-col>
<b-col class="p-1">
<b-row>
<b-col>
</BCol>
<BCol class="p-1">
<BRow>
<BCol>
<div class="font-weight-bold">
<name :linkedUser="transaction.linkedUser" fontColor="text-dark" />
<name :linked-user="transaction.linkedUser" font-color="text-dark" />
</div>
<div class="d-flex mt-3">
<div class="small">
{{ transaction.amount | GDD }}
{{ $filters.GDD(transaction.amount) }}
</div>
<div class="small ml-3 text-right">
{{ $d(new Date(transaction.balanceDate), 'short') }}
</div>
</div>
</b-col>
</b-row>
</b-col>
</b-row>
</BCol>
</BRow>
</BCol>
</BRow>
</div>
</div>
</template>
@ -62,6 +62,7 @@ export default {
props: {
transactions: {
default: () => [],
type: Array,
},
transactionCount: { type: Number, default: 0 },
transactionLinkCount: { type: Number, default: 0 },

View File

@ -1,22 +1,22 @@
<template>
<div class="gdt-transaction-list">
<div class="list-group bg-white appBoxShadow gradido-border-radius p-3 mb-3">
<b-row @click="visible = !visible" class="align-items-center">
<b-col cols="3" lg="2" md="2">
<b-avatar
<div class="list-group bg-white app-box-shadow gradido-border-radius p-3 mb-3">
<BRow class="align-items-center" @click="visible = !visible">
<BCol cols="3" lg="2" md="2">
<BAvatar
:icon="getLinesByType.icon"
variant="light"
size="3em"
:class="getLinesByType.iconclasses"
></b-avatar>
</b-col>
<b-col>
/>
</BCol>
<BCol>
<!-- <div>
{{ getLinesByType }}
</div> -->
<div>
<span class="small">{{ this.$d(new Date(date), 'short') }}</span>
<span class="small ml-3">{{ this.$d(new Date(date), 'time') }}</span>
<span class="small">{{ $d(new Date(date), 'short') }}</span>
<span class="small ml-3">{{ $d(new Date(date), 'time') }}</span>
</div>
<div>
{{ getLinesByType.description }}
@ -24,111 +24,198 @@
<div class="small">
{{ getLinesByType.descriptiontext }}
</div>
</b-col>
<b-col cols="8" lg="3" md="3" sm="8" offset="3" offset-md="0" offset-lg="0">
</BCol>
<BCol cols="8" lg="3" md="3" sm="8" offset="3" offset-md="0" offset-lg="0">
<div class="small mb-2">{{ $t('gdt.credit') }}</div>
<div class="font-weight-bold">{{ getLinesByType.credittext }}</div>
</b-col>
<b-col cols="12" md="1" lg="1" class="text-right">
</BCol>
<BCol cols="12" md="1" lg="1" class="text-right">
<collapse-icon class="text-right" :visible="visible" />
</b-col>
</b-row>
</BCol>
</BRow>
<b-collapse :id="collapseId" class="mt-2" v-model="visible">
<BCollapse :id="collapseId" v-model="visible" class="mt-2">
<transaction-collapse
:amount="amount"
:gdtEntryType="gdtEntryType"
:gdt-entry-type="gdtEntryType"
:factor="factor"
:gdt="gdt"
></transaction-collapse>
</b-collapse>
</BCollapse>
</div>
</div>
</template>
<script>
<!--<script>-->
<!--import CollapseIcon from './TransactionRows/CollapseIcon'-->
<!--import TransactionCollapse from './TransactionCollapse'-->
<!--import { GdtEntryType } from '../graphql/enums'-->
<!--export default {-->
<!-- name: 'Transaction',-->
<!-- components: {-->
<!-- CollapseIcon,-->
<!-- TransactionCollapse,-->
<!-- },-->
<!-- props: {-->
<!-- amount: { type: Number },-->
<!-- date: { type: String },-->
<!-- comment: { type: String },-->
<!-- gdtEntryType: { type: String, default: GdtEntryType.FORM },-->
<!-- factor: { type: Number },-->
<!-- gdt: { type: Number },-->
<!-- id: { type: Number },-->
<!-- },-->
<!-- data() {-->
<!-- return {-->
<!-- collapseStatus: [],-->
<!-- visible: false,-->
<!-- }-->
<!-- },-->
<!-- methods: {-->
<!-- getCollapseState(id) {-->
<!-- return this.collapseStatus.includes('gdt-collapse-' + id)-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- collapseId() {-->
<!-- return 'gdt-collapse-' + String(this.id)-->
<!-- },-->
<!-- isGlobalModificator() {-->
<!-- return this.gdtEntryType === GdtEntryType.GLOBAL_MODIFICATOR-->
<!-- },-->
<!-- getLinesByType() {-->
<!-- switch (this.gdtEntryType) {-->
<!-- case GdtEntryType.FORM:-->
<!-- case GdtEntryType.CVS:-->
<!-- case GdtEntryType.ELOPAGE:-->
<!-- case GdtEntryType.DIGISTORE:-->
<!-- case GdtEntryType.CVS2: {-->
<!-- return {-->
<!-- icon: 'heart',-->
<!-- iconclasses: 'gradido-global-color-accent',-->
<!-- description: this.$t('gdt.contribution'),-->
<!-- descriptiontext: this.$n(this.amount, 'decimal') + ' €',-->
<!-- credittext: this.$n(this.gdt, 'decimal') + ' GDT',-->
<!-- }-->
<!-- }-->
<!-- case GdtEntryType.ELOPAGE_PUBLISHER: {-->
<!-- return {-->
<!-- icon: 'person-check',-->
<!-- iconclasses: 'gradido-global-color-accent',-->
<!-- description: this.$t('gdt.recruited-member'),-->
<!-- descriptiontext: '5%',-->
<!-- credittext: this.$n(this.amount, 'decimal') + ' GDT',-->
<!-- }-->
<!-- }-->
<!-- case GdtEntryType.GLOBAL_MODIFICATOR: {-->
<!-- return {-->
<!-- icon: 'gift',-->
<!-- iconclasses: 'gradido-global-color-accent',-->
<!-- description: this.$t('gdt.gdt-received'),-->
<!-- descriptiontext: this.comment,-->
<!-- credittext: this.$n(this.gdt, 'decimal') + ' GDT',-->
<!-- }-->
<!-- }-->
<!-- default:-->
<!-- throw new Error('no lines for this type: ' + this.gdtEntryType)-->
<!-- }-->
<!-- },-->
<!-- },-->
<!-- mounted() {-->
<!-- this.$root.$on('bv::collapse::state', (collapseId, isJustShown) => {-->
<!-- if (isJustShown) {-->
<!-- this.collapseStatus.push(collapseId)-->
<!-- } else {-->
<!-- this.collapseStatus = this.collapseStatus.filter((id) => id !== collapseId)-->
<!-- }-->
<!-- })-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import CollapseIcon from './TransactionRows/CollapseIcon'
import TransactionCollapse from './TransactionCollapse'
import { GdtEntryType } from '../graphql/enums'
export default {
name: 'Transaction',
components: {
CollapseIcon,
TransactionCollapse,
},
props: {
amount: { type: Number },
date: { type: String },
comment: { type: String },
gdtEntryType: { type: String, default: GdtEntryType.FORM },
factor: { type: Number },
gdt: { type: Number },
id: { type: Number },
},
data() {
return {
collapseStatus: [],
visible: false,
}
},
methods: {
getCollapseState(id) {
return this.collapseStatus.includes('gdt-collapse-' + id)
},
},
computed: {
collapseId() {
return 'gdt-collapse-' + String(this.id)
},
isGlobalModificator() {
return this.gdtEntryType === GdtEntryType.GLOBAL_MODIFICATOR
},
getLinesByType() {
switch (this.gdtEntryType) {
case GdtEntryType.FORM:
case GdtEntryType.CVS:
case GdtEntryType.ELOPAGE:
case GdtEntryType.DIGISTORE:
case GdtEntryType.CVS2: {
return {
icon: 'heart',
iconclasses: 'gradido-global-color-accent',
description: this.$t('gdt.contribution'),
descriptiontext: this.$n(this.amount, 'decimal') + ' €',
credittext: this.$n(this.gdt, 'decimal') + ' GDT',
}
}
case GdtEntryType.ELOPAGE_PUBLISHER: {
return {
icon: 'person-check',
iconclasses: 'gradido-global-color-accent',
description: this.$t('gdt.recruited-member'),
descriptiontext: '5%',
credittext: this.$n(this.amount, 'decimal') + ' GDT',
}
}
case GdtEntryType.GLOBAL_MODIFICATOR: {
return {
icon: 'gift',
iconclasses: 'gradido-global-color-accent',
description: this.$t('gdt.gdt-received'),
descriptiontext: this.comment,
credittext: this.$n(this.gdt, 'decimal') + ' GDT',
}
}
default:
throw new Error('no lines for this type: ' + this.gdtEntryType)
}
},
},
mounted() {
this.$root.$on('bv::collapse::state', (collapseId, isJustShown) => {
if (isJustShown) {
this.collapseStatus.push(collapseId)
} else {
this.collapseStatus = this.collapseStatus.filter((id) => id !== collapseId)
}
})
// Props
const props = defineProps({
amount: Number,
date: String,
comment: String,
gdtEntryType: {
type: String,
default: GdtEntryType.FORM,
},
factor: Number,
gdt: Number,
id: Number,
})
// Reactive state
const collapseStatus = ref([])
const visible = ref(false)
const { t, n } = useI18n()
const getCollapseState = (id) => {
return collapseStatus.value.includes('gdt-collapse-' + id)
}
const collapseId = computed(() => 'gdt-collapse-' + String(props.id))
const isGlobalModificator = computed(() => props.gdtEntryType === GdtEntryType.GLOBAL_MODIFICATOR)
const getLinesByType = computed(() => {
switch (props.gdtEntryType) {
case GdtEntryType.FORM:
case GdtEntryType.CVS:
case GdtEntryType.ELOPAGE:
case GdtEntryType.DIGISTORE:
case GdtEntryType.CVS2: {
return {
icon: 'heart',
iconclasses: 'gradido-global-color-accent',
description: t('gdt.contribution'),
descriptiontext: n(props.amount, 'decimal') + ' €',
credittext: n(props.gdt, 'decimal') + ' GDT',
}
}
case GdtEntryType.ELOPAGE_PUBLISHER: {
return {
icon: 'person-check',
iconclasses: 'gradido-global-color-accent',
description: t('gdt.recruited-member'),
descriptiontext: '5%',
credittext: n(props.amount, 'decimal') + ' GDT',
}
}
case GdtEntryType.GLOBAL_MODIFICATOR: {
return {
icon: 'gift',
iconclasses: 'gradido-global-color-accent',
description: t('gdt.gdt-received'),
descriptiontext: props.comment,
credittext: n(props.gdt, 'decimal') + ' GDT',
}
}
default:
throw new Error('no lines for this type: ' + props.gdtEntryType)
}
})
onMounted(() => {
// Note: This event listener setup might need to be adjusted for Vue 3
// You might want to use provide/inject or a global event bus instead
// const root = getCurrentInstance().appContext.config.globalProperties
// root.$on('bv::collapse::state', (collapseId, isJustShown) => {
// if (isJustShown) {
// collapseStatus.value.push(collapseId)
// } else {
// collapseStatus.value = collapseStatus.value.filter((id) => id !== collapseId)
// }
// })
})
</script>

View File

@ -1,26 +1,26 @@
<template>
<div class="gdt-transaction-collapse py-4 mb-4 gradido-no-border">
<b-row class="gdt-list-collapse-header-text mb-3">
<b-col class="collapse-headline">
<BRow class="gdt-list-collapse-header-text mb-3">
<BCol class="collapse-headline">
<b>{{ getLinesByType.headline }}</b>
</b-col>
</b-row>
<b-row class="gdt-list-collapse-box--all">
<b-col cols="12" lg="4" md="4">
</BCol>
</BRow>
<BRow class="gdt-list-collapse-box--all">
<BCol cols="12" lg="4" md="4">
<div class="collapse-first">{{ getLinesByType.first }}</div>
<div class="collapse-second">{{ getLinesByType.second }}</div>
</b-col>
<b-col offset="1" offset-md="0" offset-lg="0">
</BCol>
<BCol offset="1" offset-md="0" offset-lg="0">
<div class="collapse-firstMath">{{ getLinesByType.firstMath }}</div>
<div class="collapse-secondMath">
{{ getLinesByType.secondMath }}
</div>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</template>
<script>
import { GdtEntryType } from '../graphql/enums'
import { GdtEntryType } from '@/graphql/enums'
export default {
name: 'TransactionCollapse',

View File

@ -1,74 +1,131 @@
<template>
<div class="transaction-link gradido-custom-background">
<b-row :class="validLink ? '' : 'bg-muted text-light'" class="mb-2 pt-2 pb-2">
<b-col cols="1">
<BRow :class="validLink ? '' : 'bg-muted text-light'" class="mb-2 pt-2 pb-2">
<BCol cols="1">
<type-icon color="text-danger" icon="link45deg" class="pt-4 pl-2" />
</b-col>
<b-col cols="11">
<b-row>
<b-col>
</BCol>
<BCol cols="11">
<BRow>
<BCol>
<amount-and-name-row :amount="amount" :text="$t('form.amount')" />
<memo-row :memo="memo" />
<date-row :date="validUntil" :diffNow="true" :validLink="validLink" />
<date-row :date="validUntil" :diff-now="true" :valid-link="validLink" />
<decay-row :decay="decay" />
</b-col>
<b-col cols="12" lg="1" md="1" class="text-center text-md-right pr-5 pr-lg-4">
<b-dropdown no-caret right aria-expanded="false" size="sm">
</BCol>
<BCol cols="12" lg="1" md="1" class="text-center text-md-right pr-5 pr-lg-4">
<BDropdown no-caret right aria-expanded="false" size="sm">
<template #button-content>
<b-icon icon="three-dots-vertical"></b-icon>
<IBiThreeDotsVertical />
</template>
<b-dropdown-item v-if="validLink" class="test-copy-link" @click="copyLink">
<b-icon icon="clipboard"></b-icon>
<BDropdownItem v-if="validLink" class="test-copy-link" @click="copyLink">
<IBiClipboard />
{{ $t('gdd_per_link.copy-link') }}
</b-dropdown-item>
<b-dropdown-item
v-if="validLink"
class="test-copy-text pt-3"
@click="copyLinkWithText"
>
<b-icon icon="clipboard-plus"></b-icon>
</BDropdownItem>
<BDropdownItem v-if="validLink" class="test-copy-text pt-3" @click="copyLinkWithText">
<IBiClipboardPlus />
{{ $t('gdd_per_link.copy-link-with-text') }}
</b-dropdown-item>
<b-dropdown-item
</BDropdownItem>
<BDropdownItem
v-if="validLink"
@click="$bvModal.show('modalPopover-' + id)"
class="pt-3 pb-3 test-qr-code"
@click="$bvModal.show('modalPopover-' + id)"
>
<b-img src="img/svg/qr-code.svg" width="18" class="filter"></b-img>
{{ $t('qrCode') }}
</b-dropdown-item>
<b-dropdown-item class="test-delete-link" @click="deleteLink()">
<b-icon icon="trash"></b-icon>
</BDropdownItem>
<BDropdownItem class="test-delete-link" @click="deleteLink()">
<IBiTrash />
{{ $t('delete') }}
</b-dropdown-item>
</b-dropdown>
</b-col>
</b-row>
</b-col>
</b-row>
<b-modal :id="'modalPopover-' + id" ok-only hide-header-close>
<b-card header-tag="header" footer-tag="footer">
</BDropdownItem>
</BDropdown>
</BCol>
</BRow>
</BCol>
</BRow>
<BModal :id="'modalPopover-' + id" ok-only hide-header-close>
<BCard header-tag="header" footer-tag="footer">
<template #header>
<h6 class="mb-0">{{ $t('qrCode') }}</h6>
</template>
<b-card-text><figure-qr-code class="text-center" :link="link" /></b-card-text>
<BCardText><figure-qr-code class="text-center" :link="link" /></BCardText>
<template #footer>
<em>{{ link }}</em>
</template>
</b-card>
</b-modal>
<b-modal :id="'modalPopoverCopyError' + id" ok-only hide-header-close>
<b-card header-tag="header" footer-tag="footer">
<b-card-text>
</BCard>
</BModal>
<BModal :id="'modalPopoverCopyError' + id" ok-only hide-header-close>
<BCard header-tag="header" footer-tag="footer">
<BCardText>
<div class="alert-danger p-3">{{ $t('gdd_per_link.not-copied') }}</div>
<div class="alert-muted h3 p-3">{{ link }}</div>
</b-card-text>
</b-card>
</b-modal>
</BCardText>
</BCard>
</BModal>
</div>
</template>
<script>
<!--<script>-->
<!--import { deleteTransactionLink } from '@/graphql/mutations'-->
<!--import TypeIcon from '../TransactionRows/TypeIcon'-->
<!--import AmountAndNameRow from '../TransactionRows/AmountAndNameRow'-->
<!--import MemoRow from '../TransactionRows/MemoRow'-->
<!--import DateRow from '../TransactionRows/DateRow'-->
<!--import DecayRow from '../TransactionRows/DecayRow'-->
<!--import FigureQrCode from '@/components/QrCode/FigureQrCode'-->
<!--import { copyLinks } from '../../mixins/copyLinks'-->
<!--export default {-->
<!-- name: 'TransactionLink',-->
<!-- mixins: [copyLinks],-->
<!-- components: {-->
<!-- TypeIcon,-->
<!-- AmountAndNameRow,-->
<!-- MemoRow,-->
<!-- DateRow,-->
<!-- DecayRow,-->
<!-- FigureQrCode,-->
<!-- },-->
<!-- props: {-->
<!-- holdAvailableAmount: { type: String, required: true },-->
<!-- id: { type: Number, required: true },-->
<!-- },-->
<!-- methods: {-->
<!-- deleteLink() {-->
<!-- this.$bvModal.msgBoxConfirm(this.$t('gdd_per_link.delete-the-link')).then(async (value) => {-->
<!-- if (value)-->
<!-- await this.$apollo-->
<!-- .mutate({-->
<!-- mutation: deleteTransactionLink,-->
<!-- variables: {-->
<!-- id: this.id,-->
<!-- },-->
<!-- })-->
<!-- .then(() => {-->
<!-- this.toastSuccess(this.$t('gdd_per_link.deleted'))-->
<!-- this.$emit('reset-transaction-link-list')-->
<!-- })-->
<!-- .catch((err) => {-->
<!-- this.toastError(err.message)-->
<!-- })-->
<!-- })-->
<!-- },-->
<!-- },-->
<!-- computed: {-->
<!-- decay() {-->
<!-- return `${this.amount - this.holdAvailableAmount}`-->
<!-- },-->
<!-- validLink() {-->
<!-- return new Date(this.validUntil) > new Date()-->
<!-- },-->
<!-- },-->
<!--}-->
<!--</script>-->
<script setup>
import { computed } from 'vue'
import { useMutation } from '@vue/apollo-composable'
import { useI18n } from 'vue-i18n'
import { useAppToast } from '@/composables/useToast'
import { useCopyLinks } from '@/composables/useCopyLinks'
import { deleteTransactionLink } from '@/graphql/mutations'
import TypeIcon from '../TransactionRows/TypeIcon'
import AmountAndNameRow from '../TransactionRows/AmountAndNameRow'
@ -76,59 +133,61 @@ import MemoRow from '../TransactionRows/MemoRow'
import DateRow from '../TransactionRows/DateRow'
import DecayRow from '../TransactionRows/DecayRow'
import FigureQrCode from '@/components/QrCode/FigureQrCode'
import { copyLinks } from '../../mixins/copyLinks'
import { useModal } from 'bootstrap-vue-next'
export default {
name: 'TransactionLink',
mixins: [copyLinks],
components: {
TypeIcon,
AmountAndNameRow,
MemoRow,
DateRow,
DecayRow,
FigureQrCode,
},
props: {
holdAvailableAmount: { type: String, required: true },
id: { type: Number, required: true },
},
methods: {
deleteLink() {
this.$bvModal.msgBoxConfirm(this.$t('gdd_per_link.delete-the-link')).then(async (value) => {
if (value)
await this.$apollo
.mutate({
mutation: deleteTransactionLink,
variables: {
id: this.id,
},
})
.then(() => {
this.toastSuccess(this.$t('gdd_per_link.deleted'))
this.$emit('reset-transaction-link-list')
})
.catch((err) => {
this.toastError(err.message)
})
})
},
},
computed: {
decay() {
return `${this.amount - this.holdAvailableAmount}`
},
validLink() {
return new Date(this.validUntil) > new Date()
},
},
const props = defineProps({
holdAvailableAmount: { type: String, required: true },
id: { type: Number, required: true },
amount: { type: Number, required: true },
validUntil: { type: String, required: true },
link: { type: String, required: true },
memo: { type: String, required: true },
})
const emit = defineEmits(['reset-transaction-link-list'])
const { t } = useI18n()
const { toastSuccess, toastError } = useAppToast()
const { copyToClipboard } = useCopyLinks({
amount: props.amount,
validUntil: props.validUntil,
link: props.link,
memo: props.memo,
})
const { showConfirmModal } = useModal()
const { mutate: deleteTransactionLinkMutation } = useMutation(deleteTransactionLink)
const decay = computed(() => `${props.amount - props.holdAvailableAmount}`)
const validLink = computed(() => new Date(props.validUntil) > new Date())
async function deleteLink() {
const confirmed = await showConfirmModal(t('gdd_per_link.delete-the-link'))
if (confirmed) {
try {
await deleteTransactionLinkMutation({ id: props.id })
toastSuccess(t('gdd_per_link.deleted'))
emit('reset-transaction-link-list')
} catch (err) {
toastError(err.message)
}
}
}
// Expose necessary methods and computed properties
// defineExpose({
// deleteLink,
// decay,
// validLink,
// copyToClipboard,
// })
</script>
<style>
.qr-button {
position: relative;
right: 20px;
}
.filter {
filter: opacity(0.6);
}

View File

@ -1,19 +1,19 @@
<template>
<div class="amount-and-name-row">
<b-row>
<b-col cols="5">
<BRow>
<BCol cols="5">
<div class="text-right">
<span class="gdd-transaction-list-item-amount">
{{ amount | GDD }}
{{ $filters.GDD(amount) }}
</span>
</div>
</b-col>
<b-col cols="7">
</BCol>
<BCol cols="7">
<div class="gdd-transaction-list-item-name">
<span>{{ text }}</span>
</div>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</template>
<script>

View File

@ -1,10 +1,12 @@
<template>
<div class="collapse-icon">
<b-icon
:icon="visible ? 'arrow-up-circle' : 'arrow-down-circle'"
:class="visible ? 'text-black' : 'text-muted'"
class="h1"
/>
<!-- <b-icon-->
<!-- :icon="visible ? 'arrow-up-circle' : 'arrow-down-circle'"-->
<!-- :class="visible ? 'text-black' : 'text-muted'"-->
<!-- class="h1"-->
<!-- />-->
<IBiArrowUpCircle v-if="visible" class="text-black h1" />
<IBiArrowDownCircle v-else class="text-muted h1" />
</div>
</template>
<script>

View File

@ -1,17 +1,17 @@
<template>
<div class="date-row">
<b-row>
<b-col cols="5">
<BRow>
<BCol cols="5">
<div class="text-right">
{{ text }}
</div>
</b-col>
<b-col cols="7">
</BCol>
<BCol cols="7">
<div class="gdd-transaction-list-item-date">
{{ $d(new Date(this.date), 'long') }}
{{ $d(new Date(date), 'long') }}
</div>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</template>
<script>

View File

@ -1,17 +1,18 @@
<template>
<div class="decay-row">
<b-row v-if="decay">
<b-col cols="5">
<BRow v-if="decay">
<BCol cols="5">
<div class="text-right">
<b-icon icon="droplet-half" height="15" class="mb-1" />
<!-- <b-icon icon="droplet-half" height="15" class="mb-1" />-->
<IBiDropletHalf height="15" class="mb-1" />
</div>
</b-col>
<b-col cols="7">
</BCol>
<BCol cols="7">
<div class="gdd-transaction-list-item-decay">
<decay-information-short decaytyp="short" :decay="decay" />
</div>
</b-col>
</b-row>
</BCol>
</BRow>
</div>
</template>
<script>

Some files were not shown because too many files have changed in this diff Show More