mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
Merge branch 'refs/heads/monterail_vue3_migration' into monterail_vue3_migration_frontend--vee-validate-refactor
This commit is contained in:
commit
4879f3c3a8
@ -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'],
|
||||
@ -27,22 +29,29 @@ module.exports = {
|
||||
'no-console': ['error'],
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'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-static-inline-styles': 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-unused-keys': [
|
||||
'error',
|
||||
{
|
||||
src: './src',
|
||||
extensions: ['.js', '.vue'],
|
||||
ignores: ['/overlay/'],
|
||||
enableFix: false,
|
||||
},
|
||||
],
|
||||
'@intlify/vue-i18n/no-raw-text': 0, // TODO remove at the end of migration and fix
|
||||
// '@intlify/vue-i18n/no-unused-keys': [
|
||||
// 'error',
|
||||
// {
|
||||
// src: './src',
|
||||
// extensions: ['.js', '.vue'],
|
||||
// ignores: ['/overlay/'],
|
||||
// enableFix: false,
|
||||
// },
|
||||
// ],
|
||||
'@intlify/vue-i18n/no-unused-keys': 0, // TODO remove at the end of migration and fix
|
||||
'@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
|
||||
@ -1 +1 @@
|
||||
v14.17.0
|
||||
v18.20
|
||||
@ -1,18 +1,17 @@
|
||||
'use strict';
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
extends: ["stylelint-config-standard-scss", "stylelint-config-recommended-vue"],
|
||||
extends: ['stylelint-config-standard-scss', 'stylelint-config-recommended-vue'],
|
||||
overrides: [
|
||||
{
|
||||
files: "**/*.{scss}",
|
||||
customSyntax: "postcss-scss",
|
||||
extends: ["stylelint-config-standard-scss"],
|
||||
files: '**/*.{scss}',
|
||||
customSyntax: 'postcss-scss',
|
||||
extends: ['stylelint-config-standard-scss'],
|
||||
},
|
||||
{
|
||||
files: "**/*.vue",
|
||||
customSyntax: "postcss-html",
|
||||
extends: ["stylelint-config-recommended-vue"],
|
||||
}
|
||||
]
|
||||
|
||||
};
|
||||
files: '**/*.vue',
|
||||
customSyntax: 'postcss-html',
|
||||
extends: ['stylelint-config-recommended-vue'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:14.17.0-alpine3.10 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
|
||||
|
||||
@ -4,7 +4,7 @@ module.exports = function (api) {
|
||||
const presets = ['@babel/preset-env']
|
||||
const plugins = []
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
if (import.meta.env.NODE_ENV === 'test') {
|
||||
plugins.push('transform-require-context')
|
||||
}
|
||||
|
||||
|
||||
101
admin/components.d.ts
vendored
Normal file
101
admin/components.d.ts
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/* 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 {
|
||||
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']
|
||||
BCardText: typeof import('bootstrap-vue-next')['BCardText']
|
||||
BCol: typeof import('bootstrap-vue-next')['BCol']
|
||||
BCollapse: typeof import('bootstrap-vue-next')['BCollapse']
|
||||
BContainer: typeof import('bootstrap-vue-next')['BContainer']
|
||||
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']
|
||||
BFormRadioGroup: typeof import('bootstrap-vue-next')['BFormRadioGroup']
|
||||
BFormSelect: typeof import('bootstrap-vue-next')['BFormSelect']
|
||||
BFormTextarea: typeof import('bootstrap-vue-next')['BFormTextarea']
|
||||
BInputGroup: typeof import('bootstrap-vue-next')['BInputGroup']
|
||||
BLink: typeof import('bootstrap-vue-next')['BLink']
|
||||
BListGroup: typeof import('bootstrap-vue-next')['BListGroup']
|
||||
BListGroupItem: typeof import('bootstrap-vue-next')['BListGroupItem']
|
||||
BModal: typeof import('bootstrap-vue-next')['BModal']
|
||||
BPagination: typeof import('bootstrap-vue-next')['BPagination']
|
||||
BRow: typeof import('bootstrap-vue-next')['BRow']
|
||||
BTab: typeof import('bootstrap-vue-next')['BTab']
|
||||
BTable: typeof import('bootstrap-vue-next')['BTable']
|
||||
BTableLite: typeof import('bootstrap-vue-next')['BTableLite']
|
||||
BTableSimple: typeof import('bootstrap-vue-next')['BTableSimple']
|
||||
BTabs: typeof import('bootstrap-vue-next')['BTabs']
|
||||
BTbody: typeof import('bootstrap-vue-next')['BTbody']
|
||||
BTd: typeof import('bootstrap-vue-next')['BTd']
|
||||
BTh: typeof import('bootstrap-vue-next')['BTh']
|
||||
BThead: typeof import('bootstrap-vue-next')['BThead']
|
||||
BToastOrchestrator: typeof import('bootstrap-vue-next')['BToastOrchestrator']
|
||||
BTooltip: typeof import('bootstrap-vue-next')['BTooltip']
|
||||
BTr: typeof import('bootstrap-vue-next')['BTr']
|
||||
ChangeUserRoleFormular: typeof import('./src/components/ChangeUserRoleFormular.vue')['default']
|
||||
CommunityVisualizeItem: typeof import('./src/components/Federation/CommunityVisualizeItem.vue')['default']
|
||||
ConfirmRegisterMailFormular: typeof import('./src/components/ConfirmRegisterMailFormular.vue')['default']
|
||||
ContentFooter: typeof import('./src/components/ContentFooter.vue')['default']
|
||||
ContributionLink: typeof import('./src/components/ContributionLink/ContributionLink.vue')['default']
|
||||
ContributionLinkForm: typeof import('./src/components/ContributionLink/ContributionLinkForm.vue')['default']
|
||||
ContributionLinkList: typeof import('./src/components/ContributionLink/ContributionLinkList.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/slots/ContributionMessagesListItem.vue')['default']
|
||||
Coordinates: typeof import('./src/components/input/Coordinates.vue')['default']
|
||||
CreationFormular: typeof import('./src/components/CreationFormular.vue')['default']
|
||||
CreationTransactionList: typeof import('./src/components/CreationTransactionList.vue')['default']
|
||||
DeletedUserFormular: typeof import('./src/components/DeletedUserFormular.vue')['default']
|
||||
EditableGroup: typeof import('./src/components/input/EditableGroup.vue')['default']
|
||||
EditableGroupableLabel: typeof import('./src/components/input/EditableGroupableLabel.vue')['default']
|
||||
EditCreationFormular: typeof import('./src/components/EditCreationFormular.vue')['default']
|
||||
FederationVisualizeItem: typeof import('./src/components/Federation/FederationVisualizeItem.vue')['default']
|
||||
FigureQrCode: typeof import('./src/components/FigureQrCode.vue')['default']
|
||||
IBiArrowClockwise: typeof import('~icons/bi/arrow-clockwise')['default']
|
||||
IBiBellFill: typeof import('~icons/bi/bell-fill')['default']
|
||||
IBiChatDots: typeof import('~icons/bi/chat-dots')['default']
|
||||
IBiCheck: typeof import('~icons/bi/check')['default']
|
||||
IBiEnvelope: typeof import('~icons/bi/envelope')['default']
|
||||
IBiExclamationCircleFill: typeof import('~icons/bi/exclamation-circle-fill')['default']
|
||||
IBiEye: typeof import('~icons/bi/eye')['default']
|
||||
IBiList: typeof import('~icons/bi/list')['default']
|
||||
IBiPencil: typeof import('~icons/bi/pencil')['default']
|
||||
IBiPencilFill: typeof import('~icons/bi/pencil-fill')['default']
|
||||
IBiPencilSquare: typeof import('~icons/bi/pencil-square')['default']
|
||||
IBiQuestionDiamond: typeof import('~icons/bi/question-diamond')['default']
|
||||
IBiQuestionSquare: typeof import('~icons/bi/question-square')['default']
|
||||
IBiTrash: typeof import('~icons/bi/trash')['default']
|
||||
IBiX: typeof import('~icons/bi/x')['default']
|
||||
IBiXCircle: typeof import('~icons/bi/x-circle')['default']
|
||||
IIcBaselineClose: typeof import('~icons/ic/baseline-close')['default']
|
||||
IPhCaretDown: typeof import('~icons/ph/caret-down')['default']
|
||||
IPhCaretUpFill: typeof import('~icons/ph/caret-up-fill')['default']
|
||||
NavBar: typeof import('./src/components/NavBar.vue')['default']
|
||||
NotFoundPage: typeof import('./src/components/NotFoundPage.vue')['default']
|
||||
OpenCreationsTable: typeof import('./src/components/Tables/OpenCreationsTable.vue')['default']
|
||||
Overlay: typeof import('./src/components/Overlay.vue')['default']
|
||||
ParseMessage: typeof import('./src/components/ContributionMessages/ParseMessage.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
RowDetails: typeof import('./src/components/RowDetails.vue')['default']
|
||||
SearchUserTable: typeof import('./src/components/Tables/SearchUserTable.vue')['default']
|
||||
StatisticTable: typeof import('./src/components/Tables/StatisticTable.vue')['default']
|
||||
TimePicker: typeof import('./src/components/input/TimePicker.vue')['default']
|
||||
TransactionLinkList: typeof import('./src/components/TransactionLinkList.vue')['default']
|
||||
UserQuery: typeof import('./src/components/UserQuery.vue')['default']
|
||||
}
|
||||
export interface ComponentCustomProperties {
|
||||
vBModal: typeof import('bootstrap-vue-next')['vBModal']
|
||||
vBToggle: typeof import('bootstrap-vue-next')['vBToggle']
|
||||
vBTooltip: typeof import('bootstrap-vue-next')['vBTooltip']
|
||||
}
|
||||
}
|
||||
21
admin/index.html
Normal file
21
admin/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/favicon.png">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<title>Gradido Admin Interface</title>
|
||||
<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">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper" id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<!-- built files will be auto injected -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "admin",
|
||||
"description": "Administraion Interface for Gradido",
|
||||
"description": "Administration Interface for Gradido",
|
||||
"main": "index.js",
|
||||
"author": "Moriz Wahl",
|
||||
"version": "2.3.1",
|
||||
@ -8,22 +8,27 @@
|
||||
"private": false,
|
||||
"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",
|
||||
"test:debug": "node --inspect-brk node_modules/.bin/vue-cli-service test:unit --no-cache --watch --runInBand",
|
||||
"locales": "scripts/sort.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/eslint-parser": "^7.24.8",
|
||||
"@babel/node": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@vue/cli-plugin-unit-jest": "^4.5.14",
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@vitejs/plugin-vue": "3.2.0",
|
||||
"@vue/apollo-composable": "^4.0.2",
|
||||
"@vue/apollo-option": "^4.0.0",
|
||||
"@vue/cli-plugin-unit-jest": "~5.0.8",
|
||||
"@vue/compat": "3.4.31",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/test-utils": "^1.2.2",
|
||||
"apollo-boost": "^0.4.9",
|
||||
@ -32,58 +37,59 @@
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-vue": "^2.0.2",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "^2.21.2",
|
||||
"core-js": "^3.6.5",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap-vue-next": "^0.23.2",
|
||||
"date-fns": "^2.29.3",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^15.6.1",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "26.6.3",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom-sixteen": "^2.0.0",
|
||||
"portal-vue": "^2.1.7",
|
||||
"qrcanvas-vue": "2.1.1",
|
||||
"portal-vue": "3.0.0",
|
||||
"qrcanvas-vue": "3.0.0",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"stats-webpack-plugin": "^0.7.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-apollo": "^3.0.8",
|
||||
"vue-i18n": "^8.26.5",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-router": "^3.5.3",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-persistedstate": "^4.1.0"
|
||||
"sass": "^1.77.8",
|
||||
"vite": "3.2.10",
|
||||
"vite-plugin-commonjs": "^0.10.1",
|
||||
"vue": "3.4.31",
|
||||
"vue-apollo": "3.1.2",
|
||||
"vue-i18n": "9.13.1",
|
||||
"vue-jest": "3.0.7",
|
||||
"vue-router": "4.4.0",
|
||||
"vuex": "4.1.0",
|
||||
"vuex-persistedstate": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/client": "^3.7.1",
|
||||
"@babel/eslint-parser": "^7.15.8",
|
||||
"@apollo/client": "^3.10.8",
|
||||
"@intlify/eslint-plugin-vue-i18n": "^1.4.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"@vue/compiler-sfc": "^3.4.32",
|
||||
"babel-plugin-transform-require-context": "^0.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "7.25.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"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": "3.3.1",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-vue": "^7.20.0",
|
||||
"eslint-plugin-vue": "8.7.1",
|
||||
"jest": "29.7.0",
|
||||
"mock-apollo-client": "^1.2.1",
|
||||
"postcss": "^8.4.8",
|
||||
"postcss-html": "^1.3.0",
|
||||
"postcss-scss": "^4.0.3",
|
||||
"stylelint": "^14.5.3",
|
||||
"stylelint-config-recommended-vue": "^1.3.0",
|
||||
"stylelint-config-standard-scss": "^3.0.0",
|
||||
"vue-cli-plugin-i18n": "^2.3.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
"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.0",
|
||||
"unplugin-vue-components": "^0.27.3",
|
||||
"vite-plugin-environment": "^1.1.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
||||
@ -4,7 +4,7 @@ const path = require('path')
|
||||
|
||||
// Host & Port
|
||||
const hostname = '127.0.0.1'
|
||||
const port = process.env.PORT || 8080
|
||||
const port = import.meta.env.PORT || 8080
|
||||
|
||||
// Express Server
|
||||
const app = express()
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<BToastOrchestrator />
|
||||
<default-layout v-if="$store.state.token" />
|
||||
<router-view v-else></router-view>
|
||||
<BModalOrchestrator />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import defaultLayout from '@/layouts/defaultLayout'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: { defaultLayout },
|
||||
}
|
||||
import { BModalOrchestrator } from 'bootstrap-vue-next'
|
||||
</script>
|
||||
<style>
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pointer:hover {
|
||||
background-color: rgb(216, 213, 213);
|
||||
background-color: rgb(216 213 213);
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
admin/src/assets/icons/circle-slash.png
Normal file
BIN
admin/src/assets/icons/circle-slash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1022 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
BIN
admin/src/assets/icons/envelope.png
Normal file
BIN
admin/src/assets/icons/envelope.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 491 B |
BIN
admin/src/assets/icons/person.png
Normal file
BIN
admin/src/assets/icons/person.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 396 B |
@ -1,124 +1,100 @@
|
||||
<template>
|
||||
<div class="change-user-role-formular">
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<div v-if="!$store.state.moderator.roles.includes('ADMIN')" class="m-3 mb-4">
|
||||
{{ roles.find((role) => role.value === currentRole).text }}
|
||||
<div v-if="!isModeratorRoleAdmin" class="m-3 mb-4">
|
||||
{{ roles.find((role) => role.value === currentRole.value).text }}
|
||||
</div>
|
||||
<div v-else-if="item.userId === $store.state.moderator.id" class="m-3 mb-4">
|
||||
<div v-else-if="item.userId === moderatorId" class="m-3 mb-4">
|
||||
{{ $t('userRole.notChangeYourSelf') }}
|
||||
</div>
|
||||
<div v-else class="m-3">
|
||||
<label for="role" class="mr-3">{{ $t('userRole.selectLabel') }}</label>
|
||||
<b-form-select class="role-select" v-model="roleSelected" :options="roles" />
|
||||
<BFormSelect v-model="roleSelected" class="role-select" :options="roles" />
|
||||
<div class="mt-3 mb-5">
|
||||
<b-button
|
||||
variant="danger"
|
||||
v-b-modal.user-role-modal
|
||||
:disabled="currentRole === roleSelected"
|
||||
@click="showModal()"
|
||||
>
|
||||
<BButton variant="danger" @click="showModal">
|
||||
<!-- :disabled="currentRole.value === roleSelected.value" -->
|
||||
{{ $t('change_user_role') }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { setUserRole } from '../graphql/setUserRole'
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { BButton, BFormSelect } from 'bootstrap-vue-next'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { setUserRole as setUserRoleMutation } from '../graphql/setUserRole'
|
||||
import { useStore } from 'vuex'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const { t } = useI18n()
|
||||
const store = useStore()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const rolesValues = {
|
||||
ADMIN: 'ADMIN',
|
||||
MODERATOR: 'MODERATOR',
|
||||
USER: 'USER',
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'ChangeUserRoleFormular',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentRole: this.getCurrentRole(),
|
||||
roleSelected: this.getCurrentRole(),
|
||||
roles: [
|
||||
{ value: rolesValues.USER, text: this.$t('userRole.selectRoles.user') },
|
||||
{ value: rolesValues.MODERATOR, text: this.$t('userRole.selectRoles.moderator') },
|
||||
{ value: rolesValues.ADMIN, text: this.$t('userRole.selectRoles.admin') },
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCurrentRole() {
|
||||
if (this.item.roles.length) return rolesValues[this.item.roles[0]]
|
||||
return rolesValues.USER
|
||||
},
|
||||
showModal() {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm(
|
||||
this.$t('overlay.changeUserRole.question', {
|
||||
username: `${this.item.firstName} ${this.item.lastName}`,
|
||||
newRole:
|
||||
this.roleSelected === rolesValues.ADMIN
|
||||
? this.$t('userRole.selectRoles.admin')
|
||||
: this.roleSelected === rolesValues.MODERATOR
|
||||
? this.$t('userRole.selectRoles.moderator')
|
||||
: this.$t('userRole.selectRoles.user'),
|
||||
}),
|
||||
{
|
||||
cancelTitle: this.$t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: this.$t('overlay.changeUserRole.title'),
|
||||
okTitle: this.$t('overlay.changeUserRole.yes'),
|
||||
okVariant: 'danger',
|
||||
},
|
||||
)
|
||||
.then((okClicked) => {
|
||||
if (okClicked) {
|
||||
this.setUserRole(this.roleSelected, this.currentRole)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
setUserRole(newRole, oldRole) {
|
||||
const role = this.roles.find((role) => {
|
||||
return role.value === newRole
|
||||
})
|
||||
const roleText = role.text
|
||||
const roleValue = role.value
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: setUserRole,
|
||||
variables: {
|
||||
userId: this.item.userId,
|
||||
role: role.value,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('updateRoles', {
|
||||
userId: this.item.userId,
|
||||
roles: roleValue === 'USER' ? [] : [roleValue],
|
||||
})
|
||||
this.toastSuccess(
|
||||
this.$t('userRole.successfullyChangedTo', {
|
||||
role: roleText,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.roleSelected = oldRole
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const getCurrentRole = () => {
|
||||
if (props.item.roles.length) return rolesValues[props.item.roles[0]]
|
||||
return rolesValues.USER
|
||||
}
|
||||
const currentRole = ref(getCurrentRole())
|
||||
const roleSelected = ref(getCurrentRole())
|
||||
|
||||
const emit = defineEmits(['update-roles', 'show-modal', 'select-role'])
|
||||
const isModeratorRoleAdmin = computed(() => store.state.moderator.roles.includes('ADMIN'))
|
||||
const moderatorId = computed(() => store.state.moderator.id)
|
||||
|
||||
const roles = computed(() => [
|
||||
{ value: rolesValues.USER, text: t('userRole.selectRoles.user') },
|
||||
{ value: rolesValues.MODERATOR, text: t('userRole.selectRoles.moderator') },
|
||||
{ value: rolesValues.ADMIN, text: t('userRole.selectRoles.admin') },
|
||||
])
|
||||
|
||||
const showModal = async () => {
|
||||
emit('show-modal')
|
||||
}
|
||||
|
||||
const { mutate: setUserRole } = useMutation(setUserRoleMutation)
|
||||
|
||||
const updateUserRole = (newRole, oldRole) => {
|
||||
const role = roles.value.find((role) => role.value === newRole)
|
||||
const roleText = role.text
|
||||
const roleValue = role.value
|
||||
|
||||
setUserRole({
|
||||
userId: props.item.userId,
|
||||
role: role.value,
|
||||
})
|
||||
.then(() => {
|
||||
emit('update-roles', {
|
||||
userId: props.item.userId,
|
||||
roles: roleValue === 'USER' ? [] : [roleValue],
|
||||
})
|
||||
toastSuccess(
|
||||
t('userRole.successfullyChangedTo', {
|
||||
role: roleText,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
roleSelected.value = oldRole
|
||||
toastError(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ currentRole, roleSelected, updateUserRole })
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@ -1,64 +1,64 @@
|
||||
<template>
|
||||
<div class="component-confirm-register-mail">
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<div v-if="checked">{{ $t('unregister_mail.text_true') }}</div>
|
||||
<div v-if="props.checked">{{ $t('unregister_mail.text_true') }}</div>
|
||||
<div v-else>
|
||||
{{
|
||||
dateLastSend === ''
|
||||
? $t('unregister_mail.never_sent', { email })
|
||||
: $t('unregister_mail.text_false', { date: dateLastSend, email })
|
||||
props.dateLastSend === ''
|
||||
? $t('unregister_mail.never_sent', { email: props.email })
|
||||
: $t('unregister_mail.text_false', { date: props.dateLastSend, email: props.email })
|
||||
}}
|
||||
|
||||
<!-- Using components -->
|
||||
<b-input-group :prepend="$t('unregister_mail.info')" class="mt-3">
|
||||
<b-form-input readonly :value="email"></b-form-input>
|
||||
<b-input-group-append>
|
||||
<b-button variant="outline-success" class="test-button" @click="sendRegisterMail">
|
||||
{{ $t('unregister_mail.button') }}
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<BInputGroup :prepend="$t('unregister_mail.info')" class="mt-3">
|
||||
<BFormInput v-model="email" readonly />
|
||||
<BButton variant="outline-success" append class="test-button" @click="sendRegisterMail">
|
||||
{{ $t('unregister_mail.button') }}
|
||||
</BButton>
|
||||
</BInputGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { sendActivationEmail } from '../graphql/sendActivationEmail'
|
||||
import { BButton, BFormInput, BInputGroup } from 'bootstrap-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'ConfirmRegisterMail',
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
},
|
||||
dateLastSend: {
|
||||
type: String,
|
||||
},
|
||||
const props = defineProps({
|
||||
checked: {
|
||||
type: Boolean,
|
||||
},
|
||||
methods: {
|
||||
sendRegisterMail() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: sendActivationEmail,
|
||||
variables: {
|
||||
email: this.email,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.toastSuccess(this.$t('unregister_mail.success', { email: this.email }))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(this.$t('unregister_mail.error', { message: error.message }))
|
||||
})
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
},
|
||||
dateLastSend: {
|
||||
type: String,
|
||||
},
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const email = ref(props.email)
|
||||
|
||||
const { mutate: activateEmail } = useMutation(sendActivationEmail)
|
||||
|
||||
const sendRegisterMail = async () => {
|
||||
try {
|
||||
await activateEmail({
|
||||
email: email.value,
|
||||
})
|
||||
toastSuccess(t('unregister_mail.success', { email: email.value }))
|
||||
} catch (error) {
|
||||
toastError(t('unregister_mail.error', { message: error.message }))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.input-group-text {
|
||||
background-color: rgb(255, 252, 205);
|
||||
background-color: rgb(255 252 205);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,19 +1,23 @@
|
||||
<template>
|
||||
<hr class="mb-0" />
|
||||
<div class="content-footer">
|
||||
<hr />
|
||||
<b-row align-v="center" class="mt-4 mb-4 justify-content-lg-between">
|
||||
<b-col>
|
||||
<BTr class="mt-4 mb-4 justify-content-lg-between">
|
||||
<BCol>
|
||||
<div class="copyright text-center text-lg-center text-muted">
|
||||
{{ $t('footer.copyright.year', { year }) }}
|
||||
<a
|
||||
:href="`https://gradido.net/${$i18n.locale}`"
|
||||
class="font-weight-bold ml-1"
|
||||
class="fw-bold ml-1 link-underline link-underline-opacity-0 link-underline-opacity-100-hover"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('footer.copyright.link') }}
|
||||
</a>
|
||||
{{ $t('math.pipe') }}
|
||||
<a href="https://github.com/gradido/gradido/releases/latest" target="_blank">
|
||||
|
|
||||
<a
|
||||
href="https://github.com/gradido/gradido/releases/latest"
|
||||
target="_blank"
|
||||
class="link-underline link-underline-opacity-0 link-underline-opacity-100-hover"
|
||||
>
|
||||
{{ $t('footer.app_version', { version }) }}
|
||||
</a>
|
||||
<a
|
||||
@ -24,22 +28,23 @@
|
||||
{{ $t('footer.short_hash', { shortHash }) }}
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</BCol>
|
||||
</BTr>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import CONFIG from '../config'
|
||||
import { BTr, BCol } from 'bootstrap-vue-next'
|
||||
|
||||
export default {
|
||||
name: 'ContentFooter',
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear(),
|
||||
version: CONFIG.APP_VERSION,
|
||||
hash: CONFIG.BUILD_COMMIT,
|
||||
shortHash: CONFIG.BUILD_COMMIT_SHORT,
|
||||
}
|
||||
},
|
||||
}
|
||||
const year = new Date().getFullYear()
|
||||
const version = CONFIG.APP_VERSION
|
||||
const hash = CONFIG.BUILD_COMMIT
|
||||
const shortHash = CONFIG.BUILD_COMMIT_SHORT
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.content-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="contribution-link">
|
||||
<b-card
|
||||
<BCard
|
||||
border-variant="success"
|
||||
:header="$t('contributionLink.contributionLinks')"
|
||||
header-bg-variant="success"
|
||||
@ -8,40 +8,41 @@
|
||||
header-class="text-center"
|
||||
class="mt-5"
|
||||
>
|
||||
<b-button
|
||||
<BButton
|
||||
v-if="!editContributionLink"
|
||||
@click="visible = !visible"
|
||||
class="my-3 d-flex justify-content-left"
|
||||
data-test="new-contribution-link-button"
|
||||
@click="visible = !visible"
|
||||
>
|
||||
{{ $t('math.plus') }} {{ $t('contributionLink.newContributionLink') }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
|
||||
<b-collapse v-model="visible" id="newContribution" class="mt-2">
|
||||
<b-card>
|
||||
<BCollapse id="newContribution" v-model="visible" class="mt-2">
|
||||
<BCard>
|
||||
<p class="h2 ml-5">{{ $t('contributionLink.contributionLinks') }}</p>
|
||||
<contribution-link-form
|
||||
:contributionLinkData="contributionLinkData"
|
||||
:editContributionLink="editContributionLink"
|
||||
:contribution-link-data="contributionLinkData"
|
||||
:edit-contribution-link="editContributionLink"
|
||||
@get-contribution-links="$emit('get-contribution-links')"
|
||||
@closeContributionForm="closeContributionForm"
|
||||
@close-contribution-form="closeContributionForm"
|
||||
/>
|
||||
</b-card>
|
||||
</b-collapse>
|
||||
</BCard>
|
||||
</BCollapse>
|
||||
|
||||
<b-card-text>
|
||||
<BCardText>
|
||||
<contribution-link-list
|
||||
v-if="count > 0"
|
||||
:items="items"
|
||||
@editContributionLinkData="editContributionLinkData"
|
||||
@edit-contribution-link-data="editContributionLinkData"
|
||||
@get-contribution-links="$emit('get-contribution-links')"
|
||||
@closeContributionForm="closeContributionForm"
|
||||
@close-contribution-form="closeContributionForm"
|
||||
/>
|
||||
<div v-else>{{ $t('contributionLink.noContributionLinks') }}</div>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
</BCardText>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContributionLinkForm from '../ContributionLink/ContributionLinkForm'
|
||||
import ContributionLinkList from '../ContributionLink/ContributionLinkList'
|
||||
@ -62,6 +63,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['get-contribution-links'],
|
||||
data: function () {
|
||||
return {
|
||||
visible: false,
|
||||
@ -72,14 +74,14 @@ export default {
|
||||
methods: {
|
||||
closeContributionForm() {
|
||||
if (this.visible) {
|
||||
this.$root.$emit('bv::toggle::collapse', 'newContribution')
|
||||
this.visible = false
|
||||
this.editContributionLink = false
|
||||
this.contributionLinkData = {}
|
||||
}
|
||||
},
|
||||
editContributionLinkData(data) {
|
||||
if (!this.visible) {
|
||||
this.$root.$emit('bv::toggle::collapse', 'newContribution')
|
||||
this.visible = true
|
||||
}
|
||||
this.contributionLinkData = data
|
||||
this.editContributionLink = true
|
||||
|
||||
@ -1,41 +1,43 @@
|
||||
<template>
|
||||
<div class="contribution-link-form">
|
||||
<b-form class="m-5" @submit.prevent="onSubmit" ref="contributionLinkForm">
|
||||
<BForm ref="contributionLinkForm" class="m-5" @submit.prevent="onSubmit" @reset="onReset">
|
||||
<!-- Date -->
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-form-group :label="$t('contributionLink.validFrom')">
|
||||
<b-form-datepicker
|
||||
reset-button
|
||||
<BRow>
|
||||
<BCol>
|
||||
<BFormGroup :label="$t('contributionLink.validFrom')">
|
||||
<BFormInput
|
||||
v-model="form.validFrom"
|
||||
reset-button
|
||||
size="lg"
|
||||
:min="min"
|
||||
class="mb-4 test-validFrom"
|
||||
reset-value=""
|
||||
:label-no-date-selected="$t('contributionLink.noDateSelected')"
|
||||
required
|
||||
></b-form-datepicker>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-group :label="$t('contributionLink.validTo')">
|
||||
<b-form-datepicker
|
||||
reset-button
|
||||
type="date"
|
||||
/>
|
||||
</BFormGroup>
|
||||
</BCol>
|
||||
<BCol>
|
||||
<BFormGroup :label="$t('contributionLink.validTo')">
|
||||
<BFormInput
|
||||
v-model="form.validTo"
|
||||
reset-button
|
||||
size="lg"
|
||||
:min="form.validFrom ? form.validFrom : min"
|
||||
class="mb-4 test-validTo"
|
||||
reset-value=""
|
||||
:label-no-date-selected="$t('contributionLink.noDateSelected')"
|
||||
required
|
||||
></b-form-datepicker>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
type="date"
|
||||
/>
|
||||
</BFormGroup>
|
||||
</BCol>
|
||||
</BRow>
|
||||
|
||||
<!-- Name -->
|
||||
<b-form-group :label="$t('contributionLink.name')">
|
||||
<b-form-input
|
||||
<BFormGroup :label="$t('contributionLink.name')">
|
||||
<BFormInput
|
||||
v-model="form.name"
|
||||
size="lg"
|
||||
type="text"
|
||||
@ -43,174 +45,176 @@
|
||||
required
|
||||
maxlength="100"
|
||||
class="test-name"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
></BFormInput>
|
||||
</BFormGroup>
|
||||
<!-- Desc -->
|
||||
<b-form-group :label="$t('contributionLink.memo')">
|
||||
<b-form-textarea
|
||||
<BFormGroup :label="$t('contributionLink.memo')">
|
||||
<BFormTextarea
|
||||
v-model="form.memo"
|
||||
size="lg"
|
||||
:placeholder="$t('contributionLink.memo')"
|
||||
required
|
||||
maxlength="255"
|
||||
class="test-memo"
|
||||
></b-form-textarea>
|
||||
</b-form-group>
|
||||
></BFormTextarea>
|
||||
</BFormGroup>
|
||||
<!-- Amount -->
|
||||
<b-form-group :label="$t('contributionLink.amount')">
|
||||
<b-form-input
|
||||
<BFormGroup :label="$t('contributionLink.amount')">
|
||||
<BFormInput
|
||||
v-model="form.amount"
|
||||
size="lg"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
required
|
||||
class="test-amount"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
<b-row class="mb-4">
|
||||
<b-col>
|
||||
></BFormInput>
|
||||
</BFormGroup>
|
||||
<BRow class="mb-4">
|
||||
<BCol>
|
||||
<!-- Cycle -->
|
||||
<label for="cycle">{{ $t('contributionLink.cycle') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.cycle"
|
||||
:options="cycle"
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<BFormSelect v-model="form.cycle" :options="cycle" class="mb-3" size="lg"></BFormSelect>
|
||||
</BCol>
|
||||
<BCol>
|
||||
<!-- maxPerCycle -->
|
||||
<label for="maxPerCycle">{{ $t('contributionLink.maxPerCycle') }}</label>
|
||||
<b-form-select
|
||||
<BFormSelect
|
||||
v-model="form.maxPerCycle"
|
||||
:options="maxPerCycle"
|
||||
:disabled="disabled"
|
||||
disabled
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
></BFormSelect>
|
||||
</BCol>
|
||||
</BRow>
|
||||
|
||||
<!-- Max amount -->
|
||||
<!--
|
||||
<b-form-group :label="$t('contributionLink.maximumAmount')">
|
||||
<b-form-input
|
||||
<BFormGroup :label="$t('contributionLink.maximumAmount')">
|
||||
<BFormInput
|
||||
v-model="form.maxAmountPerMonth"
|
||||
size="lg"
|
||||
:disabled="disabled"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
></BFormInput>
|
||||
</BFormGroup>
|
||||
-->
|
||||
<div class="mt-6">
|
||||
<b-button type="submit" variant="primary">
|
||||
<BButton type="submit" variant="primary">
|
||||
{{
|
||||
editContributionLink ? $t('contributionLink.saveChange') : $t('contributionLink.create')
|
||||
}}
|
||||
</b-button>
|
||||
<b-button type="reset" variant="danger" @click.prevent="onReset">
|
||||
</BButton>
|
||||
<BButton type="reset" variant="danger">
|
||||
{{ $t('contributionLink.clear') }}
|
||||
</b-button>
|
||||
<b-button @click.prevent="$emit('closeContributionForm')">
|
||||
</BButton>
|
||||
<BButton @click.prevent="emit('close-contribution-form')">
|
||||
{{ $t('close') }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
{{ console.log(editContributionLink) }}
|
||||
</div>
|
||||
</b-form>
|
||||
</BForm>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { createContributionLink } from '@/graphql/createContributionLink.js'
|
||||
import { updateContributionLink } from '@/graphql/updateContributionLink.js'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
export default {
|
||||
name: 'ContributionLinkForm',
|
||||
props: {
|
||||
contributionLinkData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
editContributionLink: { type: Boolean, required: true },
|
||||
const props = defineProps({
|
||||
contributionLinkData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
name: null,
|
||||
memo: null,
|
||||
amount: null,
|
||||
validFrom: null,
|
||||
validTo: null,
|
||||
cycle: 'ONCE',
|
||||
maxPerCycle: 1,
|
||||
maxAmountPerMonth: '0',
|
||||
},
|
||||
min: new Date(),
|
||||
cycle: [
|
||||
{ value: 'ONCE', text: this.$t('contributionLink.options.cycle.once') },
|
||||
// { value: 'hourly', text: this.$t('contributionLink.options.cycle.hourly') },
|
||||
{ value: 'DAILY', text: this.$t('contributionLink.options.cycle.daily') },
|
||||
// { value: 'weekly', text: this.$t('contributionLink.options.cycle.weekly') },
|
||||
// { value: 'monthly', text: this.$t('contributionLink.options.cycle.monthly') },
|
||||
// { value: 'yearly', text: this.$t('contributionLink.options.cycle.yearly') },
|
||||
],
|
||||
maxPerCycle: [
|
||||
{ value: '1', text: '1 x' },
|
||||
// { value: '2', text: '2 x' },
|
||||
// { value: '3', text: '3 x' },
|
||||
// { value: '4', text: '4 x' },
|
||||
// { value: '5', text: '5 x' },
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmit() {
|
||||
if (this.form.validFrom === null)
|
||||
return this.toastError(this.$t('contributionLink.noStartDate'))
|
||||
if (this.form.validTo === null) return this.toastError(this.$t('contributionLink.noEndDate'))
|
||||
editContributionLink: { type: Boolean, required: true },
|
||||
})
|
||||
|
||||
const variables = {
|
||||
...this.form,
|
||||
id: this.contributionLinkData.id ? this.contributionLinkData.id : null,
|
||||
}
|
||||
const emit = defineEmits(['get-contribution-links', 'close-contribution-form'])
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: this.editContributionLink ? updateContributionLink : createContributionLink,
|
||||
variables: variables,
|
||||
})
|
||||
.then((result) => {
|
||||
const link = this.editContributionLink
|
||||
? result.data.updateContributionLink.link
|
||||
: result.data.createContributionLink.link
|
||||
this.toastSuccess(
|
||||
this.editContributionLink ? this.$t('contributionLink.changeSaved') : link,
|
||||
)
|
||||
this.onReset()
|
||||
this.$root.$emit('bv::toggle::collapse', 'newContribution')
|
||||
this.$emit('get-contribution-links')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
onReset() {
|
||||
this.$refs.contributionLinkForm.reset()
|
||||
this.form = {}
|
||||
this.form.validFrom = null
|
||||
this.form.validTo = null
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
disabled() {
|
||||
return true
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
contributionLinkData() {
|
||||
this.form = this.contributionLinkData
|
||||
},
|
||||
const { t } = useI18n()
|
||||
|
||||
const contributionLinkForm = ref(null)
|
||||
|
||||
const form = ref({
|
||||
name: null,
|
||||
memo: null,
|
||||
amount: null,
|
||||
validFrom: null,
|
||||
validTo: null,
|
||||
cycle: 'ONCE',
|
||||
maxAmountPerMonth: '0',
|
||||
})
|
||||
|
||||
const min = new Date().toLocaleDateString()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const cycle = ref([
|
||||
{ value: 'ONCE', text: t('contributionLink.options.cycle.once') },
|
||||
{ value: 'DAILY', text: t('contributionLink.options.cycle.daily') },
|
||||
])
|
||||
|
||||
const maxPerCycle = ref([{ value: '1', text: '1 x' }])
|
||||
|
||||
const { mutate: contributionLinkMutation } = useMutation(createContributionLink)
|
||||
|
||||
const { mutate: contributionLinkMutationUpdate } = useMutation(updateContributionLink)
|
||||
|
||||
watch(
|
||||
() => props.contributionLinkData,
|
||||
(newVal) => {
|
||||
form.value = newVal
|
||||
form.value.validFrom = formatDateFromDateTime(newVal.validFrom)
|
||||
form.value.validTo = formatDateFromDateTime(newVal.validTo)
|
||||
},
|
||||
)
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (form.value.validFrom === null) return toastError(t('contributionLink.noStartDate'))
|
||||
|
||||
if (form.value.validTo === null) return toastError(t('contributionLink.noEndDate'))
|
||||
|
||||
const variables = {
|
||||
...form.value,
|
||||
// maxAmountPerMonth: 1, // TODO this is added only for test puropuse during migration since max amount input is commented out but without it being a number bigger then 0 it doesn't work
|
||||
id: props.contributionLinkData.id ? props.contributionLinkData.id : null,
|
||||
}
|
||||
|
||||
try {
|
||||
const mutationType = props.editContributionLink
|
||||
? contributionLinkMutationUpdate
|
||||
: contributionLinkMutation
|
||||
const result = await mutationType({ ...variables })
|
||||
const link = props.editContributionLink
|
||||
? result.data.updateContributionLink.link
|
||||
: result.data.createContributionLink.link
|
||||
toastSuccess(props.editContributionLink ? t('contributionLink.changeSaved') : link)
|
||||
onReset()
|
||||
emit('close-contribution-form')
|
||||
emit('get-contribution-links')
|
||||
} catch (error) {
|
||||
toastError(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
const formatDateFromDateTime = (datetimeString) => {
|
||||
if (!datetimeString || !datetimeString?.includes('T')) return datetimeString
|
||||
return datetimeString.split('T')[0]
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
form.value = { validFrom: null, validTo: null }
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
form,
|
||||
min,
|
||||
cycle,
|
||||
maxPerCycle,
|
||||
onSubmit,
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,125 +1,151 @@
|
||||
<template>
|
||||
<div class="contribution-link-list">
|
||||
<b-table :items="items" :fields="fields" striped hover stacked="lg">
|
||||
<BTable :items="props.items" :fields="fields" striped hover stacked="lg">
|
||||
<template #cell(delete)="data">
|
||||
<b-button
|
||||
<BButton
|
||||
variant="danger"
|
||||
size="md"
|
||||
class="mr-2 test-delete-link"
|
||||
@click="deleteContributionLink(data.item.id, data.item.name)"
|
||||
@click="handleDelete(data)"
|
||||
>
|
||||
<b-icon icon="trash" variant="light"></b-icon>
|
||||
</b-button>
|
||||
<IBiTrash />
|
||||
</BButton>
|
||||
</template>
|
||||
<template #cell(edit)="data">
|
||||
<b-button variant="success" size="md" class="mr-2" @click="editContributionLink(data.item)">
|
||||
<b-icon icon="pencil" variant="light"></b-icon>
|
||||
</b-button>
|
||||
<BButton variant="success" size="md" class="mr-2" @click="editContributionLink(data.item)">
|
||||
<IBiPencil />
|
||||
</BButton>
|
||||
</template>
|
||||
<template #cell(show)="data">
|
||||
<b-button
|
||||
<BButton
|
||||
variant="info"
|
||||
size="md"
|
||||
class="mr-2 test-show"
|
||||
@click="showContributionLink(data.item)"
|
||||
>
|
||||
<b-icon icon="eye" variant="light"></b-icon>
|
||||
</b-button>
|
||||
<IBiEye />
|
||||
</BButton>
|
||||
</template>
|
||||
</b-table>
|
||||
</BTable>
|
||||
|
||||
<b-modal ref="my-modal" ok-only hide-header-close>
|
||||
<b-card header-tag="header" footer-tag="footer">
|
||||
<BModal
|
||||
v-if="modalData"
|
||||
id="qr-link-modal"
|
||||
ref="my-modal"
|
||||
v-model="qrLinkModal"
|
||||
ok-only
|
||||
hide-header-close
|
||||
>
|
||||
<BCard header-tag="header" footer-tag="footer">
|
||||
<template #header>
|
||||
<h6 class="mb-0">{{ modalData ? modalData.name : '' }}</h6>
|
||||
</template>
|
||||
<b-card-text>
|
||||
<BCardText>
|
||||
{{ modalData.memo ? modalData.memo : '' }}
|
||||
<figure-qr-code :link="modalData ? modalData.link : ''" />
|
||||
</b-card-text>
|
||||
</BCardText>
|
||||
<template #footer>
|
||||
<em>{{ modalData ? modalData.link : '' }}</em>
|
||||
</template>
|
||||
</b-card>
|
||||
</b-modal>
|
||||
</BCard>
|
||||
</BModal>
|
||||
<BModal id="delete-link-modal" v-model="deleteLinkModal" @ok="executeDelete">
|
||||
<template #default>
|
||||
{{ t('contributionLink.deleteNow', { name: itemToBeDeleted.name }) }}
|
||||
</template>
|
||||
</BModal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { deleteContributionLink } from '@/graphql/deleteContributionLink.js'
|
||||
import FigureQrCode from '../FigureQrCode'
|
||||
import { useModal } from 'bootstrap-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'ContributionLinkList',
|
||||
components: {
|
||||
FigureQrCode,
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
items: { type: Array, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
'name',
|
||||
'memo',
|
||||
'amount',
|
||||
{ key: 'cycle', label: this.$t('contributionLink.cycle') },
|
||||
{ key: 'maxPerCycle', label: this.$t('contributionLink.maxPerCycle') },
|
||||
{
|
||||
key: 'validFrom',
|
||||
label: this.$t('contributionLink.validFrom'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'validTo',
|
||||
label: this.$t('contributionLink.validTo'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
}
|
||||
},
|
||||
},
|
||||
'delete',
|
||||
'edit',
|
||||
'show',
|
||||
],
|
||||
modalData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteContributionLink(id, name) {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm(this.$t('contributionLink.deleteNow', { name: name }))
|
||||
.then(async (value) => {
|
||||
if (value)
|
||||
await this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteContributionLink,
|
||||
variables: {
|
||||
id: id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.toastSuccess(this.$t('contributionLink.deleted'))
|
||||
this.$emit('closeContributionForm')
|
||||
this.$emit('get-contribution-links')
|
||||
})
|
||||
.catch((err) => {
|
||||
this.toastError(err.message)
|
||||
})
|
||||
})
|
||||
},
|
||||
editContributionLink(row) {
|
||||
this.$emit('editContributionLinkData', row)
|
||||
},
|
||||
})
|
||||
|
||||
showContributionLink(row) {
|
||||
this.modalData = row
|
||||
this.$refs['my-modal'].show()
|
||||
},
|
||||
const qrLinkModal = ref(false)
|
||||
const { show: showQrCodeModal } = useModal('qr-link-modal')
|
||||
|
||||
const deleteLinkModal = ref(false)
|
||||
const { show: showDeleteLinkModal } = useModal('delete-link-modal')
|
||||
|
||||
const emit = defineEmits([
|
||||
'close-contribution-form',
|
||||
'get-contribution-links',
|
||||
'edit-contribution-link-data',
|
||||
])
|
||||
|
||||
const { t, d } = useI18n()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const modalData = ref({})
|
||||
|
||||
const fields = ref([
|
||||
'name',
|
||||
'memo',
|
||||
'amount',
|
||||
{ key: 'cycle', label: t('contributionLink.cycle') },
|
||||
{ key: 'maxPerCycle', label: t('contributionLink.maxPerCycle') },
|
||||
{
|
||||
key: 'validFrom',
|
||||
label: t('contributionLink.validFrom'),
|
||||
formatter: (value) => (value ? d(new Date(value)) : ''),
|
||||
},
|
||||
{
|
||||
key: 'validTo',
|
||||
label: t('contributionLink.validTo'),
|
||||
formatter: (value) => (value ? d(new Date(value)) : ''),
|
||||
},
|
||||
'delete',
|
||||
'edit',
|
||||
'show',
|
||||
])
|
||||
|
||||
const { mutate: deleteContributionLinkMutation } = useMutation(deleteContributionLink)
|
||||
|
||||
const itemToBeDeleted = ref({})
|
||||
|
||||
const handleDelete = async (dataPayload) => {
|
||||
itemToBeDeleted.value = { ...dataPayload.item }
|
||||
showDeleteLinkModal()
|
||||
}
|
||||
|
||||
const executeDelete = async () => {
|
||||
try {
|
||||
await deleteContributionLinkMutation({ id: parseInt(itemToBeDeleted.value.id) })
|
||||
toastSuccess(t('contributionLink.deleted'))
|
||||
emit('close-contribution-form')
|
||||
emit('get-contribution-links')
|
||||
itemToBeDeleted.value = {}
|
||||
} catch (err) {
|
||||
toastError(err.message)
|
||||
}
|
||||
}
|
||||
|
||||
const editContributionLink = (row) => {
|
||||
emit('edit-contribution-link-data', row)
|
||||
}
|
||||
|
||||
const showContributionLink = (row) => {
|
||||
modalData.value = row
|
||||
showQrCodeModal()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
fields,
|
||||
modalData,
|
||||
deleteContributionLink,
|
||||
editContributionLink,
|
||||
showContributionLink,
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,244 +1,423 @@
|
||||
<template>
|
||||
<div class="contribution-messages-formular">
|
||||
<div class="mt-5">
|
||||
<b-form @reset.prevent="onReset" @submit="onSubmit()">
|
||||
<b-form-group>
|
||||
<b-form-checkbox v-model="showResubmissionDate">
|
||||
<BForm @reset.prevent="onReset" @submit="onSubmit()">
|
||||
<BFormGroup>
|
||||
<BFormCheckbox v-model="showResubmissionDate">
|
||||
{{ $t('moderator.show-submission-form') }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
<b-form-group v-if="showResubmissionDate">
|
||||
<b-form-datepicker v-model="resubmissionDate" :min="now"></b-form-datepicker>
|
||||
</BFormCheckbox>
|
||||
</BFormGroup>
|
||||
<BFormGroup v-if="showResubmissionDate">
|
||||
<BFormInput v-model="resubmissionDate" type="date" :min="now"></BFormInput>
|
||||
<time-picker v-model="resubmissionTime"></time-picker>
|
||||
</b-form-group>
|
||||
<b-tabs content-class="mt-3" v-model="tabindex" data-test="message-type-tabs">
|
||||
<b-tab active>
|
||||
</BFormGroup>
|
||||
<BTabs v-model="tabindex" content-class="mt-3" data-test="message-type-tabs">
|
||||
<BTab active>
|
||||
<template #title>
|
||||
<span id="message-tab-title">{{ $t('moderator.message') }}</span>
|
||||
<b-tooltip target="message-tab-title" triggers="hover">
|
||||
{{ $t('moderator.message-tooltip') }}
|
||||
</b-tooltip>
|
||||
</template>
|
||||
<b-form-textarea
|
||||
<BFormTextarea
|
||||
id="textarea"
|
||||
v-model="form.text"
|
||||
:placeholder="$t('contributionLink.memo')"
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
</b-tab>
|
||||
<b-tab>
|
||||
></BFormTextarea>
|
||||
</BTab>
|
||||
<BTab>
|
||||
<template #title>
|
||||
<span id="notice-tab-title">{{ $t('moderator.notice') }}</span>
|
||||
<b-tooltip target="notice-tab-title" triggers="hover">
|
||||
<BTooltip target="notice-tab-title" triggers="hover">
|
||||
{{ $t('moderator.notice-tooltip') }}
|
||||
</b-tooltip>
|
||||
</BTooltip>
|
||||
</template>
|
||||
<b-form-textarea
|
||||
<BFormTextarea
|
||||
id="textarea"
|
||||
v-model="form.text"
|
||||
:placeholder="$t('moderator.notice')"
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
</b-tab>
|
||||
<b-tab>
|
||||
></BFormTextarea>
|
||||
</BTab>
|
||||
<BTab>
|
||||
<template #title>
|
||||
<span id="memo-tab-title">{{ $t('moderator.memo') }}</span>
|
||||
<b-tooltip target="memo-tab-title" triggers="hover">
|
||||
<BTooltip target="memo-tab-title" triggers="hover">
|
||||
{{ $t('moderator.memo-tooltip') }}
|
||||
</b-tooltip>
|
||||
</BTooltip>
|
||||
</template>
|
||||
<b-form-textarea
|
||||
<BFormTextarea
|
||||
id="textarea"
|
||||
v-model="form.memo"
|
||||
:placeholder="$t('contributionLink.memo')"
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
<b-row class="mt-4 mb-6">
|
||||
<b-col>
|
||||
<b-button type="reset" variant="danger">{{ $t('form.cancel') }}</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button
|
||||
></BFormTextarea>
|
||||
</BTab>
|
||||
</BTabs>
|
||||
<BRow class="mt-4 mb-6">
|
||||
<BCol>
|
||||
<BButton type="reset" variant="danger">{{ $t('form.cancel') }}</BButton>
|
||||
</BCol>
|
||||
<BCol class="text-right">
|
||||
<BButton
|
||||
type="submit"
|
||||
variant="primary"
|
||||
:disabled="disabled"
|
||||
@click.prevent="onSubmit()"
|
||||
data-test="submit-dialog"
|
||||
@click.prevent="onSubmit()"
|
||||
>
|
||||
{{ $t('save') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</BButton>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BForm>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<!--<script>-->
|
||||
<!--import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage'-->
|
||||
<!--import { adminUpdateContribution } from '@/graphql/adminUpdateContribution'-->
|
||||
<!--import TimePicker from '@/components/input/TimePicker'-->
|
||||
|
||||
<!--export default {-->
|
||||
<!-- name: 'ContributionMessagesFormular',-->
|
||||
<!-- components: {-->
|
||||
<!-- TimePicker,-->
|
||||
<!-- },-->
|
||||
<!-- props: {-->
|
||||
<!-- contributionId: {-->
|
||||
<!-- type: Number,-->
|
||||
<!-- required: true,-->
|
||||
<!-- },-->
|
||||
<!-- contributionMemo: {-->
|
||||
<!-- type: String,-->
|
||||
<!-- required: true,-->
|
||||
<!-- },-->
|
||||
<!-- hideResubmission: {-->
|
||||
<!-- type: Boolean,-->
|
||||
<!-- required: true,-->
|
||||
<!-- },-->
|
||||
<!-- inputResubmissionDate: {-->
|
||||
<!-- type: String,-->
|
||||
<!-- required: false,-->
|
||||
<!-- },-->
|
||||
<!-- },-->
|
||||
<!-- emits: [-->
|
||||
<!-- 'update-contribution',-->
|
||||
<!-- 'update-contributions',-->
|
||||
<!-- 'get-contribution',-->
|
||||
<!-- 'update-status',-->
|
||||
<!-- 'get-list-contribution-messages',-->
|
||||
<!-- ],-->
|
||||
<!-- data() {-->
|
||||
<!-- const localInputResubmissionDate = this.inputResubmissionDate-->
|
||||
<!-- ? new Date(this.inputResubmissionDate)-->
|
||||
<!-- : null-->
|
||||
|
||||
<!-- return {-->
|
||||
<!-- form: {-->
|
||||
<!-- text: '',-->
|
||||
<!-- memo: this.contributionMemo,-->
|
||||
<!-- },-->
|
||||
<!-- loading: false,-->
|
||||
<!-- resubmissionDate: localInputResubmissionDate,-->
|
||||
<!-- resubmissionTime: localInputResubmissionDate-->
|
||||
<!-- ? localInputResubmissionDate.toLocaleTimeString('de-DE', {-->
|
||||
<!-- hour: '2-digit',-->
|
||||
<!-- minute: '2-digit',-->
|
||||
<!-- })-->
|
||||
<!-- : '00:00',-->
|
||||
<!-- showResubmissionDate: localInputResubmissionDate !== null,-->
|
||||
<!-- tabindex: 0, // 0 = Chat, 1 = Notice, 2 = Memo-->
|
||||
<!-- messageType: {-->
|
||||
<!-- DIALOG: 'DIALOG',-->
|
||||
<!-- MODERATOR: 'MODERATOR',-->
|
||||
<!-- },-->
|
||||
<!-- }-->
|
||||
<!-- },-->
|
||||
<!-- computed: {-->
|
||||
<!-- disabled() {-->
|
||||
<!-- return (-->
|
||||
<!-- (this.chatOrMemo === 0 && this.form.text === '') ||-->
|
||||
<!-- this.loading ||-->
|
||||
<!-- (this.chatOrMemo === 1 && this.form.memo.length < 5) ||-->
|
||||
<!-- (this.showResubmissionDate && !this.resubmissionDate)-->
|
||||
<!-- )-->
|
||||
<!-- },-->
|
||||
<!-- moderatorDisabled() {-->
|
||||
<!-- return this.form.text === '' || this.loading || this.chatOrMemo === 1-->
|
||||
<!-- },-->
|
||||
<!-- now() {-->
|
||||
<!-- return new Date()-->
|
||||
<!-- },-->
|
||||
<!-- },-->
|
||||
<!-- methods: {-->
|
||||
<!-- combineResubmissionDateAndTime() {-->
|
||||
<!-- // getTimezoneOffset-->
|
||||
<!-- const formattedDate = new Date(this.resubmissionDate)-->
|
||||
<!-- const [hours, minutes] = this.resubmissionTime.split(':')-->
|
||||
<!-- formattedDate.setHours(parseInt(hours))-->
|
||||
<!-- formattedDate.setMinutes(parseInt(minutes))-->
|
||||
<!-- return formattedDate-->
|
||||
<!-- },-->
|
||||
<!-- utcResubmissionDateTime() {-->
|
||||
<!-- if (!this.resubmissionDate) return null-->
|
||||
<!-- const localResubmissionDateAndTime = this.combineResubmissionDateAndTime()-->
|
||||
<!-- return new Date(-->
|
||||
<!-- localResubmissionDateAndTime.getTime() +-->
|
||||
<!-- localResubmissionDateAndTime.getTimezoneOffset() * 60000,-->
|
||||
<!-- )-->
|
||||
<!-- },-->
|
||||
<!-- onSubmit() {-->
|
||||
<!-- this.loading = true-->
|
||||
<!-- let mutation-->
|
||||
<!-- let updateOnlyResubmissionAt = false-->
|
||||
<!-- const resubmissionAtDate = this.showResubmissionDate-->
|
||||
<!-- ? this.combineResubmissionDateAndTime()-->
|
||||
<!-- : null-->
|
||||
<!-- const variables = {-->
|
||||
<!-- resubmissionAt: resubmissionAtDate ? resubmissionAtDate.toString() : null,-->
|
||||
<!-- }-->
|
||||
<!-- // update only resubmission date?-->
|
||||
<!-- if (this.form.text === '' && this.form.memo === this.contributionMemo) {-->
|
||||
<!-- mutation = adminUpdateContribution-->
|
||||
<!-- variables.id = this.contributionId-->
|
||||
<!-- updateOnlyResubmissionAt = true-->
|
||||
<!-- }-->
|
||||
<!-- // update tabindex 0 = dialog or 1 = moderator-->
|
||||
<!-- else if (this.tabindex !== 2) {-->
|
||||
<!-- mutation = adminCreateContributionMessage-->
|
||||
<!-- variables.message = this.form.text-->
|
||||
<!-- variables.messageType =-->
|
||||
<!-- this.tabindex === 0 ? this.messageType.DIALOG : this.messageType.MODERATOR-->
|
||||
<!-- variables.contributionId = this.contributionId-->
|
||||
<!-- // update contribution memo, tabindex 2-->
|
||||
<!-- } else {-->
|
||||
<!-- mutation = adminUpdateContribution-->
|
||||
<!-- variables.memo = this.form.memo-->
|
||||
<!-- variables.id = this.contributionId-->
|
||||
<!-- }-->
|
||||
<!-- if (this.showResubmissionDate && resubmissionAtDate < new Date()) {-->
|
||||
<!-- this.toastError(this.$t('contributionMessagesForm.resubmissionDateInPast'))-->
|
||||
<!-- this.loading = false-->
|
||||
<!-- return-->
|
||||
<!-- }-->
|
||||
<!-- this.$apollo-->
|
||||
<!-- .mutate({ mutation, variables })-->
|
||||
<!-- .then((result) => {-->
|
||||
<!-- if (-->
|
||||
<!-- (this.hideResubmission &&-->
|
||||
<!-- this.showResubmissionDate &&-->
|
||||
<!-- resubmissionAtDate > new Date()) ||-->
|
||||
<!-- this.tabindex === 2-->
|
||||
<!-- ) {-->
|
||||
<!-- this.$emit('update-contributions')-->
|
||||
<!-- } else {-->
|
||||
<!-- this.$emit('get-list-contribution-messages', this.contributionId)-->
|
||||
<!-- // update status increase message count and update chat symbol-->
|
||||
<!-- // if (updateOnlyResubmissionAt === true) no message was created-->
|
||||
<!-- if (!updateOnlyResubmissionAt) {-->
|
||||
<!-- this.$emit('update-status', this.contributionId)-->
|
||||
<!-- }-->
|
||||
<!-- }-->
|
||||
<!-- this.toastSuccess(this.$t('message.request'))-->
|
||||
<!-- this.loading = false-->
|
||||
<!-- })-->
|
||||
<!-- .catch((error) => {-->
|
||||
<!-- this.toastError(error.message)-->
|
||||
<!-- this.loading = false-->
|
||||
<!-- })-->
|
||||
<!-- },-->
|
||||
<!-- onReset(event) {-->
|
||||
<!-- this.form.text = ''-->
|
||||
<!-- this.form.memo = this.contributionMemo-->
|
||||
<!-- this.showResubmissionDate = false-->
|
||||
<!-- this.resubmissionDate = this.inputResubmissionDate-->
|
||||
<!-- this.resubmissionTime = this.inputResubmissionDate-->
|
||||
<!-- ? new Date(this.inputResubmissionDate).toLocaleTimeString('de-DE', {-->
|
||||
<!-- hour: '2-digit',-->
|
||||
<!-- minute: '2-digit',-->
|
||||
<!-- })-->
|
||||
<!-- : '00:00'-->
|
||||
<!-- this.showResubmissionDate =-->
|
||||
<!-- this.inputResubmissionDate !== undefined && this.inputResubmissionDate !== null-->
|
||||
<!-- },-->
|
||||
<!-- enableMemo() {-->
|
||||
<!-- this.chatOrMemo = 1-->
|
||||
<!-- },-->
|
||||
<!-- },-->
|
||||
<!--}-->
|
||||
<!--</script>-->
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import TimePicker from '@/components/input/TimePicker'
|
||||
import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage'
|
||||
import { adminUpdateContribution } from '@/graphql/adminUpdateContribution'
|
||||
import TimePicker from '@/components/input/TimePicker'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TimePicker,
|
||||
const props = defineProps({
|
||||
contributionId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
name: 'ContributionMessagesFormular',
|
||||
props: {
|
||||
contributionId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
contributionMemo: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
hideResubmission: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
inputResubmissionDate: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
contributionMemo: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
const localInputResubmissionDate = this.inputResubmissionDate
|
||||
? new Date(this.inputResubmissionDate)
|
||||
: null
|
||||
hideResubmission: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
inputResubmissionDate: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
form: {
|
||||
text: '',
|
||||
memo: this.contributionMemo,
|
||||
},
|
||||
loading: false,
|
||||
resubmissionDate: localInputResubmissionDate,
|
||||
resubmissionTime: localInputResubmissionDate
|
||||
? localInputResubmissionDate.toLocaleTimeString('de-DE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
: '00:00',
|
||||
showResubmissionDate: localInputResubmissionDate !== null,
|
||||
tabindex: 0, // 0 = Chat, 1 = Notice, 2 = Memo
|
||||
messageType: {
|
||||
DIALOG: 'DIALOG',
|
||||
MODERATOR: 'MODERATOR',
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
combineResubmissionDateAndTime() {
|
||||
// getTimezoneOffset
|
||||
const formattedDate = new Date(this.resubmissionDate)
|
||||
const [hours, minutes] = this.resubmissionTime.split(':')
|
||||
formattedDate.setHours(parseInt(hours))
|
||||
formattedDate.setMinutes(parseInt(minutes))
|
||||
return formattedDate
|
||||
},
|
||||
utcResubmissionDateTime() {
|
||||
if (!this.resubmissionDate) return null
|
||||
const localResubmissionDateAndTime = this.combineResubmissionDateAndTime()
|
||||
return new Date(
|
||||
localResubmissionDateAndTime.getTime() +
|
||||
localResubmissionDateAndTime.getTimezoneOffset() * 60000,
|
||||
)
|
||||
},
|
||||
onSubmit() {
|
||||
this.loading = true
|
||||
let mutation
|
||||
let updateOnlyResubmissionAt = false
|
||||
const resubmissionAtDate = this.showResubmissionDate
|
||||
? this.combineResubmissionDateAndTime()
|
||||
: null
|
||||
const variables = {
|
||||
resubmissionAt: resubmissionAtDate ? resubmissionAtDate.toString() : null,
|
||||
}
|
||||
// update only resubmission date?
|
||||
if (this.form.text === '' && this.form.memo === this.contributionMemo) {
|
||||
mutation = adminUpdateContribution
|
||||
variables.id = this.contributionId
|
||||
updateOnlyResubmissionAt = true
|
||||
}
|
||||
// update tabindex 0 = dialog or 1 = moderator
|
||||
else if (this.tabindex !== 2) {
|
||||
mutation = adminCreateContributionMessage
|
||||
variables.message = this.form.text
|
||||
variables.messageType =
|
||||
this.tabindex === 0 ? this.messageType.DIALOG : this.messageType.MODERATOR
|
||||
variables.contributionId = this.contributionId
|
||||
// update contribution memo, tabindex 2
|
||||
} else {
|
||||
mutation = adminUpdateContribution
|
||||
variables.memo = this.form.memo
|
||||
variables.id = this.contributionId
|
||||
}
|
||||
if (this.showResubmissionDate && resubmissionAtDate < new Date()) {
|
||||
this.toastError(this.$t('contributionMessagesForm.resubmissionDateInPast'))
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
this.$apollo
|
||||
.mutate({ mutation, variables })
|
||||
.then((result) => {
|
||||
if (
|
||||
(this.hideResubmission &&
|
||||
this.showResubmissionDate &&
|
||||
resubmissionAtDate > new Date()) ||
|
||||
this.tabindex === 2
|
||||
) {
|
||||
this.$emit('update-contributions')
|
||||
} else {
|
||||
this.$emit('get-list-contribution-messages', this.contributionId)
|
||||
// update status increase message count and update chat symbol
|
||||
// if (updateOnlyResubmissionAt === true) no message was created
|
||||
if (!updateOnlyResubmissionAt) {
|
||||
this.$emit('update-status', this.contributionId)
|
||||
}
|
||||
}
|
||||
this.toastSuccess(this.$t('message.request'))
|
||||
this.loading = false
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
onReset(event) {
|
||||
this.form.text = ''
|
||||
this.form.memo = this.contributionMemo
|
||||
this.showResubmissionDate = false
|
||||
this.resubmissionDate = this.inputResubmissionDate
|
||||
this.resubmissionTime = this.inputResubmissionDate
|
||||
? new Date(this.inputResubmissionDate).toLocaleTimeString('de-DE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
: '00:00'
|
||||
this.showResubmissionDate =
|
||||
this.inputResubmissionDate !== undefined && this.inputResubmissionDate !== null
|
||||
},
|
||||
enableMemo() {
|
||||
this.chatOrMemo = 1
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
disabled() {
|
||||
return (
|
||||
(this.chatOrMemo === 0 && this.form.text === '') ||
|
||||
this.loading ||
|
||||
(this.chatOrMemo === 1 && this.form.memo.length < 5) ||
|
||||
(this.showResubmissionDate && !this.resubmissionDate)
|
||||
)
|
||||
},
|
||||
moderatorDisabled() {
|
||||
return this.form.text === '' || this.loading || this.chatOrMemo === 1
|
||||
},
|
||||
now() {
|
||||
return new Date()
|
||||
},
|
||||
},
|
||||
const emit = defineEmits([
|
||||
'update-contribution',
|
||||
'update-contributions',
|
||||
'get-contribution',
|
||||
'update-status',
|
||||
'get-list-contribution-messages',
|
||||
])
|
||||
|
||||
const { t } = useI18n()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const form = ref({
|
||||
text: '',
|
||||
memo: props.contributionMemo,
|
||||
})
|
||||
const loading = ref(false)
|
||||
|
||||
const localInputResubmissionDate = props.inputResubmissionDate
|
||||
? new Date(props.inputResubmissionDate)
|
||||
: null
|
||||
const resubmissionDate = ref(localInputResubmissionDate)
|
||||
const resubmissionTime = ref(
|
||||
localInputResubmissionDate
|
||||
? localInputResubmissionDate.toLocaleTimeString('de-DE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
: '00:00',
|
||||
)
|
||||
const showResubmissionDate = ref(localInputResubmissionDate !== null)
|
||||
const tabindex = ref(0) // 0 = Chat, 1 = Notice, 2 = Memo
|
||||
const messageType = {
|
||||
DIALOG: 'DIALOG',
|
||||
MODERATOR: 'MODERATOR',
|
||||
}
|
||||
|
||||
const disabled = computed(() => {
|
||||
return (
|
||||
(tabindex.value === 0 && form.value.text === '') ||
|
||||
loading.value ||
|
||||
(tabindex.value === 1 && form.value.memo.length < 5) ||
|
||||
(showResubmissionDate.value && !resubmissionDate.value)
|
||||
)
|
||||
})
|
||||
|
||||
// const moderatorDisabled = computed(() => {
|
||||
// return form.value.text === '' || loading.value || tabindex.value === 1
|
||||
// })
|
||||
|
||||
const now = computed(() => new Date())
|
||||
|
||||
const { mutate: createContributionMessageMutation } = useMutation(adminCreateContributionMessage)
|
||||
const { mutate: updateContributionMutation } = useMutation(adminUpdateContribution)
|
||||
|
||||
const combineResubmissionDateAndTime = () => {
|
||||
const formattedDate = new Date(resubmissionDate.value)
|
||||
const [hours, minutes] = resubmissionTime.value.split(':')
|
||||
formattedDate.setHours(parseInt(hours))
|
||||
formattedDate.setMinutes(parseInt(minutes))
|
||||
return formattedDate
|
||||
}
|
||||
|
||||
// const utcResubmissionDateTime = () => {
|
||||
// if (!resubmissionDate.value) return null
|
||||
// const localResubmissionDateAndTime = combineResubmissionDateAndTime()
|
||||
// return new Date(
|
||||
// localResubmissionDateAndTime.getTime() +
|
||||
// localResubmissionDateAndTime.getTimezoneOffset() * 60000,
|
||||
// )
|
||||
// }
|
||||
|
||||
const onSubmit = () => {
|
||||
loading.value = true
|
||||
let mutation
|
||||
let updateOnlyResubmissionAt = false
|
||||
const resubmissionAtDate = showResubmissionDate.value ? combineResubmissionDateAndTime() : null
|
||||
const variables = {
|
||||
resubmissionAt: resubmissionAtDate ? resubmissionAtDate.toString() : null,
|
||||
}
|
||||
|
||||
if (form.value.text === '' && form.value.memo === props.contributionMemo) {
|
||||
mutation = updateContributionMutation
|
||||
variables.id = props.contributionId
|
||||
updateOnlyResubmissionAt = true
|
||||
} else if (tabindex.value !== 2) {
|
||||
mutation = createContributionMessageMutation
|
||||
variables.message = form.value.text
|
||||
variables.messageType = tabindex.value === 0 ? messageType.DIALOG : messageType.MODERATOR
|
||||
variables.contributionId = props.contributionId
|
||||
} else {
|
||||
mutation = updateContributionMutation
|
||||
variables.memo = form.value.memo
|
||||
variables.id = props.contributionId
|
||||
}
|
||||
|
||||
if (showResubmissionDate.value && resubmissionAtDate < new Date()) {
|
||||
toastError(t('contributionMessagesForm.resubmissionDateInPast'))
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
mutation({ variables })
|
||||
.then(() => {
|
||||
if (
|
||||
(props.hideResubmission && showResubmissionDate.value && resubmissionAtDate > new Date()) ||
|
||||
tabindex.value === 2
|
||||
) {
|
||||
emit('update-contributions')
|
||||
} else {
|
||||
emit('get-list-contribution-messages', props.contributionId)
|
||||
if (!updateOnlyResubmissionAt) {
|
||||
emit('update-status', props.contributionId)
|
||||
}
|
||||
}
|
||||
toastSuccess(t('message.request'))
|
||||
loading.value = false
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.message)
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
form.value.text = ''
|
||||
form.value.memo = props.contributionMemo
|
||||
showResubmissionDate.value = false
|
||||
resubmissionDate.value = props.inputResubmissionDate
|
||||
resubmissionTime.value = props.inputResubmissionDate
|
||||
? new Date(props.inputResubmissionDate).toLocaleTimeString('de-DE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
: '00:00'
|
||||
showResubmissionDate.value =
|
||||
props.inputResubmissionDate !== undefined && props.inputResubmissionDate !== null
|
||||
}
|
||||
|
||||
// const enableMemo = () => {
|
||||
// tabindex.value = 1
|
||||
// }
|
||||
</script>
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
<template>
|
||||
<div class="contribution-messages-list">
|
||||
<b-container>
|
||||
<div v-for="message in messages" v-bind:key="message.id">
|
||||
<BContainer>
|
||||
<div v-for="message in messages" :key="message.id">
|
||||
<contribution-messages-list-item
|
||||
:message="message"
|
||||
:contributionUserId="contributionUserId"
|
||||
:contribution-user-id="contributionUserId"
|
||||
/>
|
||||
</div>
|
||||
</b-container>
|
||||
</BContainer>
|
||||
<div v-if="contributionStatus === 'PENDING' || contributionStatus === 'IN_PROGRESS'">
|
||||
<contribution-messages-formular
|
||||
:contributionId="contributionId"
|
||||
:contributionMemo="contributionMemo"
|
||||
:hideResubmission="hideResubmission"
|
||||
:inputResubmissionDate="resubmissionAt"
|
||||
@get-list-contribution-messages="$apollo.queries.Messages.refetch()"
|
||||
:contribution-id="contributionId"
|
||||
:contribution-memo="contributionMemo"
|
||||
:hide-resubmission="hideResubmission"
|
||||
:input-resubmission-date="resubmissionAt"
|
||||
@get-list-contribution-messages="refetch"
|
||||
@update-status="updateStatus"
|
||||
@reload-contribution="reloadContribution"
|
||||
@update-contributions="updateContributions"
|
||||
@ -22,78 +22,74 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ContributionMessagesListItem from './slots/ContributionMessagesListItem'
|
||||
import ContributionMessagesFormular from '../ContributionMessages/ContributionMessagesFormular'
|
||||
import { adminListContributionMessages } from '../../graphql/adminListContributionMessages.js'
|
||||
|
||||
export default {
|
||||
name: 'ContributionMessagesList',
|
||||
components: {
|
||||
ContributionMessagesListItem,
|
||||
ContributionMessagesFormular,
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
|
||||
import { adminListContributionMessages } from '../../graphql/adminListContributionMessages.js'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const props = defineProps({
|
||||
contributionId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
contributionId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
contributionMemo: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
contributionStatus: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
contributionUserId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
hideResubmission: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
resubmissionAt: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
contributionMemo: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
messages: [],
|
||||
}
|
||||
contributionStatus: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
apollo: {
|
||||
Messages: {
|
||||
query() {
|
||||
return adminListContributionMessages
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
contributionId: this.contributionId,
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
update({ adminListContributionMessages }) {
|
||||
this.messages = adminListContributionMessages.messages
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
},
|
||||
contributionUserId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
methods: {
|
||||
updateStatus(id) {
|
||||
this.$emit('update-status', id)
|
||||
},
|
||||
reloadContribution(id) {
|
||||
this.$emit('reload-contribution', id)
|
||||
},
|
||||
updateContributions() {
|
||||
this.$emit('update-contributions')
|
||||
},
|
||||
hideResubmission: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
resubmissionAt: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update-status', 'reload-contribution', 'update-contributions'])
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
const messages = ref([])
|
||||
|
||||
const { onResult, onError, result, refetch } = useQuery(
|
||||
adminListContributionMessages,
|
||||
{
|
||||
contributionId: props.contributionId,
|
||||
},
|
||||
{
|
||||
fetchPolicy: 'no-cache',
|
||||
},
|
||||
)
|
||||
|
||||
onError((error) => {
|
||||
toastError(error.message)
|
||||
})
|
||||
|
||||
onResult(() => {
|
||||
messages.value = result.value.adminListContributionMessages.messages
|
||||
})
|
||||
|
||||
const updateStatus = (id) => {
|
||||
emit('update-status', id)
|
||||
}
|
||||
|
||||
const reloadContribution = (id) => {
|
||||
emit('reload-contribution', id)
|
||||
}
|
||||
|
||||
const updateContributions = () => {
|
||||
emit('update-contributions')
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
@ -26,9 +26,9 @@ export default {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
messageType: {
|
||||
type: String,
|
||||
reuired: true,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@ -36,7 +36,7 @@ export default {
|
||||
let string = this.message
|
||||
const linkified = []
|
||||
let amount
|
||||
if (this.type === 'HISTORY') {
|
||||
if (this.messageType === 'HISTORY') {
|
||||
const split = string.split(/\n\s*---\n\s*/)
|
||||
string = split[1]
|
||||
linkified.push({ type: 'date', text: split[0].trim() })
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<span class="ml-2 mr-2" data-test="moderator-name">
|
||||
{{ message.userFirstName }} {{ message.userLastName }}
|
||||
</span>
|
||||
<b-avatar square variant="warning"></b-avatar>
|
||||
<BAvatar square variant="warning"></BAvatar>
|
||||
<small v-if="isHistory">
|
||||
<hr />
|
||||
{{ $t('moderator.history') }}
|
||||
@ -23,7 +23,7 @@
|
||||
</small>
|
||||
</div>
|
||||
<div v-else class="text-left p-2 rounded-sm mb-3" :class="boxClass">
|
||||
<b-avatar variant="info"></b-avatar>
|
||||
<BAvatar variant="info"></BAvatar>
|
||||
<span class="ml-2 mr-2" data-test="user-name">
|
||||
{{ message.userFirstName }} {{ message.userLastName }}
|
||||
</span>
|
||||
@ -82,20 +82,25 @@ export default {
|
||||
float: right;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.is-moderator-message {
|
||||
background-color: rgb(228, 237, 245);
|
||||
background-color: rgb(228 237 245);
|
||||
}
|
||||
|
||||
.is-moderator-hidden-message {
|
||||
background-color: rgb(217, 161, 228);
|
||||
background-color: rgb(217 161 228);
|
||||
}
|
||||
|
||||
.is-user {
|
||||
clear: both;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.is-user-message {
|
||||
background-color: rgb(236, 235, 213);
|
||||
background-color: rgb(236 235 213);
|
||||
}
|
||||
|
||||
.is-user-history-message {
|
||||
background-color: rgb(235, 226, 57);
|
||||
background-color: rgb(235 226 57);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,178 +2,201 @@
|
||||
<div class="component-creation-formular">
|
||||
{{ $t('creation_form.form') }}
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<b-form ref="creationForm">
|
||||
<div class="ml-4">
|
||||
<BForm ref="creationForm">
|
||||
<div class="m-4 mt-0">
|
||||
<label>{{ $t('creation_form.select_month') }}</label>
|
||||
</div>
|
||||
<b-row class="ml-4">
|
||||
<b-form-radio-group
|
||||
<BFormRadioGroup
|
||||
id="radio-group-month-selection"
|
||||
v-model="selected"
|
||||
:options="radioOptions"
|
||||
value-field="item"
|
||||
text-field="name"
|
||||
name="month-selection"
|
||||
></b-form-radio-group>
|
||||
</b-row>
|
||||
<b-row class="m-4" v-show="selected !== ''">
|
||||
/>
|
||||
</div>
|
||||
<div v-if="selected" class="m-4 d-flex">
|
||||
<label>{{ $t('creation_form.select_value') }}</label>
|
||||
<div>
|
||||
<b-input-group prepend="GDD" append=".00">
|
||||
<b-form-input
|
||||
type="number"
|
||||
v-model="value"
|
||||
:min="rangeMin"
|
||||
:max="rangeMax"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
|
||||
<b-form-input
|
||||
type="range"
|
||||
v-model="value"
|
||||
:min="rangeMin"
|
||||
:max="rangeMax"
|
||||
step="10"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
<BInputGroup prepend="GDD" append=".00">
|
||||
<BFormInput v-model="value" type="number" :min="rangeMin" :max="rangeMax" />
|
||||
</BInputGroup>
|
||||
<BInputGroup
|
||||
prepend="0"
|
||||
:append="String(rangeMax)"
|
||||
class="mt-3 flex-nowrap align-items-center"
|
||||
>
|
||||
<BFormInput v-model="value" type="range" :min="rangeMin" :max="rangeMax" step="10" />
|
||||
</BInputGroup>
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.enter_text') }}</label>
|
||||
<div>
|
||||
<b-form-textarea
|
||||
<BFormTextarea
|
||||
id="textarea-state"
|
||||
v-model="text"
|
||||
:state="text.length >= 10"
|
||||
:placeholder="$t('creation_form.min_characters')"
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-left">
|
||||
<b-button type="reset" variant="danger" @click="$refs.creationForm.reset()">
|
||||
{{ $t('creation_form.reset') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-center">
|
||||
<div class="text-right">
|
||||
<b-button
|
||||
v-if="pagetype === 'PageCreationConfirm'"
|
||||
type="button"
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
@click="submitCreation"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
>
|
||||
{{ $t('creation_form.update_creation') }}
|
||||
</b-button>
|
||||
<b-button
|
||||
v-else
|
||||
type="button"
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
@click="submitCreation"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
>
|
||||
{{ $t('creation_form.submit_creation') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
<div class="buttons-wrapper d-flex justify-content-between">
|
||||
<BButton type="reset" variant="danger" @click="onReset()">
|
||||
{{ $t('creation_form.reset') }}
|
||||
</BButton>
|
||||
<div>
|
||||
<BButton
|
||||
v-if="pagetype === 'PageCreationConfirm'"
|
||||
type="button"
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
@click="submitCreation"
|
||||
>
|
||||
{{ $t('creation_form.update_creation') }}
|
||||
</BButton>
|
||||
<BButton
|
||||
v-else
|
||||
type="button"
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
@click="submitCreation"
|
||||
>
|
||||
{{ $t('creation_form.submit_creation') }}
|
||||
</BButton>
|
||||
</div>
|
||||
</div>
|
||||
</BForm>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import { useMutation, useQuery } from '@vue/apollo-composable'
|
||||
import { useStore } from 'vuex'
|
||||
import { adminCreateContribution } from '../graphql/adminCreateContribution'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
export default {
|
||||
name: 'CreationFormular',
|
||||
mixins: [creationMonths],
|
||||
props: {
|
||||
pagetype: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
creationUserData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
import { adminOpenCreations } from '../graphql/adminOpenCreations'
|
||||
import useCreationMonths from '../composables/useCreationMonths'
|
||||
import {
|
||||
BFormInput,
|
||||
BFormRadioGroup,
|
||||
BForm,
|
||||
BInputGroup,
|
||||
BButton,
|
||||
BFormTextarea,
|
||||
} from 'bootstrap-vue-next'
|
||||
|
||||
const { creationDateObjects } = useCreationMonths()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const props = defineProps({
|
||||
pagetype: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
data() {
|
||||
item: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
creationUserData: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
creation: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const store = useStore()
|
||||
|
||||
const text = ref(!props.creationUserData.memo ? '' : props.creationUserData.memo)
|
||||
const value = ref(!props.creationUserData.amount ? 0 : props.creationUserData.amount)
|
||||
const rangeMin = ref(0)
|
||||
const rangeMax = ref(1000)
|
||||
const selected = ref()
|
||||
const creationForm = ref(null)
|
||||
|
||||
const radioOptions = computed(() => {
|
||||
return creationDateObjects.value.map((obj, idx) => {
|
||||
return {
|
||||
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
|
||||
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
|
||||
rangeMin: 0,
|
||||
rangeMax: 1000,
|
||||
selected: '',
|
||||
userId: this.item.userId,
|
||||
item: { ...obj, creation: props.creation[idx] },
|
||||
name: obj.short + (props.creation[idx] ? ' ' + props.creation[idx] + ' GDD' : ''),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const updateRadioSelected = (name) => {
|
||||
text.value = `${t('creation_form.creation_for')} ${name?.short} ${name?.year}`
|
||||
rangeMin.value = 0
|
||||
rangeMax.value = Number(name?.creation)
|
||||
}
|
||||
|
||||
const onReset = () => {
|
||||
text.value = ''
|
||||
value.value = 0
|
||||
selected.value = null
|
||||
}
|
||||
const { mutate: createContribution } = useMutation(adminCreateContribution)
|
||||
|
||||
const { refetch: refetchCreations } = useQuery(adminOpenCreations, { userId: props.item.userId })
|
||||
const emit = defineEmits(['update-user-data'])
|
||||
|
||||
const submitCreation = async () => {
|
||||
try {
|
||||
const result = await createContribution({
|
||||
email: props.item.email,
|
||||
creationDate: selected.value.date,
|
||||
amount: Number(value.value),
|
||||
memo: text.value,
|
||||
})
|
||||
|
||||
emit('update-user-data', props.item, result.data.adminCreateContribution)
|
||||
|
||||
store.commit('openCreationsPlus', 1)
|
||||
|
||||
toastSuccess(
|
||||
t('creation_form.toasted', {
|
||||
value: value.value,
|
||||
email: props.item.email,
|
||||
}),
|
||||
)
|
||||
|
||||
onReset()
|
||||
} catch (error) {
|
||||
toastError(error.message)
|
||||
onReset()
|
||||
} finally {
|
||||
refetchCreations()
|
||||
selected.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => selected.value,
|
||||
async (newValue, oldValue) => {
|
||||
if (newValue !== oldValue && selected.value !== '') {
|
||||
updateRadioSelected(newValue)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateRadioSelected(name) {
|
||||
// do we want to reset the memo everytime the month changes?
|
||||
this.text = this.$t('creation_form.creation_for') + ' ' + name.short + ' ' + name.year
|
||||
this.rangeMin = 0
|
||||
this.rangeMax = Number(name.creation)
|
||||
},
|
||||
submitCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: adminCreateContribution,
|
||||
variables: {
|
||||
email: this.item.email,
|
||||
creationDate: this.selected.date,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('update-user-data', this.item, result.data.adminCreateContribution)
|
||||
this.$store.commit('openCreationsPlus', 1)
|
||||
this.toastSuccess(
|
||||
this.$t('creation_form.toasted', {
|
||||
value: this.value,
|
||||
email: this.item.email,
|
||||
}),
|
||||
)
|
||||
// what is this? Tests says that this.text is not reseted
|
||||
this.$refs.creationForm.reset()
|
||||
this.value = 0
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
this.$refs.creationForm.reset()
|
||||
this.value = 0
|
||||
})
|
||||
.finally(() => {
|
||||
this.$apollo.queries.OpenCreations.refetch()
|
||||
this.selected = ''
|
||||
})
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selected() {
|
||||
this.updateRadioSelected(this.selected)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style scoped>
|
||||
.buttons-wrapper {
|
||||
margin: 1.5rem 2.4rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,118 +1,113 @@
|
||||
<template>
|
||||
<div class="component-creation-transaction-list">
|
||||
<div class="h3">{{ $t('transactionlist.title') }}</div>
|
||||
<b-table striped hover :fields="fields" :items="items">
|
||||
<BTable striped hover :fields="fields" :items="items">
|
||||
<template #cell(contributionDate)="data">
|
||||
<div class="font-weight-bold">
|
||||
{{ $d(new Date(data.item.contributionDate), 'month') }}
|
||||
</div>
|
||||
<div>{{ $d(new Date(data.item.contributionDate)) }}</div>
|
||||
</template>
|
||||
</b-table>
|
||||
</BTable>
|
||||
<div>
|
||||
<b-pagination
|
||||
<BPagination
|
||||
v-model="currentPage"
|
||||
pills
|
||||
size="lg"
|
||||
v-model="currentPage"
|
||||
:per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
:hide-ellipsis="true"
|
||||
></b-pagination>
|
||||
<b-button v-b-toggle.collapse-1 variant="light" size="sm">{{ $t('help.help') }}</b-button>
|
||||
<b-collapse id="collapse-1" class="mt-2">
|
||||
/>
|
||||
<BButton v-b-toggle="'collapse-1'" variant="light" size="sm">{{ t('help.help') }}</BButton>
|
||||
<BCollapse id="collapse-1" class="mt-2">
|
||||
<div>
|
||||
{{ $t('transactionlist.submitted') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.submitted') }}
|
||||
{{ t('transactionlist.submitted') }} {{ t('math.equals') }}
|
||||
{{ t('help.transactionlist.submitted') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.period') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.periods') }}
|
||||
{{ t('transactionlist.period') }} {{ t('math.equals') }}
|
||||
{{ t('help.transactionlist.periods') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.confirmed') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.confirmed') }}
|
||||
{{ t('transactionlist.confirmed') }} {{ t('math.equals') }}
|
||||
{{ t('help.transactionlist.confirmed') }}
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('transactionlist.status') }} {{ $t('math.equals') }}
|
||||
{{ $t('help.transactionlist.status') }}
|
||||
{{ t('transactionlist.status') }} {{ t('math.equals') }}
|
||||
{{ t('help.transactionlist.status') }}
|
||||
</div>
|
||||
</b-collapse>
|
||||
</BCollapse>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { adminListContributions } from '../graphql/adminListContributions'
|
||||
export default {
|
||||
name: 'CreationTransactionList',
|
||||
props: {
|
||||
userId: { type: Number, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 10,
|
||||
fields: [
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('transactionlist.submitted'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('transactionlist.period'),
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: this.$t('transactionlist.confirmed'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: this.$t('transactionlist.status'),
|
||||
},
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('transactionlist.amount'),
|
||||
formatter: (value, key, item) => {
|
||||
return `${value} GDD`
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('transactionlist.memo'), class: 'text-break' },
|
||||
],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
AdminListContributions: {
|
||||
query() {
|
||||
return adminListContributions
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
order: 'DESC',
|
||||
userId: parseInt(this.userId),
|
||||
}
|
||||
},
|
||||
update({ adminListContributions }) {
|
||||
this.rows = adminListContributions.contributionCount
|
||||
this.items = adminListContributions.contributionList
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
import { BTable, BPagination, BButton, BCollapse, vBToggle } from 'bootstrap-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
userId: { type: Number, required: true },
|
||||
})
|
||||
|
||||
const items = ref([])
|
||||
const rows = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const perPage = ref(10)
|
||||
|
||||
const fields = [
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('transactionlist.submitted'),
|
||||
formatter: (value) => {
|
||||
return new Date(value).toLocaleDateString()
|
||||
},
|
||||
},
|
||||
}
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('transactionlist.period'),
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: t('transactionlist.confirmed'),
|
||||
formatter: (value) => {
|
||||
return value ? new Date(value).toLocaleDateString() : null
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: t('transactionlist.status'),
|
||||
},
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('transactionlist.amount'),
|
||||
formatter: (value) => {
|
||||
return `${value} GDD`
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: t('transactionlist.memo'), class: 'text-break' },
|
||||
]
|
||||
|
||||
const { result, refetch } = useQuery(adminListContributions, {
|
||||
currentPage: currentPage.value,
|
||||
pageSize: perPage.value,
|
||||
order: 'DESC',
|
||||
userId: props.userId,
|
||||
})
|
||||
|
||||
watch(result, (newResult) => {
|
||||
if (newResult && newResult.adminListContributions) {
|
||||
rows.value = newResult.adminListContributions.contributionCount
|
||||
items.value = newResult.adminListContributions.contributionList
|
||||
}
|
||||
})
|
||||
|
||||
watch(currentPage, () => {
|
||||
refetch()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,128 +1,97 @@
|
||||
<template>
|
||||
<div class="deleted-user-formular">
|
||||
<div v-if="item.userId === $store.state.moderator.id" class="mt-5 mb-5">
|
||||
<div v-if="isUserModerator" class="mt-5 mb-5">
|
||||
{{ $t('removeNotSelf') }}
|
||||
</div>
|
||||
<div v-else class="mt-5">
|
||||
<div class="mt-3 mb-5">
|
||||
<b-button
|
||||
<BButton
|
||||
v-if="!item.deletedAt"
|
||||
variant="danger"
|
||||
v-b-modal.delete-user-modal
|
||||
@click="showDeleteModal()"
|
||||
variant="danger"
|
||||
@click="showDeleteModal"
|
||||
>
|
||||
{{ $t('delete_user') }}
|
||||
</b-button>
|
||||
<b-button v-else variant="success" v-b-modal.delete-user-modal @click="showUndeleteModal()">
|
||||
</BButton>
|
||||
<BButton v-else v-b-modal.delete-user-modal variant="success" @click="showUndeleteModal">
|
||||
{{ $t('undelete_user') }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useApolloClient } from '@vue/apollo-composable'
|
||||
import { BButton, vBModal } from 'bootstrap-vue-next'
|
||||
import { deleteUser } from '../graphql/deleteUser'
|
||||
import { unDeleteUser } from '../graphql/unDeleteUser'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'DeletedUser',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showDeleteModal() {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm(
|
||||
this.$t('overlay.deleteUser.question', {
|
||||
username: `${this.item.firstName} ${this.item.lastName}`,
|
||||
}),
|
||||
{
|
||||
cancelTitle: this.$t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: this.$t('overlay.deleteUser.title'),
|
||||
okTitle: this.$t('overlay.deleteUser.yes'),
|
||||
okVariant: 'danger',
|
||||
static: true,
|
||||
},
|
||||
)
|
||||
.then((okClicked) => {
|
||||
if (okClicked) {
|
||||
this.deleteUser()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
showUndeleteModal() {
|
||||
this.$bvModal
|
||||
.msgBoxConfirm(
|
||||
this.$t('overlay.undeleteUser.question', {
|
||||
username: `${this.item.firstName} ${this.item.lastName}`,
|
||||
}),
|
||||
{
|
||||
cancelTitle: this.$t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: this.$t('overlay.undeleteUser.title'),
|
||||
okTitle: this.$t('overlay.undeleteUser.yes'),
|
||||
okVariant: 'success',
|
||||
},
|
||||
)
|
||||
.then((okClicked) => {
|
||||
if (okClicked) {
|
||||
this.unDeleteUser()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
deleteUser() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteUser,
|
||||
variables: {
|
||||
userId: this.item.userId,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('updateDeletedAt', {
|
||||
userId: this.item.userId,
|
||||
deletedAt: result.data.deleteUser,
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
unDeleteUser() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: unDeleteUser,
|
||||
variables: {
|
||||
userId: this.item.userId,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('updateDeletedAt', {
|
||||
userId: this.item.userId,
|
||||
deletedAt: result.data.unDeleteUser,
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update-deleted-at', 'show-delete-modal', 'show-undelete-modal'])
|
||||
|
||||
const { client } = useApolloClient()
|
||||
const store = useStore()
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
const isUserModerator = computed(() => props.item.userId === store.state.moderator.id)
|
||||
|
||||
const showDeleteModal = () => {
|
||||
emit('show-delete-modal')
|
||||
}
|
||||
|
||||
const showUndeleteModal = () => {
|
||||
emit('show-undelete-modal')
|
||||
}
|
||||
|
||||
const deleteUserMutation = async () => {
|
||||
try {
|
||||
const result = await client.mutate({
|
||||
mutation: deleteUser,
|
||||
variables: {
|
||||
userId: props.item.userId,
|
||||
},
|
||||
})
|
||||
emit('update-deleted-at', {
|
||||
userId: props.item.userId,
|
||||
deletedAt: result.data.deleteUser,
|
||||
})
|
||||
} catch (error) {
|
||||
toastError(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
const undeleteUserMutation = async () => {
|
||||
try {
|
||||
const result = await client.mutate({
|
||||
mutation: unDeleteUser,
|
||||
variables: {
|
||||
userId: props.item.userId,
|
||||
},
|
||||
})
|
||||
emit('update-deleted-at', {
|
||||
userId: props.item.userId,
|
||||
deletedAt: result.data.unDeleteUser,
|
||||
})
|
||||
} catch (error) {
|
||||
toastError(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ deleteUserMutation, undeleteUserMutation })
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.input-group-text {
|
||||
background-color: rgb(255, 252, 205);
|
||||
background-color: rgb(255 252 205);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,166 +1,157 @@
|
||||
<template>
|
||||
<div class="component-edit-creation-formular">
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<b-form ref="updateCreationForm">
|
||||
<BForm ref="updateCreationForm">
|
||||
<div class="ml-4">
|
||||
<label>{{ $t('creation_form.select_month') }}</label>
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-form-radio-group
|
||||
<BRow class="m-4">
|
||||
<BFormRadioGroup
|
||||
v-model="selected"
|
||||
:options="radioOptions"
|
||||
:options="creationMonths.radioOptions()"
|
||||
value-field="item"
|
||||
text-field="name"
|
||||
name="month-selection"
|
||||
:disabled="true"
|
||||
></b-form-radio-group>
|
||||
</b-row>
|
||||
></BFormRadioGroup>
|
||||
</BRow>
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.select_value') }}</label>
|
||||
<div>
|
||||
<b-input-group prepend="GDD" append=".00">
|
||||
<b-form-input
|
||||
<BInputGroup prepend="GDD" append=".00">
|
||||
<BFormInput
|
||||
v-model="value"
|
||||
type="number"
|
||||
v-model="value"
|
||||
:min="rangeMin"
|
||||
:max="rangeMax"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
|
||||
<b-form-input
|
||||
type="range"
|
||||
v-model="value"
|
||||
:min="rangeMin"
|
||||
:max="rangeMax"
|
||||
step="10"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
></BFormInput>
|
||||
</BInputGroup>
|
||||
<BInputGroup
|
||||
prepend="0"
|
||||
:append="String(rangeMax)"
|
||||
class="mt-3 flex-nowrap align-items-center"
|
||||
>
|
||||
<BFormInput v-model="value" type="range" :min="rangeMin" :max="rangeMax" step="10" />
|
||||
</BInputGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.enter_text') }}</label>
|
||||
<div>
|
||||
<b-form-textarea
|
||||
<BFormTextarea
|
||||
id="textarea-state"
|
||||
v-model="text"
|
||||
:state="text.length >= 10"
|
||||
placeholder="Mindestens 10 Zeichen eingeben"
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
></BFormTextarea>
|
||||
</div>
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-left">
|
||||
<b-button type="reset" variant="danger" @click="$refs.updateCreationForm.reset()">
|
||||
<BRow class="m-4">
|
||||
<BCol class="text-left">
|
||||
<BButton type="reset" variant="danger" @click="$refs.updateCreationForm.reset()">
|
||||
{{ $t('creation_form.reset') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-center">
|
||||
</BButton>
|
||||
</BCol>
|
||||
<BCol class="text-center">
|
||||
<div class="text-right">
|
||||
<b-button
|
||||
<BButton
|
||||
type="button"
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
@click="submitCreation"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
@click="submitCreation"
|
||||
>
|
||||
{{ $t('creation_form.update_creation') }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BForm>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { adminUpdateContribution } from '../graphql/adminUpdateContribution'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
|
||||
export default {
|
||||
name: 'EditCreationFormular',
|
||||
mixins: [creationMonths],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
row: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
creationUserData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useMutation, useQuery } from '@vue/apollo-composable'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { adminUpdateContribution } from '../graphql/adminUpdateContribution'
|
||||
import { adminOpenCreations } from '../graphql/adminOpenCreations'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import useCreationMonths from '@/composables/useCreationMonths'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
|
||||
value: !this.creationUserData.amount ? 0 : Number(this.creationUserData.amount),
|
||||
rangeMin: 0,
|
||||
selected: this.selectedComputed,
|
||||
userId: this.item.userId,
|
||||
}
|
||||
row: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
methods: {
|
||||
submitCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: adminUpdateContribution,
|
||||
variables: {
|
||||
id: this.item.id,
|
||||
creationDate: this.selected.date,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('update-creation-data')
|
||||
this.toastSuccess(
|
||||
this.$t('creation_form.toasted_update', {
|
||||
value: this.value,
|
||||
email: this.item.email,
|
||||
}),
|
||||
)
|
||||
// das creation Formular reseten
|
||||
this.$refs.updateCreationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
this.value = 0
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
// das creation Formular reseten
|
||||
this.$refs.updateCreationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
this.value = 0
|
||||
})
|
||||
.finally(() => {
|
||||
this.$apollo.queries.OpenCreations.refetch()
|
||||
})
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
creationIndex() {
|
||||
const month = this.$d(new Date(this.item.contributionDate), 'month')
|
||||
return this.radioOptions.findIndex((obj) => {
|
||||
return obj.item.short === month
|
||||
})
|
||||
},
|
||||
selectedComputed() {
|
||||
return this.radioOptions[this.creationIndex].item
|
||||
},
|
||||
rangeMax() {
|
||||
return Number(this.creation[this.creationIndex]) + Number(this.item.amount)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedComputed() {
|
||||
this.selected = this.selectedComputed
|
||||
},
|
||||
creationUserData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update-creation-data'])
|
||||
const creationMonths = useCreationMonths()
|
||||
|
||||
const { t, d } = useI18n()
|
||||
const { toastSuccess, toastError } = useAppToast()
|
||||
const text = ref(props.creationUserData.memo || '')
|
||||
const value = ref(props.creationUserData.amount ? Number(props.creationUserData.amount) : 0)
|
||||
const rangeMin = ref(0)
|
||||
|
||||
const creationIndex = computed(() => {
|
||||
const month = d(new Date(props.item.contributionDate), 'month')
|
||||
return creationMonths.radioOptions().findIndex((obj) => obj.item.short === month)
|
||||
})
|
||||
|
||||
const selectedComputed = computed(() => creationMonths.radioOptions()[creationIndex.value].item)
|
||||
const selected = ref(selectedComputed.value)
|
||||
const rangeMax = computed(
|
||||
() => Number(creationMonths.creation.value[creationIndex.value]) + Number(props.item.amount),
|
||||
)
|
||||
|
||||
watch(selectedComputed, () => {
|
||||
selected.value = selectedComputed.value
|
||||
})
|
||||
|
||||
const { mutate: updateMutation, onDone, onError } = useMutation(adminUpdateContribution)
|
||||
|
||||
onDone(() => {
|
||||
emit('update-creation-data')
|
||||
toastSuccess(t('creation_form.toasted_update', { value: value.value, email: props.item.email }))
|
||||
resetForm()
|
||||
refetchOpenCreations()
|
||||
})
|
||||
|
||||
onError((error) => {
|
||||
toastError(error.message)
|
||||
resetForm()
|
||||
})
|
||||
|
||||
const { refetch: refetchCreations } = useQuery(adminOpenCreations)
|
||||
|
||||
const submitCreation = () => {
|
||||
updateMutation({
|
||||
id: props.item.id,
|
||||
creationDate: selected.value.date,
|
||||
amount: Number(value.value),
|
||||
memo: text.value,
|
||||
})
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
value.value = 0
|
||||
}
|
||||
|
||||
const refetchOpenCreations = () => {
|
||||
refetchCreations()
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,38 +1,38 @@
|
||||
<template>
|
||||
<div class="community-visualize-item">
|
||||
<b-row @click="toggleDetails">
|
||||
<b-col cols="1"><b-icon :icon="icon" :variant="variant" class="mr-4"></b-icon></b-col>
|
||||
<b-col>
|
||||
<BRow @click="toggleDetails">
|
||||
<BCol cols="1"><b-icon :icon="icon" :variant="variant" class="mr-4"></b-icon></BCol>
|
||||
<BCol>
|
||||
<div>
|
||||
<a :href="item.url" target="_blank">{{ item.url }}</a>
|
||||
</div>
|
||||
<small>{{ `${item.publicKey.substring(0, 26)}…` }}</small>
|
||||
</b-col>
|
||||
<b-col v-b-tooltip="item.description">{{ item.name }}</b-col>
|
||||
<b-col cols="2">{{ lastAnnouncedAt }}</b-col>
|
||||
<b-col cols="2">{{ createdAt }}</b-col>
|
||||
</b-row>
|
||||
<b-row v-if="details" class="details">
|
||||
<b-col colspan="5">
|
||||
<b-list-group>
|
||||
<b-list-group-item v-if="item.uuid">
|
||||
</BCol>
|
||||
<BCol v-b-tooltip="item.description">{{ item.name }}</BCol>
|
||||
<BCol cols="2">{{ lastAnnouncedAt }}</BCol>
|
||||
<BCol cols="2">{{ createdAt }}</BCol>
|
||||
</BRow>
|
||||
<BRow v-if="details" class="details">
|
||||
<BCol colspan="5">
|
||||
<BListGroup>
|
||||
<BListGroupItem v-if="item.uuid">
|
||||
{{ $t('federation.communityUuid') }} {{ item.uuid }}
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-if="item.authenticatedAt">
|
||||
</BListGroupItem>
|
||||
<BListGroupItem v-if="item.authenticatedAt">
|
||||
{{ $t('federation.authenticatedAt') }} {{ item.authenticatedAt }}
|
||||
</b-list-group-item>
|
||||
<b-list-group-item>
|
||||
</BListGroupItem>
|
||||
<BListGroupItem>
|
||||
{{ $t('federation.publicKey') }} {{ item.publicKey }}
|
||||
</b-list-group-item>
|
||||
<b-list-group-item v-if="!item.foreign">
|
||||
</BListGroupItem>
|
||||
<BListGroupItem v-if="!item.foreign">
|
||||
<editable-group
|
||||
:allowEdit="$store.state.moderator.roles.includes('ADMIN')"
|
||||
:allow-edit="$store.state.moderator.roles.includes('ADMIN')"
|
||||
@save="handleUpdateHomeCommunity"
|
||||
@reset="resetHomeCommunityEditable"
|
||||
>
|
||||
<template #view>
|
||||
<label>{{ $t('federation.gmsApiKey') }} {{ gmsApiKey }}</label>
|
||||
<b-form-group>
|
||||
<BFormGroup>
|
||||
{{ $t('federation.coordinates') }}
|
||||
<span v-if="isValidLocation">
|
||||
{{
|
||||
@ -42,40 +42,40 @@
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</b-form-group>
|
||||
</BFormGroup>
|
||||
</template>
|
||||
<template #edit>
|
||||
<editable-groupable-label
|
||||
v-model="gmsApiKey"
|
||||
:label="$t('federation.gmsApiKey')"
|
||||
idName="home-community-api-key"
|
||||
id-name="home-community-api-key"
|
||||
/>
|
||||
<coordinates v-model="location" />
|
||||
</template>
|
||||
</editable-group>
|
||||
</b-list-group-item>
|
||||
<b-list-group-item>
|
||||
<b-list-group>
|
||||
<b-row>
|
||||
<b-col class="ml-1">{{ $t('federation.verified') }}</b-col>
|
||||
<b-col>{{ $t('federation.apiVersion') }}</b-col>
|
||||
<b-col>{{ $t('federation.createdAt') }}</b-col>
|
||||
<b-col>{{ $t('federation.lastAnnouncedAt') }}</b-col>
|
||||
<b-col>{{ $t('federation.verifiedAt') }}</b-col>
|
||||
<b-col>{{ $t('federation.lastErrorAt') }}</b-col>
|
||||
</b-row>
|
||||
<b-list-group-item
|
||||
</BListGroupItem>
|
||||
<BListGroup-item>
|
||||
<BListGroup>
|
||||
<BRow>
|
||||
<BCol class="ml-1">{{ $t('federation.verified') }}</BCol>
|
||||
<BCol>{{ $t('federation.apiVersion') }}</BCol>
|
||||
<BCol>{{ $t('federation.createdAt') }}</BCol>
|
||||
<BCol>{{ $t('federation.lastAnnouncedAt') }}</BCol>
|
||||
<BCol>{{ $t('federation.verifiedAt') }}</BCol>
|
||||
<BCol>{{ $t('federation.lastErrorAt') }}</BCol>
|
||||
</BRow>
|
||||
<BListGroup-item
|
||||
v-for="federation in item.federatedCommunities"
|
||||
:key="federation.id"
|
||||
:variant="!item.foreign ? 'primary' : 'warning'"
|
||||
>
|
||||
<federation-visualize-item :item="federation" />
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</BListGroup-item>
|
||||
</BListGroup>
|
||||
</BListGroup-item>
|
||||
</BListGroup>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="figure-qr-code">
|
||||
<div class="qrbox">
|
||||
<q-r-canvas :options="options" class="canvas" />
|
||||
<q-r-canvas v-if="showQr" :options="qrOptions" class="canvas" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -18,26 +18,26 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
image: null,
|
||||
showQr: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
qrOptions() {
|
||||
return {
|
||||
cellSize: 8,
|
||||
correctLevel: 'H',
|
||||
data: this.link,
|
||||
logo: {
|
||||
image: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
logo: { image: this.image },
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const image = new Image()
|
||||
image.src = 'img/gdd-coin.png'
|
||||
image.src = '/img/gdd-coin.png'
|
||||
image.onload = () => {
|
||||
this.options = {
|
||||
...this.options,
|
||||
logo: {
|
||||
image,
|
||||
},
|
||||
}
|
||||
this.image = image
|
||||
this.showQr = true
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -45,12 +45,13 @@ export default {
|
||||
<style scoped>
|
||||
.qrbox {
|
||||
padding: 20px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
background-color: rgb(255 255 255);
|
||||
}
|
||||
|
||||
.canvas {
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
padding: 5px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
background-color: rgb(255 255 255);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,59 +1,98 @@
|
||||
<template>
|
||||
<div class="component-nabvar">
|
||||
<b-navbar toggleable="lg" type="dark" class="bg-dark">
|
||||
<b-navbar-brand class="mb-2" to="/">
|
||||
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img pl-2" alt="..." />
|
||||
</b-navbar-brand>
|
||||
<BNavbar v-b-color-mode="'dark'" toggleable="lg" variant="light-dark">
|
||||
<BNavbarBrand class="mb-2" to="/">
|
||||
<img
|
||||
src="../../public/img/brand/gradido_logo_w.png"
|
||||
class="navbar-brand-img pl-2"
|
||||
alt="..."
|
||||
/>
|
||||
</BNavbarBrand>
|
||||
|
||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||
<BNavbarToggle v-b-toggle.nav-collapse target="navbar-toggle-collapse" />
|
||||
|
||||
<b-collapse id="nav-collapse" is-nav>
|
||||
<b-navbar-nav>
|
||||
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
|
||||
<b-nav-item class="bg-color-creation" to="/creation-confirm">
|
||||
<BCollapse id="nav-collapse" is-nav>
|
||||
<BNavbarNav>
|
||||
<BNavItem :active="isActive('user')" to="/user">
|
||||
{{ $t('navbar.user_search') }}
|
||||
</BNavItem>
|
||||
<BNavItem
|
||||
:active="isActive('creation-confirm')"
|
||||
class="bg-color-creation"
|
||||
to="/creation-confirm"
|
||||
>
|
||||
{{ $t('creation') }}
|
||||
<b-badge v-show="$store.state.openCreations > 0" variant="danger">
|
||||
{{ $store.state.openCreations }}
|
||||
</b-badge>
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/contribution-links">
|
||||
<BBadge v-show="openCreations > 0" variant="danger">
|
||||
{{ openCreations }}
|
||||
</BBadge>
|
||||
</BNavItem>
|
||||
<BNavItem to="/contribution-links" :active="isActive('contribution-links')">
|
||||
{{ $t('navbar.automaticContributions') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/federation">
|
||||
</BNavItem>
|
||||
<BNavItem to="/federation" :active="isActive('federation')">
|
||||
{{ $t('navbar.instances') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item to="/statistic">{{ $t('navbar.statistic') }}</b-nav-item>
|
||||
<b-nav-item @click="wallet">{{ $t('navbar.my-account') }}</b-nav-item>
|
||||
<b-nav-item @click="logout">{{ $t('navbar.logout') }}</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
</BNavItem>
|
||||
<BNavItem to="/statistic" :active="isActive('statistic')">
|
||||
{{ $t('navbar.statistic') }}
|
||||
</BNavItem>
|
||||
<BNavItem @click="handleWallet">{{ $t('navbar.my-account') }}</BNavItem>
|
||||
<BNavItem @click="handleLogout">{{ $t('navbar.logout') }}</BNavItem>
|
||||
</BNavbarNav>
|
||||
</BCollapse>
|
||||
</BNavbar>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import CONFIG from '../config'
|
||||
import { useStore } from 'vuex'
|
||||
import { computed } from 'vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { logout } from '../graphql/logout'
|
||||
import {
|
||||
BNavbar,
|
||||
BCollapse,
|
||||
BNavbarNav,
|
||||
BNavItem,
|
||||
BNavbarBrand,
|
||||
BBadge,
|
||||
BNavbarToggle,
|
||||
vBToggle,
|
||||
vBColorMode,
|
||||
} from 'bootstrap-vue-next'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default {
|
||||
name: 'navbar',
|
||||
methods: {
|
||||
async logout() {
|
||||
window.location.assign(CONFIG.WALLET_LOGIN_URL)
|
||||
// window.location = CONFIG.WALLET_LOGIN_URL
|
||||
this.$store.dispatch('logout')
|
||||
await this.$apollo.mutate({
|
||||
mutation: logout,
|
||||
})
|
||||
},
|
||||
wallet() {
|
||||
window.location = CONFIG.WALLET_AUTH_URL.replace('{token}', this.$store.state.token)
|
||||
this.$store.dispatch('logout') // logout without redirect
|
||||
},
|
||||
},
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
|
||||
const openCreations = computed(() => store.state.openCreations)
|
||||
|
||||
const { mutate: executeLogout } = useMutation(logout)
|
||||
|
||||
const handleLogout = async () => {
|
||||
window.location.assign(CONFIG.WALLET_LOGIN_URL)
|
||||
// window.location = CONFIG.WALLET_LOGIN_URL
|
||||
await store.dispatch('logout')
|
||||
await executeLogout()
|
||||
}
|
||||
|
||||
const handleWallet = () => {
|
||||
window.location = CONFIG.WALLET_AUTH_URL.replace('{token}', store.state.token)
|
||||
store.dispatch('logout') // logout without redirect
|
||||
}
|
||||
|
||||
const isActive = (tabRoute) => {
|
||||
return tabRoute === route.name
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
<style lang="scss" scoped>
|
||||
.navbar-brand-img {
|
||||
height: 2rem;
|
||||
padding-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.bg-light-dark {
|
||||
background-color: #343a40;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1195,7 +1195,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'not-found',
|
||||
name: 'NotFound',
|
||||
data() {
|
||||
return {
|
||||
anime: {
|
||||
@ -1249,21 +1249,23 @@ export default {
|
||||
transform-box: fill-box;
|
||||
}
|
||||
|
||||
/*************swing************/
|
||||
/************* swing ************/
|
||||
@keyframes swing {
|
||||
0% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************swing hair************/
|
||||
/************* swing hair ************/
|
||||
@keyframes swinghair {
|
||||
0% {
|
||||
transform: rotate(6deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(-6deg);
|
||||
}
|
||||
|
||||
@ -1,60 +1,65 @@
|
||||
<template>
|
||||
<div class="component-overlay">
|
||||
<b-jumbotron class="bg-light p-4">
|
||||
<template #header><slot name="title" /></template>
|
||||
<BCard class="bg-light p-4">
|
||||
<h1 class="display-3"><slot name="title" /></h1>
|
||||
|
||||
<template #lead>
|
||||
<b-row class="mt-4">
|
||||
<b-col class="col-3">{{ $t('transactionlist.amount') }}</b-col>
|
||||
<b-col class="h3">
|
||||
<b>{{ item.amount }} {{ $t('GDD') }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col class="col-3">{{ $t('creation_for_month') }}</b-col>
|
||||
<b-col class="h3">
|
||||
{{ $d(new Date(item.contributionDate), 'month') }}
|
||||
{{ $d(new Date(item.contributionDate), 'year') }}
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col class="col-3">{{ $t('transactionlist.memo') }}</b-col>
|
||||
<b-col>{{ item.memo }}</b-col>
|
||||
</b-row>
|
||||
<b-row class="mt-3">
|
||||
<b-col class="col-3">{{ $t('name') }}</b-col>
|
||||
<b-col>{{ item.firstName }} {{ item.lastName }}</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col class="col-3">{{ $t('e_mail') }}</b-col>
|
||||
<b-col>{{ item.email }}</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
<!-- <template #lead>-->
|
||||
<BRow class="mt-4">
|
||||
<BCol class="col-3">{{ $t('transactionlist.amount') }}</BCol>
|
||||
<BCol class="h3">
|
||||
<b>{{ item.amount }} {{ $t('GDD') }}</b>
|
||||
</BCol>
|
||||
</BRow>
|
||||
<BRow>
|
||||
<BCol class="col-3">{{ $t('creation_for_month') }}</BCol>
|
||||
<BCol class="h3">
|
||||
{{ $d(new Date(item.contributionDate), 'month') }}
|
||||
{{ $d(new Date(item.contributionDate), 'year') }}
|
||||
</BCol>
|
||||
</BRow>
|
||||
<BRow>
|
||||
<BCol class="col-3">{{ $t('transactionlist.memo') }}</BCol>
|
||||
<BCol>{{ item.memo }}</BCol>
|
||||
</BRow>
|
||||
<BRow class="mt-3">
|
||||
<BCol class="col-3">{{ $t('name') }}</BCol>
|
||||
<BCol>{{ item.firstName }} {{ item.lastName }}</BCol>
|
||||
</BRow>
|
||||
<BRow>
|
||||
<BCol class="col-3">{{ $t('e_mail') }}</BCol>
|
||||
<BCol>{{ item.email }}</BCol>
|
||||
</BRow>
|
||||
|
||||
<hr class="my-4" />
|
||||
<slot name="text" />
|
||||
<slot name="question" />
|
||||
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-button size="md" variant="info" class="m-3" @click="$emit('overlay-cancel')">
|
||||
<BContainer>
|
||||
<BRow>
|
||||
<BCol>
|
||||
<BButton
|
||||
size="md"
|
||||
variant="info"
|
||||
class="m-3 text-light"
|
||||
@click="$emit('overlay-cancel')"
|
||||
>
|
||||
{{ $t('overlay.cancel') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
</BButton>
|
||||
</BCol>
|
||||
<BCol class="text-right">
|
||||
<slot name="submit-btn" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-jumbotron>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BContainer>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'overlay',
|
||||
name: 'Overlay',
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
},
|
||||
emits: ['overlay-cancel'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -16,9 +16,10 @@ export default {
|
||||
name: 'RowDetails',
|
||||
props: {
|
||||
row: { required: true, type: Object },
|
||||
slotName: { requried: true, type: String },
|
||||
type: { requried: true, type: String },
|
||||
index: { requried: true, type: Number },
|
||||
slotName: { required: true, type: String },
|
||||
type: { required: true, type: String },
|
||||
index: { required: true, type: Number },
|
||||
},
|
||||
emits: ['row-toggle-details'],
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="open-creations-table">
|
||||
<b-table-lite
|
||||
<BTableLite
|
||||
:items="items"
|
||||
:fields="fields"
|
||||
caption-top
|
||||
@ -10,18 +10,22 @@
|
||||
:tbody-tr-class="rowClass"
|
||||
>
|
||||
<template #cell(status)="row">
|
||||
<b-icon :icon="getStatusIcon(row.item.status)"></b-icon>
|
||||
<IBiQuestionSquare v-if="row.item.status === 'IN_PROGRESS'" />
|
||||
<IBiBellFill v-else-if="row.item.status === 'PENDING'" />
|
||||
<IBiCheck v-else-if="row.item.status === 'CONFIRMED'" />
|
||||
<IBiXCircle v-else-if="row.item.status === 'DENIED'" />
|
||||
<IBiTrash v-else-if="row.item.status === 'DELETED'" />
|
||||
</template>
|
||||
<template #cell(bookmark)="row">
|
||||
<div v-if="!myself(row.item)">
|
||||
<b-button
|
||||
<BButton
|
||||
variant="danger"
|
||||
size="md"
|
||||
@click="$emit('show-overlay', row.item, 'delete')"
|
||||
class="mr-2"
|
||||
@click="$emit('show-overlay', row.item, 'delete')"
|
||||
>
|
||||
<b-icon icon="trash" variant="light"></b-icon>
|
||||
</b-button>
|
||||
<IBiTrash />
|
||||
</BButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #cell(memo)="row">
|
||||
@ -33,66 +37,66 @@
|
||||
</template>
|
||||
<template #cell(editCreation)="row">
|
||||
<div v-if="!myself(row.item)">
|
||||
<b-button
|
||||
<BButton
|
||||
v-if="row.item.moderatorId"
|
||||
variant="info"
|
||||
size="md"
|
||||
:index="0"
|
||||
@click="rowToggleDetails(row, 0)"
|
||||
class="mr-2"
|
||||
@click="rowToggleDetails(row, 0)"
|
||||
>
|
||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||
</b-button>
|
||||
<b-button v-else @click="rowToggleDetails(row, 0)">
|
||||
<b-icon icon="chat-dots"></b-icon>
|
||||
<b-icon
|
||||
<IBiX v-if="row.detailsShowing" />
|
||||
<IBiPencilSquare v-else />
|
||||
</BButton>
|
||||
<BButton v-else @click="rowToggleDetails(row, 0)">
|
||||
<IBiChatDots />
|
||||
<IBiExclamationCircleFill
|
||||
v-if="row.item.status === 'PENDING' && row.item.messagesCount > 0"
|
||||
icon="exclamation-circle-fill"
|
||||
variant="warning"
|
||||
></b-icon>
|
||||
<b-icon
|
||||
style="color: #ffc107"
|
||||
/>
|
||||
<IBiQuestionDiamond
|
||||
v-if="row.item.status === 'IN_PROGRESS' && row.item.messagesCount > 0"
|
||||
icon="question-diamond"
|
||||
variant="warning"
|
||||
style="color: #ffc107"
|
||||
class="pl-1"
|
||||
></b-icon>
|
||||
</b-button>
|
||||
/>
|
||||
</BButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #cell(chatCreation)="row">
|
||||
<b-button v-if="row.item.messagesCount > 0" @click="rowToggleDetails(row, 0)">
|
||||
<b-icon icon="chat-dots"></b-icon>
|
||||
</b-button>
|
||||
<BButton v-if="row.item.messagesCount > 0" @click="rowToggleDetails(row, 0)">
|
||||
<IBiChatDots />
|
||||
</BButton>
|
||||
</template>
|
||||
<template #cell(deny)="row">
|
||||
<div v-if="!myself(row.item)">
|
||||
<b-button
|
||||
<BButton
|
||||
variant="warning"
|
||||
size="md"
|
||||
@click="$emit('show-overlay', row.item, 'deny')"
|
||||
class="mr-2"
|
||||
@click="$emit('show-overlay', row.item, 'deny')"
|
||||
>
|
||||
<b-icon icon="x" variant="light"></b-icon>
|
||||
</b-button>
|
||||
<IBiX />
|
||||
</BButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #cell(confirm)="row">
|
||||
<div v-if="!myself(row.item)">
|
||||
<b-button
|
||||
<BButton
|
||||
variant="success"
|
||||
size="md"
|
||||
@click="$emit('show-overlay', row.item, 'confirm')"
|
||||
class="mr-2"
|
||||
@click="$emit('show-overlay', row.item, 'confirm')"
|
||||
>
|
||||
<b-icon icon="check" scale="2" variant=""></b-icon>
|
||||
</b-button>
|
||||
<IBiCheck />
|
||||
</BButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #row-details="row">
|
||||
<row-details
|
||||
:row="row"
|
||||
type="show-creation"
|
||||
slotName="show-creation"
|
||||
slot-name="show-creation"
|
||||
:index="0"
|
||||
@row-toggle-details="rowToggleDetails(row, 0)"
|
||||
>
|
||||
@ -102,18 +106,18 @@
|
||||
type="singleCreation"
|
||||
:item="row.item"
|
||||
:row="row"
|
||||
:creationUserData="creationUserData"
|
||||
:creation-user-data="creationUserData"
|
||||
@update-creation-data="$emit('update-contributions')"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<contribution-messages-list
|
||||
:contributionId="row.item.id"
|
||||
:contributionStatus="row.item.status"
|
||||
:contributionUserId="row.item.userId"
|
||||
:contributionMemo="row.item.memo"
|
||||
:resubmissionAt="row.item.resubmissionAt"
|
||||
:hideResubmission="hideResubmission"
|
||||
:contribution-id="row.item.id"
|
||||
:contribution-status="row.item.status"
|
||||
:contribution-user-id="row.item.userId"
|
||||
:contribution-memo="row.item.memo"
|
||||
:resubmission-at="row.item.resubmissionAt"
|
||||
:hide-resubmission="hideResubmission"
|
||||
@update-status="updateStatus"
|
||||
@reload-contribution="reloadContribution"
|
||||
@update-contributions="updateContributions"
|
||||
@ -122,7 +126,7 @@
|
||||
</template>
|
||||
</row-details>
|
||||
</template>
|
||||
</b-table-lite>
|
||||
</BTableLite>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -142,12 +146,12 @@ const iconMap = {
|
||||
|
||||
export default {
|
||||
name: 'OpenCreationsTable',
|
||||
mixins: [toggleRowDetails],
|
||||
components: {
|
||||
EditCreationFormular,
|
||||
RowDetails,
|
||||
ContributionMessagesList,
|
||||
},
|
||||
mixins: [toggleRowDetails],
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
@ -166,6 +170,7 @@ export default {
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
emits: ['update-contributions', 'reload-contribution', 'update-status', 'show-overlay'],
|
||||
methods: {
|
||||
myself(item) {
|
||||
return item.userId === this.$store.state.moderator.id
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="search-user-table">
|
||||
<b-table
|
||||
<BTable
|
||||
tbody-tr-class="pointer"
|
||||
:items="myItems"
|
||||
:fields="fields"
|
||||
@ -9,151 +9,294 @@
|
||||
hover
|
||||
stacked="md"
|
||||
select-mode="single"
|
||||
selectableonRowSelected
|
||||
selectable-on-row-selected
|
||||
@row-clicked="onRowClicked"
|
||||
>
|
||||
<template #cell(creation)="data">
|
||||
<div v-html="data.value"></div>
|
||||
<div v-html="data.value" />
|
||||
</template>
|
||||
|
||||
<template #cell(status)="row">
|
||||
<div class="text-right">
|
||||
<b-avatar v-if="row.item.deletedAt" class="mr-3 test-deleted-icon" variant="light">
|
||||
<b-iconstack font-scale="2">
|
||||
<b-icon stacked icon="person" variant="info" scale="0.75"></b-icon>
|
||||
<b-icon stacked icon="slash-circle" variant="danger"></b-icon>
|
||||
</b-iconstack>
|
||||
</b-avatar>
|
||||
<span v-if="!row.item.deletedAt">
|
||||
<b-avatar
|
||||
<div class="d-flex gap-3 justify-content-end align-items-center">
|
||||
<div
|
||||
v-if="row.item.deletedAt"
|
||||
class="mr-3 test-deleted-icon position-relative rounded-circle"
|
||||
style="width: 40px; height: 40px"
|
||||
>
|
||||
<img src="../../assets/icons/circle-slash.png" class="position-absolute" />
|
||||
<img
|
||||
src="../../assets/icons/person.png"
|
||||
class="position-relative"
|
||||
style="transform: translate(50%, 30%)"
|
||||
/>
|
||||
</div>
|
||||
<span v-if="!row.item.deletedAt" class="d-flex gap-2">
|
||||
<div
|
||||
v-if="!row.item.emailChecked"
|
||||
icon="envelope"
|
||||
class="align-center mr-3"
|
||||
variant="danger"
|
||||
></b-avatar>
|
||||
|
||||
<b-avatar
|
||||
v-if="!row.item.hasElopage"
|
||||
variant="danger"
|
||||
class="mr-3"
|
||||
src="img/elopage_favicon.png"
|
||||
></b-avatar>
|
||||
class="mr-3 rounded-circle position-relative"
|
||||
style="background-color: #dc3545; width: 40px; height: 40px"
|
||||
>
|
||||
<img
|
||||
src="../../assets/icons/envelope.png"
|
||||
style="transform: translate(30%, 30%); width: 25px; height: 25px"
|
||||
class="position-absolute"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
v-if="!row.item.hasElopage"
|
||||
class="mr-3 rounded-circle bg-red-dark"
|
||||
src="../../assets/icons/elopage_favicon.png"
|
||||
style="background-color: #dc3545; width: 40px; height: 40px"
|
||||
/>
|
||||
</div>
|
||||
</span>
|
||||
<b-icon
|
||||
variant="dark"
|
||||
:icon="row.detailsShowing ? 'caret-up-fill' : 'caret-down'"
|
||||
<IPhCaretUpFill
|
||||
v-if="row.detailsShowing === 'caret-up-fill'"
|
||||
style="color: #212529"
|
||||
:title="row.item.enabled ? $t('enabled') : $t('deleted')"
|
||||
></b-icon>
|
||||
/>
|
||||
<IPhCaretDown
|
||||
v-else
|
||||
style="color: #212529"
|
||||
:title="row.item.enabled ? $t('enabled') : $t('deleted')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #row-details="row">
|
||||
<b-card ref="rowDetails" class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||
<b-tabs content-class="mt-3">
|
||||
<b-tab :title="$t('creation')" active :disabled="row.item.deletedAt !== null">
|
||||
<BCard ref="rowDetails" class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||
<BTabs content-class="mt-3">
|
||||
<BTab :title="$t('creation')" active :disabled="row.item.deletedAt !== null">
|
||||
<creation-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
pagetype="singleCreation"
|
||||
:creation="row.item.creation"
|
||||
:item="row.item"
|
||||
:creationUserData="creationUserData"
|
||||
:creation-user-data="creationUserData"
|
||||
@update-user-data="updateUserData"
|
||||
/>
|
||||
</b-tab>
|
||||
<b-tab :title="$t('e_mail')" :disabled="row.item.deletedAt !== null">
|
||||
</BTab>
|
||||
<BTab :title="$t('e_mail')" :disabled="row.item.deletedAt !== null">
|
||||
<confirm-register-mail-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
:checked="row.item.emailChecked"
|
||||
:email="row.item.email"
|
||||
:dateLastSend="
|
||||
:date-last-send="
|
||||
row.item.emailConfirmationSend
|
||||
? $d(new Date(row.item.emailConfirmationSend), 'long')
|
||||
: ''
|
||||
"
|
||||
/>
|
||||
</b-tab>
|
||||
<b-tab :title="$t('creationList')" :disabled="row.item.deletedAt !== null">
|
||||
<creation-transaction-list v-if="!row.item.deletedAt" :userId="row.item.userId" />
|
||||
</b-tab>
|
||||
<b-tab :title="$t('transactionlink.name')" :disabled="row.item.deletedAt !== null">
|
||||
<transaction-link-list v-if="!row.item.deletedAt" :userId="row.item.userId" />
|
||||
</b-tab>
|
||||
<b-tab :title="$t('userRole.tabTitle')">
|
||||
<change-user-role-formular :item="row.item" @updateRoles="updateRoles" />
|
||||
</b-tab>
|
||||
<b-tab v-if="$store.state.moderator.roles.includes('ADMIN')" :title="$t('delete_user')">
|
||||
<deleted-user-formular :item="row.item" @updateDeletedAt="updateDeletedAt" />
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</b-card>
|
||||
</BTab>
|
||||
<BTab :title="$t('creationList')" :disabled="row.item.deletedAt !== null">
|
||||
<creation-transaction-list v-if="!row.item.deletedAt" :user-id="row.item.userId" />
|
||||
</BTab>
|
||||
<BTab :title="$t('transactionlink.name')" :disabled="row.item.deletedAt !== null">
|
||||
<transaction-link-list v-if="!row.item.deletedAt" :user-id="row.item.userId" />
|
||||
</BTab>
|
||||
<BTab :title="$t('userRole.tabTitle')">
|
||||
<change-user-role-formular
|
||||
ref="userChangeForm"
|
||||
:item="row.item"
|
||||
@update-roles="updateRoles"
|
||||
@show-modal="showModal"
|
||||
/>
|
||||
</BTab>
|
||||
<BTab v-if="store.state.moderator.roles.includes('ADMIN')" :title="$t('delete_user')">
|
||||
<deleted-user-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
ref="deletedUserForm"
|
||||
:item="row.item"
|
||||
@update-deleted-at="updateDeletedAt"
|
||||
@show-delete-modal="showDeleteModal"
|
||||
/>
|
||||
<deleted-user-formular
|
||||
v-else
|
||||
ref="undeletedUserForm"
|
||||
:item="row.item"
|
||||
@show-undelete-modal="showUndeleteModal"
|
||||
/>
|
||||
</BTab>
|
||||
</BTabs>
|
||||
</BCard>
|
||||
</template>
|
||||
</b-table>
|
||||
</BTable>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CreationFormular from '../CreationFormular'
|
||||
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular'
|
||||
import CreationTransactionList from '../CreationTransactionList'
|
||||
import TransactionLinkList from '../TransactionLinkList'
|
||||
import ChangeUserRoleFormular from '../ChangeUserRoleFormular'
|
||||
import DeletedUserFormular from '../DeletedUserFormular'
|
||||
<script setup>
|
||||
import { ref, nextTick, onMounted, watch, computed } from 'vue'
|
||||
import { BTable, BTab, BTabs, BCard, useModalController } from 'bootstrap-vue-next'
|
||||
import { useStore } from 'vuex'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import CreationFormular from '../CreationFormular.vue'
|
||||
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue'
|
||||
import CreationTransactionList from '../CreationTransactionList.vue'
|
||||
import TransactionLinkList from '../TransactionLinkList.vue'
|
||||
import ChangeUserRoleFormular from '../ChangeUserRoleFormular.vue'
|
||||
import DeletedUserFormular from '../DeletedUserFormular.vue'
|
||||
|
||||
export default {
|
||||
name: 'SearchUserTable',
|
||||
components: {
|
||||
CreationFormular,
|
||||
ConfirmRegisterMailFormular,
|
||||
CreationTransactionList,
|
||||
TransactionLinkList,
|
||||
ChangeUserRoleFormular,
|
||||
DeletedUserFormular,
|
||||
const { t } = useI18n()
|
||||
const { confirm } = useModalController()
|
||||
const store = useStore()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
creationUserData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateUserData(rowItem, newCreation) {
|
||||
rowItem.creation = newCreation
|
||||
},
|
||||
updateRoles({ userId, roles }) {
|
||||
this.$emit('updateRoles', userId, roles)
|
||||
},
|
||||
updateDeletedAt({ userId, deletedAt }) {
|
||||
this.$emit('updateDeletedAt', userId, deletedAt)
|
||||
},
|
||||
async onRowClicked(item) {
|
||||
const status = this.myItems.find((obj) => obj === item)._showDetails
|
||||
this.myItems.forEach((obj) => {
|
||||
if (obj === item) {
|
||||
obj._showDetails = !status
|
||||
} else {
|
||||
obj._showDetails = false
|
||||
}
|
||||
})
|
||||
await this.$nextTick()
|
||||
if (!status && this.$refs.rowDetails) {
|
||||
this.$refs.rowDetails.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
myItems() {
|
||||
return this.items.map((item) => {
|
||||
return { ...item, _showDetails: false }
|
||||
})
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const rolesValues = {
|
||||
ADMIN: 'ADMIN',
|
||||
MODERATOR: 'MODERATOR',
|
||||
USER: 'USER',
|
||||
}
|
||||
|
||||
const userChangeForm = ref()
|
||||
const deletedUserForm = ref()
|
||||
const undeletedUserForm = ref()
|
||||
const myItems = ref()
|
||||
const creationUserData = ref({})
|
||||
const rowDetails = ref()
|
||||
|
||||
const showModal = async () => {
|
||||
await confirm?.({
|
||||
props: {
|
||||
cancelTitle: t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: t('overlay.changeUserRole.title'),
|
||||
okTitle: t('overlay.changeUserRole.yes'),
|
||||
okVariant: 'danger',
|
||||
body: t('overlay.changeUserRole.question', {
|
||||
username: `${selectedRow.value.firstName} ${selectedRow.value.lastName}`,
|
||||
newRole:
|
||||
userChangeForm.value.roleSelected === rolesValues.ADMIN
|
||||
? t('userRole.selectRoles.admin')
|
||||
: userChangeForm.value.roleSelected === rolesValues.MODERATOR
|
||||
? t('userRole.selectRoles.moderator')
|
||||
: t('userRole.selectRoles.user'),
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((ok) => {
|
||||
if (ok) {
|
||||
userChangeForm.value.updateUserRole(
|
||||
userChangeForm.value.roleSelected,
|
||||
userChangeForm.value.currentRole,
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
const showDeleteModal = async () => {
|
||||
await confirm?.({
|
||||
props: {
|
||||
cancelTitle: t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: t('overlay.deleteUser.title'),
|
||||
okTitle: t('overlay.deleteUser.yes'),
|
||||
okVariant: 'danger',
|
||||
static: true,
|
||||
body: t('overlay.deleteUser.question', {
|
||||
username: `${selectedRow.value.firstName} ${selectedRow.value.lastName}`,
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((ok) => {
|
||||
if (ok) {
|
||||
deletedUserForm.value.deleteUserMutation()
|
||||
}
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
toastError(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
const showUndeleteModal = async () => {
|
||||
await confirm?.({
|
||||
props: {
|
||||
cancelTitle: t('overlay.cancel'),
|
||||
centered: true,
|
||||
hideHeaderClose: true,
|
||||
title: t('overlay.undeleteUser.title'),
|
||||
okTitle: t('overlay.undeleteUser.yes'),
|
||||
okVariant: 'success',
|
||||
body: t('overlay.undeleteUser.question', {
|
||||
username: `${selectedRow.value.firstName} ${selectedRow.value.lastName}`,
|
||||
}),
|
||||
},
|
||||
})
|
||||
.then((ok) => {
|
||||
if (ok) {
|
||||
undeletedUserForm.value.undeleteUserMutation()
|
||||
toastSuccess(t('user_recovered'))
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toastError(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
const updateUserData = (rowItem, newCreation) => {
|
||||
rowItem.creation = newCreation
|
||||
}
|
||||
|
||||
const updateRoles = ({ userId, roles }) => {
|
||||
emit('update-roles', userId, roles)
|
||||
}
|
||||
|
||||
const updateDeletedAt = ({ userId, deletedAt }) => {
|
||||
emit('update-deleted-at', userId, deletedAt)
|
||||
}
|
||||
|
||||
const emit = defineEmits(['update-roles', 'update-deleted-at'])
|
||||
|
||||
const selectedRow = computed(() => {
|
||||
return myItems.value.find((obj) => obj._showDetails)
|
||||
})
|
||||
|
||||
const onRowClicked = async (item) => {
|
||||
const status = myItems.value.find((obj) => {
|
||||
return obj?.userId === item?.userId
|
||||
})?._showDetails
|
||||
|
||||
myItems.value.forEach((obj) => {
|
||||
if (obj === item) {
|
||||
obj._showDetails = !status
|
||||
} else {
|
||||
obj._showDetails = false
|
||||
}
|
||||
})
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.items,
|
||||
(items) => {
|
||||
myItems.value = items.map((item) => {
|
||||
return { ...item, _showDetails: false }
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
myItems.value = props.items.map((item) => {
|
||||
return { ...item, _showDetails: false }
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,84 +1,95 @@
|
||||
<!-- eslint-disable vue/no-static-inline-styles -->
|
||||
<template>
|
||||
<div class="statistic-table">
|
||||
<b-table-simple style="width: auto" class="mt-5" striped stacked="md">
|
||||
<b-thead>
|
||||
<b-tr>
|
||||
<b-th></b-th>
|
||||
<b-th class="text-right">{{ $t('statistic.count') }}</b-th>
|
||||
<b-th class="text-right">{{ $t('statistic.details') }}</b-th>
|
||||
</b-tr>
|
||||
</b-thead>
|
||||
<b-tbody>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
<BTableSimple style="width: auto" class="mt-5" striped stacked="md">
|
||||
<BThead>
|
||||
<BTr>
|
||||
<BTh />
|
||||
<BTh class="text-right">{{ $t('statistic.count') }}</BTh>
|
||||
<BTh class="text-right">{{ $t('statistic.details') }}</BTh>
|
||||
</BTr>
|
||||
</BThead>
|
||||
<BTbody>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.totalUsers') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.totalUsers }}</b-td>
|
||||
<b-td></b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ props.statistics.totalUsers }}
|
||||
</BTd>
|
||||
<BTd></BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.activeUsers') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.activeUsers }}</b-td>
|
||||
<b-td></b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ props.statistics.activeUsers }}
|
||||
</BTd>
|
||||
<BTd></BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.deletedUsers') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.deletedUsers }}</b-td>
|
||||
<b-td></b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ props.statistics.deletedUsers }}
|
||||
</BTd>
|
||||
<BTd></BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.totalGradidoCreated') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">
|
||||
{{ $n(value.totalGradidoCreated, 'decimal') }} {{ $t('GDD') }}
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.totalGradidoCreated }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
<!-- {{ $n(props.statistics.totalGradidoCreated, 'decimal') }} {{ $t('GDD') }}-->
|
||||
4500
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ props.statistics.totalGradidoCreated }}
|
||||
</BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.totalGradidoDecayed') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">
|
||||
{{ $n(value.totalGradidoDecayed, 'decimal') }} {{ $t('GDD') }}
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.totalGradidoDecayed }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ $n(parseFloat(props.statistics.totalGradidoDecayed), 'decimal') }} {{ $t('GDD') }}
|
||||
</BTd>
|
||||
<BTd class="text-right">{{ props.statistics.totalGradidoDecayed }}</BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.totalGradidoAvailable') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">
|
||||
{{ $n(value.totalGradidoAvailable, 'decimal') }} {{ $t('GDD') }}
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.totalGradidoAvailable }}</b-td>
|
||||
</b-tr>
|
||||
<b-tr>
|
||||
<b-td>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ $n(parseFloat(props.statistics.totalGradidoAvailable), 'decimal') }} {{ $t('GDD') }}
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ props.statistics.totalGradidoAvailable }}
|
||||
</BTd>
|
||||
</BTr>
|
||||
<BTr>
|
||||
<BTd>
|
||||
<b>{{ $t('statistic.totalGradidoUnbookedDecayed') }}</b>
|
||||
</b-td>
|
||||
<b-td class="text-right">
|
||||
{{ $n(value.totalGradidoUnbookedDecayed, 'decimal') }} {{ $t('GDD') }}
|
||||
</b-td>
|
||||
<b-td class="text-right">{{ value.totalGradidoUnbookedDecayed }}</b-td>
|
||||
</b-tr>
|
||||
</b-tbody>
|
||||
</b-table-simple>
|
||||
</BTd>
|
||||
<BTd class="text-right">
|
||||
{{ $n(parseFloat(props.statistics.totalGradidoUnbookedDecayed), 'decimal') }}
|
||||
{{ $t('GDD') }}
|
||||
</BTd>
|
||||
<BTd class="text-right">{{ props.statistics.totalGradidoUnbookedDecayed }}</BTd>
|
||||
</BTr>
|
||||
</BTbody>
|
||||
</BTableSimple>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'StatisticTable',
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
<script setup>
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
statistics: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,106 +1,91 @@
|
||||
<template>
|
||||
<div class="transaction-link-list">
|
||||
<div v-if="items.length > 0">
|
||||
<div class="h3">{{ $t('transactionlink.name') }}</div>
|
||||
<b-table striped hover :fields="fields" :items="items"></b-table>
|
||||
<div class="h3">{{ t('transactionlink.name') }}</div>
|
||||
<BTable striped hover :fields="fields" :items="items"></BTable>
|
||||
</div>
|
||||
<b-pagination
|
||||
<BPagination
|
||||
v-model="currentPage"
|
||||
pills
|
||||
size="lg"
|
||||
v-model="currentPage"
|
||||
:per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
:hide-ellipsis="true"
|
||||
></b-pagination>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { BTable, BPagination } from 'bootstrap-vue-next'
|
||||
import { listTransactionLinksAdmin } from '../graphql/listTransactionLinksAdmin.js'
|
||||
export default {
|
||||
name: 'TransactionLinkList',
|
||||
props: {
|
||||
userId: { type: Number, required: true },
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const props = defineProps({
|
||||
userId: { type: Number, required: true },
|
||||
})
|
||||
|
||||
const { t, d } = useI18n()
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
const items = ref([])
|
||||
const rows = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const perPage = ref(5)
|
||||
|
||||
const fields = computed(() => [
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('transactionlink.created'),
|
||||
formatter: (value) => d(new Date(value)),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 5,
|
||||
}
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('transactionlist.amount'),
|
||||
formatter: (value) => `${value} GDD`,
|
||||
},
|
||||
methods: {
|
||||
getListTransactionLinks() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listTransactionLinksAdmin,
|
||||
variables: {
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
userId: this.userId,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.rows = result.data.listTransactionLinksAdmin.count
|
||||
this.items = result.data.listTransactionLinksAdmin.links
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
{ key: 'memo', label: t('transactionlist.memo'), class: 'text-break' },
|
||||
{
|
||||
key: 'validUntil',
|
||||
label: t('transactionlink.valid_until'),
|
||||
formatter: (value) => d(new Date(value)),
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: 'status',
|
||||
formatter: (value, key, item) => {
|
||||
if (item.deletedAt) return `${t('deleted')}: ${d(new Date(item.deletedAt))}`
|
||||
if (item.redeemedAt) return `${t('redeemed')}: ${d(new Date(item.redeemedAt))}`
|
||||
if (new Date() > new Date(item.validUntil))
|
||||
return `${t('expired')}: ${d(new Date(item.validUntil))}`
|
||||
return t('open')
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('transactionlink.created'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('transactionlist.amount'),
|
||||
formatter: (value, key, item) => {
|
||||
return `${value} GDD`
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('transactionlist.memo'), class: 'text-break' },
|
||||
{
|
||||
key: 'validUntil',
|
||||
label: this.$t('transactionlink.valid_until'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: 'status',
|
||||
formatter: (value, key, item) => {
|
||||
// deleted
|
||||
if (item.deletedAt) return this.$t('deleted') + ': ' + this.$d(new Date(item.deletedAt))
|
||||
// redeemed
|
||||
if (item.redeemedAt)
|
||||
return this.$t('redeemed') + ': ' + this.$d(new Date(item.redeemedAt))
|
||||
// expired
|
||||
if (new Date() > new Date(item.validUntil))
|
||||
return this.$t('expired') + ': ' + this.$d(new Date(item.validUntil))
|
||||
// open
|
||||
return this.$t('open')
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getListTransactionLinks()
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.getListTransactionLinks()
|
||||
},
|
||||
},
|
||||
}
|
||||
])
|
||||
|
||||
const { result, error, refetch } = useQuery(listTransactionLinksAdmin, () => ({
|
||||
currentPage: currentPage.value,
|
||||
pageSize: perPage.value,
|
||||
userId: props.userId,
|
||||
}))
|
||||
|
||||
watch(result, (newResult) => {
|
||||
if (newResult && newResult.listTransactionLinksAdmin) {
|
||||
rows.value = newResult.listTransactionLinksAdmin.count
|
||||
items.value = newResult.listTransactionLinksAdmin.links
|
||||
}
|
||||
})
|
||||
|
||||
watch(error, (err) => {
|
||||
if (err) {
|
||||
toastError(error.message)
|
||||
}
|
||||
})
|
||||
|
||||
watch([currentPage, perPage], () => {
|
||||
refetch()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,43 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
<div class="d-flex">
|
||||
<BFormInput
|
||||
v-model="currentValue"
|
||||
type="text"
|
||||
class="test-input-criteria"
|
||||
v-model="currentValue"
|
||||
:placeholder="placeholderText"
|
||||
></b-form-input>
|
||||
<b-input-group-append class="test-click-clear-criteria" @click="currentValue = ''">
|
||||
<b-input-group-text class="pointer">
|
||||
<b-icon icon="x" />
|
||||
</b-input-group-text>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
/>
|
||||
<div append class="test-click-clear-criteria" @click="onClear">
|
||||
<BInputGroupText class="pointer h-100">
|
||||
<IIcBaselineClose />
|
||||
</BInputGroupText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'UserQuery',
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { BInputGroupText, BFormInput } from 'bootstrap-vue-next'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const placeholderText = computed(() => props.placeholder || t('user_search'))
|
||||
|
||||
const onClear = () => {
|
||||
currentValue.value = ''
|
||||
}
|
||||
|
||||
const currentValue = ref(props.modelValue)
|
||||
|
||||
watch(currentValue, (newValue) => {
|
||||
emit('update:modelValue', newValue)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (newValue !== currentValue.value) {
|
||||
currentValue.value = newValue
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
placeholderText() {
|
||||
return this.placeholder || this.$t('user_search')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentValue() {
|
||||
if (this.value !== this.currentValue) {
|
||||
this.$emit('input', this.currentValue)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
@ -11,24 +11,24 @@
|
||||
:description="$t('geo-coordinates.latitude-longitude-smart.describe')"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="locationString"
|
||||
id="home-community-latitude-longitude-smart"
|
||||
v-model="locationString"
|
||||
type="text"
|
||||
@input="splitCoordinates"
|
||||
/>
|
||||
</b-form-group>
|
||||
<b-form-group :label="$t('latitude')" label-for="home-community-latitude">
|
||||
<b-form-input
|
||||
v-model="inputValue.latitude"
|
||||
id="home-community-latitude"
|
||||
v-model="inputValue.latitude"
|
||||
type="text"
|
||||
@input="valueUpdated"
|
||||
/>
|
||||
</b-form-group>
|
||||
<b-form-group :label="$t('longitude')" label-for="home-community-longitude">
|
||||
<b-form-input
|
||||
v-model="inputValue.longitude"
|
||||
id="home-community-longitude"
|
||||
v-model="inputValue.longitude"
|
||||
type="text"
|
||||
@input="valueUpdated"
|
||||
/>
|
||||
@ -41,9 +41,12 @@
|
||||
export default {
|
||||
name: 'Coordinates',
|
||||
props: {
|
||||
value: Object,
|
||||
default: null,
|
||||
value: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['input'],
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot v-if="!isEditing" v-bind:isEditing="isEditing" name="view"></slot>
|
||||
<slot v-else v-bind:isEditing="isEditing" name="edit" @input="valueChanged"></slot>
|
||||
<b-form-group v-if="allowEdit && !isEditing">
|
||||
<b-button @click="enableEdit" :variant="variant">
|
||||
<b-icon icon="pencil-fill">{{ $t('edit') }}</b-icon>
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
<b-form-group v-else-if="allowEdit && isEditing">
|
||||
<b-button @click="save" :variant="variant" :disabled="!isValueChanged" class="save-button">
|
||||
<slot v-if="!isEditing" :is-editing="isEditing" name="view"></slot>
|
||||
<slot v-else :is-editing="isEditing" name="edit" @input="valueChanged"></slot>
|
||||
<BFormGroup v-if="allowEdit && !isEditing">
|
||||
<BButton :variant="variant" @click="enableEdit">
|
||||
<IBiPencilFill />
|
||||
{{ $t('edit') }}
|
||||
</BButton>
|
||||
</BFormGroup>
|
||||
<BFormGroup v-else-if="allowEdit && isEditing">
|
||||
<BButton :variant="variant" :disabled="!isValueChanged" class="save-button" @click="save">
|
||||
{{ $t('save') }}
|
||||
</b-button>
|
||||
<b-button @click="close" variant="secondary" class="close-button">
|
||||
</BButton>
|
||||
<BButton variant="secondary" class="close-button" @click="close">
|
||||
{{ $t('close') }}
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
</BButton>
|
||||
</BFormGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -27,6 +28,7 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['save', 'reset'],
|
||||
data() {
|
||||
return {
|
||||
isEditing: false,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<b-form-group :label="label" :label-for="idName">
|
||||
<b-form-input :id="idName" v-model="inputValue" @input="updateValue" />
|
||||
</b-form-group>
|
||||
<BFormGroup :label="label" :label-for="idName">
|
||||
<BFormInput :id="idName" v-model="inputValue" @input="updateValue" />
|
||||
</BFormGroup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -22,6 +22,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ['input'],
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
v-model="timeValue"
|
||||
type="text"
|
||||
placeholder="hh:mm"
|
||||
@input="updateValues"
|
||||
@blur="validateAndCorrect"
|
||||
placeholder="hh:mm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -20,6 +20,7 @@ export default {
|
||||
default: '00:00',
|
||||
},
|
||||
},
|
||||
emits: ['input'],
|
||||
data() {
|
||||
return {
|
||||
timeValue: this.value,
|
||||
|
||||
70
admin/src/composables/useCreationMonths.js
Normal file
70
admin/src/composables/useCreationMonths.js
Normal file
@ -0,0 +1,70 @@
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { adminOpenCreations } from '../graphql/adminOpenCreations'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import toast from 'bootstrap/js/src/toast'
|
||||
|
||||
export default () => {
|
||||
const { d } = useI18n()
|
||||
|
||||
const creation = ref([1000, 1000, 1000])
|
||||
const userId = ref(0)
|
||||
|
||||
const creationDates = computed(() => {
|
||||
const now = new Date(Date.now())
|
||||
const dates = [now]
|
||||
for (let i = 1; i < 3; i++) {
|
||||
dates.push(new Date(now.getFullYear(), now.getMonth() - i, 1))
|
||||
}
|
||||
return dates.reverse()
|
||||
})
|
||||
|
||||
const creationDateObjects = computed(() => {
|
||||
const result = []
|
||||
creationDates.value.forEach((date) => {
|
||||
result.push({
|
||||
short: d(date, 'month'),
|
||||
long: d(date, 'long'),
|
||||
year: d(date, 'year'),
|
||||
date: d(date, 'short', 'en'),
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
const radioOptions = () => {
|
||||
return creationDateObjects.value.map((obj, idx) => {
|
||||
return {
|
||||
item: { ...obj, creation: creation.value[idx] },
|
||||
name: obj.short + (creation.value[idx] ? ' ' + creation.value[idx] + ' GDD' : ''),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const creationLabel = () => {
|
||||
return creationDates.value.map((date) => d(date, 'monthShort')).join(' | ')
|
||||
}
|
||||
|
||||
const { result, error } = useQuery(adminOpenCreations, { userId }, { fetchPolicy: 'no-cache' })
|
||||
|
||||
watch(result, (newResult) => {
|
||||
if (newResult && newResult.adminOpenCreations) {
|
||||
creation.value = newResult.adminOpenCreations.map((obj) => obj.amount)
|
||||
}
|
||||
})
|
||||
|
||||
watch(error, (err) => {
|
||||
if (err) {
|
||||
toast.error(err.message)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
creation,
|
||||
userId,
|
||||
creationDates,
|
||||
creationDateObjects,
|
||||
radioOptions,
|
||||
creationLabel,
|
||||
}
|
||||
}
|
||||
41
admin/src/composables/useToast.js
Normal file
41
admin/src/composables/useToast.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useToast } from 'bootstrap-vue-next'
|
||||
|
||||
export function useAppToast() {
|
||||
const { t } = useI18n()
|
||||
const { show } = useToast()
|
||||
const toastSuccess = (message) => {
|
||||
toast(message, {
|
||||
title: t('success'),
|
||||
variant: 'success',
|
||||
})
|
||||
}
|
||||
|
||||
const toastError = (message) => {
|
||||
toast(message, {
|
||||
title: t('error'),
|
||||
variant: 'danger',
|
||||
})
|
||||
}
|
||||
|
||||
const toast = (message, options = {}) => {
|
||||
if (message.replace) message = message.replace(/^GraphQL error: /, '')
|
||||
options = {
|
||||
solid: true,
|
||||
toaster: 'b-toaster-top-right',
|
||||
headerClass: 'gdd-toaster-title',
|
||||
bodyClass: 'gdd-toaster-body',
|
||||
toastClass: 'gdd-toaster',
|
||||
...options,
|
||||
body: message,
|
||||
}
|
||||
|
||||
show({ props: { ...options } })
|
||||
}
|
||||
|
||||
return {
|
||||
toastSuccess,
|
||||
toastError,
|
||||
toast,
|
||||
}
|
||||
}
|
||||
@ -21,9 +21,9 @@ const version = {
|
||||
}
|
||||
|
||||
const environment = {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
DEBUG: process.env.NODE_ENV !== 'production' ?? false,
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
|
||||
NODE_ENV: import.meta.env.NODE_ENV,
|
||||
DEBUG: import.meta.env.NODE_ENV !== 'production' ?? false,
|
||||
PRODUCTION: import.meta.env.NODE_ENV === 'production' ?? false,
|
||||
}
|
||||
|
||||
const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? undefined
|
||||
@ -64,4 +64,4 @@ const CONFIG = {
|
||||
...debug,
|
||||
}
|
||||
|
||||
module.exports = CONFIG
|
||||
export default CONFIG
|
||||
|
||||
@ -1,20 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
|
||||
Vue.use(VueI18n)
|
||||
|
||||
const loadLocaleMessages = () => {
|
||||
const locales = require.context('./locales/', true, /[A-Za-z0-9-_,\s]+\.json$/i)
|
||||
const messages = {}
|
||||
locales.keys().forEach((key) => {
|
||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
|
||||
if (matched && matched.length > 1) {
|
||||
const locale = matched[1]
|
||||
messages[locale] = locales(key)
|
||||
}
|
||||
})
|
||||
return messages
|
||||
}
|
||||
import { createI18n } from 'vue-i18n'
|
||||
import de from './locales/de.json'
|
||||
import en from './locales/en.json'
|
||||
|
||||
const numberFormats = {
|
||||
en: {
|
||||
@ -45,7 +31,7 @@ const numberFormats = {
|
||||
},
|
||||
}
|
||||
|
||||
const dateTimeFormats = {
|
||||
const datetimeFormats = {
|
||||
en: {
|
||||
short: {
|
||||
year: 'numeric',
|
||||
@ -96,12 +82,13 @@ const dateTimeFormats = {
|
||||
},
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
const i18n = createI18n({
|
||||
locale: 'en',
|
||||
legacy: false,
|
||||
fallbackLocale: 'en',
|
||||
messages: loadLocaleMessages(),
|
||||
messages: { de, en },
|
||||
numberFormats,
|
||||
dateTimeFormats,
|
||||
datetimeFormats,
|
||||
})
|
||||
|
||||
export default i18n
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
import NavBar from '@/components/NavBar'
|
||||
import ContentFooter from '@/components/ContentFooter'
|
||||
export default {
|
||||
name: 'defaultLayout',
|
||||
name: 'DefaultLayout',
|
||||
components: {
|
||||
NavBar,
|
||||
ContentFooter,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
// without this async calls are not working
|
||||
import 'regenerator-runtime'
|
||||
@ -11,36 +11,35 @@ import addNavigationGuards from './router/guards'
|
||||
|
||||
import i18n from './i18n'
|
||||
|
||||
import VueApollo from 'vue-apollo'
|
||||
// import VueApollo from 'vue-apollo'
|
||||
|
||||
import PortalVue from 'portal-vue'
|
||||
|
||||
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||
import { createBootstrap } from 'bootstrap-vue-next'
|
||||
|
||||
// Add the necessary CSS
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
import 'bootstrap-vue-next/dist/bootstrap-vue-next.css'
|
||||
|
||||
import { toasters } from './mixins/toaster'
|
||||
|
||||
import { apolloProvider } from './plugins/apolloProvider'
|
||||
|
||||
Vue.use(PortalVue)
|
||||
Vue.use(BootstrapVue)
|
||||
const app = createApp(App)
|
||||
|
||||
Vue.use(IconsPlugin)
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
app.use(i18n)
|
||||
app.use(PortalVue)
|
||||
app.use(createBootstrap())
|
||||
|
||||
Vue.use(VueApollo)
|
||||
app.use(() => apolloProvider)
|
||||
|
||||
Vue.mixin(toasters)
|
||||
app.mixin(toasters)
|
||||
|
||||
addNavigationGuards(router, store, apolloProvider.defaultClient, i18n)
|
||||
|
||||
i18n.locale =
|
||||
store.state.moderator && store.state.moderator.language ? store.state.moderator.language : 'en'
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
apolloProvider,
|
||||
render: (h) => h(App),
|
||||
}).$mount('#app')
|
||||
app.mount('#app')
|
||||
|
||||
@ -1,44 +1,43 @@
|
||||
<template>
|
||||
<div class="community-statistic">
|
||||
<statistic-table v-model="statistics" />
|
||||
<statistic-table v-if="!loading" :statistics="statistics" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { communityStatistics } from '@/graphql/communityStatistics.js'
|
||||
import StatisticTable from '../components/Tables/StatisticTable'
|
||||
|
||||
export default {
|
||||
name: 'CommunityStatistic',
|
||||
components: {
|
||||
StatisticTable,
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { communityStatistics } from '@/graphql/communityStatistics'
|
||||
import StatisticTable from '../components/Tables/StatisticTable'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const statistics = ref({
|
||||
totalUsers: null,
|
||||
activeUsers: null,
|
||||
deletedUsers: null,
|
||||
totalGradidoCreated: null,
|
||||
totalGradidoDecayed: null,
|
||||
totalGradidoAvailable: null,
|
||||
totalGradidoUnbookedDecayed: null,
|
||||
})
|
||||
|
||||
const { result, loading, error } = useQuery(communityStatistics, () => ({}))
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
watch(
|
||||
result,
|
||||
() => {
|
||||
if (!result.value) return
|
||||
const totals = { ...result.value.communityStatistics.dynamicStatisticsFields }
|
||||
statistics.value = { ...result.value.communityStatistics, ...totals }
|
||||
delete statistics.value.dynamicStatisticsFields
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
statistics: {
|
||||
totalUsers: null,
|
||||
activeUsers: null,
|
||||
deletedUsers: null,
|
||||
totalGradidoCreated: null,
|
||||
totalGradidoDecayed: null,
|
||||
totalGradidoAvailable: null,
|
||||
totalGradidoUnbookedDecayed: null,
|
||||
},
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
CommunityStatistics: {
|
||||
query() {
|
||||
return communityStatistics
|
||||
},
|
||||
update({ communityStatistics }) {
|
||||
const totals = { ...communityStatistics.dynamicStatisticsFields }
|
||||
this.statistics = { ...communityStatistics, ...totals }
|
||||
delete this.statistics.dynamicStatisticsFields
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(error, () => {
|
||||
if (error.value) {
|
||||
toastError(error.value.message)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,45 +1,31 @@
|
||||
<template>
|
||||
<div class="contribution-link">
|
||||
<contribution-link
|
||||
:items="items"
|
||||
:count="count"
|
||||
@get-contribution-links="getContributionLinks"
|
||||
/>
|
||||
<contribution-link :items="items" :count="count" @get-contribution-links="refetch" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||
import ContributionLink from '../components/ContributionLink/ContributionLink'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'ContributionLinks',
|
||||
components: {
|
||||
ContributionLink,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
count: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getContributionLinks() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: listContributionLinks,
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.count = result.data.listContributionLinks.count
|
||||
this.items = result.data.listContributionLinks.links
|
||||
})
|
||||
.catch(() => {
|
||||
this.toastError('listContributionLinks has no result, use default data')
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getContributionLinks()
|
||||
},
|
||||
}
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
const { result, error, refetch } = useQuery(listContributionLinks, null, {
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
|
||||
const items = computed(() => {
|
||||
return result.value?.listContributionLinks?.links || []
|
||||
})
|
||||
|
||||
const count = computed(() => {
|
||||
return result.value?.listContributionLinks?.count || 0
|
||||
})
|
||||
|
||||
watch(error, () => {
|
||||
toastError('listContributionLinks has no result, use default data')
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,77 +1,78 @@
|
||||
<!-- eslint-disable @intlify/vue-i18n/no-dynamic-keys -->
|
||||
<template>
|
||||
<div class="creation-confirm">
|
||||
<user-query class="mb-2 mt-2" v-model="query" :placeholder="$t('user_memo_search')" />
|
||||
{{ console.log(currentPage) }}
|
||||
<user-query v-model="query" class="mb-2 mt-2" :placeholder="$t('user_memo_search')" />
|
||||
<p class="mb-2">
|
||||
<input type="checkbox" class="noHashtag" v-model="noHashtag" />
|
||||
<span class="ml-2" v-b-tooltip="$t('no_hashtag_tooltip')">{{ $t('no_hashtag') }}</span>
|
||||
<input v-model="noHashtag" type="checkbox" class="noHashtag" />
|
||||
<span v-b-tooltip="$t('no_hashtag_tooltip')" class="ml-2">{{ $t('no_hashtag') }}</span>
|
||||
</p>
|
||||
<p class="mb-4" v-if="showResubmissionCheckbox">
|
||||
<input type="checkbox" class="hideResubmission" v-model="hideResubmissionModel" />
|
||||
<span class="ml-2" v-b-tooltip="$t('hide_resubmission_tooltip')">
|
||||
<p v-if="showResubmissionCheckbox" class="mb-4">
|
||||
<input v-model="hideResubmissionModel" type="checkbox" class="hideResubmission" />
|
||||
<span v-b-tooltip="$t('hide_resubmission_tooltip')" class="ml-2">
|
||||
{{ $t('hide_resubmission') }}
|
||||
</span>
|
||||
</p>
|
||||
<div>
|
||||
<b-tabs v-model="tabIndex" content-class="mt-3" fill>
|
||||
<b-tab active :title-link-attributes="{ 'data-test': 'open' }">
|
||||
<BTabs v-model="tabIndex" content-class="mt-3" fill>
|
||||
<BTab active :title-link-attributes="{ 'data-test': 'open' }">
|
||||
<template #title>
|
||||
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
||||
<IBiBellFill style="color: #0d6efd" />
|
||||
{{ $t('contributions.open') }}
|
||||
<b-badge v-if="$store.state.openCreations > 0" variant="danger">
|
||||
<BBadge v-if="$store.state.openCreations > 0" variant="danger">
|
||||
{{ $store.state.openCreations }}
|
||||
</b-badge>
|
||||
</BBadge>
|
||||
</template>
|
||||
</b-tab>
|
||||
<b-tab :title-link-attributes="{ 'data-test': 'confirmed' }">
|
||||
</BTab>
|
||||
<BTab :title-link-attributes="{ 'data-test': 'confirmed' }">
|
||||
<template #title>
|
||||
<b-icon icon="check" variant="success"></b-icon>
|
||||
<IBiCheck style="color: #198754" />
|
||||
{{ $t('contributions.confirms') }}
|
||||
</template>
|
||||
</b-tab>
|
||||
<b-tab :title-link-attributes="{ 'data-test': 'denied' }">
|
||||
</BTab>
|
||||
<BTab :title-link-attributes="{ 'data-test': 'denied' }">
|
||||
<template #title>
|
||||
<b-icon icon="x-circle" variant="warning"></b-icon>
|
||||
<IBiXCircle style="color: #ffc107" />
|
||||
{{ $t('contributions.denied') }}
|
||||
</template>
|
||||
</b-tab>
|
||||
<b-tab :title-link-attributes="{ 'data-test': 'deleted' }">
|
||||
</BTab>
|
||||
<BTab :title-link-attributes="{ 'data-test': 'deleted' }">
|
||||
<template #title>
|
||||
<b-icon icon="trash" variant="danger"></b-icon>
|
||||
<IBiTrash style="color: #dc3545" />
|
||||
{{ $t('contributions.deleted') }}
|
||||
</template>
|
||||
</b-tab>
|
||||
<b-tab :title-link-attributes="{ 'data-test': 'all' }">
|
||||
</BTab>
|
||||
<BTab :title-link-attributes="{ 'data-test': 'all' }">
|
||||
<template #title>
|
||||
<b-icon icon="list"></b-icon>
|
||||
<IBiList />
|
||||
{{ $t('contributions.all') }}
|
||||
</template>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</BTab>
|
||||
</BTabs>
|
||||
</div>
|
||||
<open-creations-table
|
||||
class="mt-4"
|
||||
:items="items"
|
||||
:fields="fields"
|
||||
:hideResubmission="hideResubmission"
|
||||
:hide-resubmission="hideResubmission"
|
||||
@show-overlay="showOverlay"
|
||||
@update-status="updateStatus"
|
||||
@reload-contribution="reloadContribution"
|
||||
@update-contributions="$apollo.queries.ListAllContributions.refetch()"
|
||||
@update-contributions="refetch"
|
||||
/>
|
||||
|
||||
<b-pagination
|
||||
<BPagination
|
||||
v-model="currentPage"
|
||||
pills
|
||||
size="lg"
|
||||
v-model="currentPage"
|
||||
:per-page="pageSize"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
:hide-ellipsis="true"
|
||||
></b-pagination>
|
||||
/>
|
||||
|
||||
<div v-if="overlay" id="overlay" @dblclick="overlay = false">
|
||||
<overlay :item="item" @overlay-cancel="overlay = false">
|
||||
<Overlay :item="item" @overlay-cancel="overlay = false">
|
||||
<template #title>
|
||||
{{ $t(overlayTitle) }}
|
||||
</template>
|
||||
@ -82,20 +83,21 @@
|
||||
<p>{{ $t(overlayQuestion) }}</p>
|
||||
</template>
|
||||
<template #submit-btn>
|
||||
<b-button
|
||||
size="md"
|
||||
v-bind:variant="overlayIcon"
|
||||
class="m-3 text-right"
|
||||
@click="overlayEvent"
|
||||
>
|
||||
<BButton size="md" :variant="overlayIcon" class="m-3 text-right" @click="overlayEvent">
|
||||
{{ $t(overlayBtnText) }}
|
||||
</b-button>
|
||||
</BButton>
|
||||
</template>
|
||||
</overlay>
|
||||
</Overlay>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useQuery, useMutation } from '@vue/apollo-composable'
|
||||
import { useStore } from 'vuex'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import Overlay from '../components/Overlay'
|
||||
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
|
||||
import UserQuery from '../components/UserQuery'
|
||||
@ -104,6 +106,7 @@ import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
|
||||
import { confirmContribution } from '../graphql/confirmContribution'
|
||||
import { denyContribution } from '../graphql/denyContribution'
|
||||
import { getContribution } from '../graphql/getContribution'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
const FILTER_TAB_MAP = [
|
||||
['IN_PROGRESS', 'PENDING'],
|
||||
@ -113,363 +116,346 @@ const FILTER_TAB_MAP = [
|
||||
['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'CreationConfirm',
|
||||
components: {
|
||||
OpenCreationsTable,
|
||||
Overlay,
|
||||
UserQuery,
|
||||
const store = useStore()
|
||||
const { t, d } = useI18n()
|
||||
const { toastError, toastSuccess } = useAppToast()
|
||||
|
||||
const tabIndex = ref(0)
|
||||
const items = ref([])
|
||||
const overlay = ref(false)
|
||||
const item = ref({})
|
||||
const variant = ref('confirm')
|
||||
const rows = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(25)
|
||||
const query = ref('')
|
||||
const noHashtag = ref(null)
|
||||
const hideResubmissionModel = ref(true)
|
||||
|
||||
const fields = computed(
|
||||
() =>
|
||||
[
|
||||
// open contributions
|
||||
[
|
||||
{ key: 'bookmark', label: t('delete') },
|
||||
{ key: 'deny', label: t('deny') },
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('creation'),
|
||||
formatter: (value) => value + ' GDD',
|
||||
},
|
||||
{ key: 'memo', label: t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('created'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{ key: 'moderatorId', label: t('moderator.moderator') },
|
||||
{ key: 'editCreation', label: t('chat') },
|
||||
{ key: 'confirm', label: t('save') },
|
||||
],
|
||||
// confirmed contributions
|
||||
[
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('creation'),
|
||||
formatter: (value) => value + ' GDD',
|
||||
},
|
||||
{ key: 'memo', label: t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('created'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('createdAt'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: t('contributions.confirms'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{ key: 'confirmedBy', label: t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: t('chat') },
|
||||
],
|
||||
// denied contributions
|
||||
[
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('creation'),
|
||||
formatter: (value) => value + ' GDD',
|
||||
},
|
||||
{ key: 'memo', label: t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('created'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('createdAt'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'deniedAt',
|
||||
label: t('contributions.denied'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{ key: 'deniedBy', label: t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: t('chat') },
|
||||
],
|
||||
// deleted contributions
|
||||
[
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('creation'),
|
||||
formatter: (value) => value + ' GDD',
|
||||
},
|
||||
{ key: 'memo', label: t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('created'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('createdAt'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'deletedAt',
|
||||
label: t('contributions.deleted'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{ key: 'deletedBy', label: t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: t('chat') },
|
||||
],
|
||||
// all contributions
|
||||
[
|
||||
{ key: 'status', label: t('status') },
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: t('creation'),
|
||||
formatter: (value) => value + ' GDD',
|
||||
},
|
||||
{ key: 'memo', label: t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: t('created'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: t('createdAt'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: t('contributions.confirms'),
|
||||
formatter: (value) => formatDateOrDash(value),
|
||||
},
|
||||
{ key: 'confirmedBy', label: t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: t('chat') },
|
||||
],
|
||||
][tabIndex.value],
|
||||
)
|
||||
|
||||
const statusFilter = computed(() => [...FILTER_TAB_MAP[tabIndex.value]])
|
||||
|
||||
const overlayTitle = computed(() => `overlay.${variant.value}.title`)
|
||||
const overlayText = computed(() => `overlay.${variant.value}.text`)
|
||||
const overlayQuestion = computed(() => `overlay.${variant.value}.question`)
|
||||
const overlayBtnText = computed(() => `overlay.${variant.value}.yes`)
|
||||
const overlayEvent = computed(() => {
|
||||
switch (variant.value) {
|
||||
case 'confirm':
|
||||
return confirmCreation
|
||||
case 'deny':
|
||||
return denyCreation
|
||||
case 'delete':
|
||||
return deleteCreation
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})
|
||||
const overlayIcon = computed(() => {
|
||||
switch (variant.value) {
|
||||
case 'confirm':
|
||||
return 'success'
|
||||
case 'deny':
|
||||
return 'warning'
|
||||
case 'delete':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
})
|
||||
const showResubmissionCheckbox = computed(() => tabIndex.value === 0)
|
||||
const hideResubmission = computed(() =>
|
||||
showResubmissionCheckbox.value ? hideResubmissionModel.value : false,
|
||||
)
|
||||
|
||||
watch(tabIndex, () => {
|
||||
currentPage.value = 1
|
||||
items.value = []
|
||||
})
|
||||
|
||||
const { onResult, onError, result, refetch } = useQuery(
|
||||
adminListContributions,
|
||||
{
|
||||
currentPage: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
statusFilter: statusFilter.value,
|
||||
query: query.value,
|
||||
noHashtag: noHashtag.value,
|
||||
hideResubmission: hideResubmission.value,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabIndex: 0,
|
||||
items: [],
|
||||
overlay: false,
|
||||
item: {},
|
||||
variant: 'confirm',
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
query: '',
|
||||
noHashtag: null,
|
||||
hideResubmissionModel: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tabIndex() {
|
||||
this.currentPage = 1
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
reloadContribution(id) {
|
||||
this.$apollo
|
||||
.query({ query: getContribution, variables: { id } })
|
||||
.then((result) => {
|
||||
const contribution = result.data.contribution
|
||||
this.$set(
|
||||
this.items,
|
||||
this.items.findIndex((obj) => obj.id === contribution.id),
|
||||
contribution,
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.overlay = false
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
deleteCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: adminDeleteContribution,
|
||||
variables: {
|
||||
id: this.item.id,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.overlay = false
|
||||
this.updatePendingCreations(this.item.id)
|
||||
this.toastSuccess(this.$t('creation_form.toasted_delete'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.overlay = false
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
denyCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: denyContribution,
|
||||
variables: {
|
||||
id: this.item.id,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.overlay = false
|
||||
this.updatePendingCreations(this.item.id)
|
||||
this.toastSuccess(this.$t('creation_form.toasted_denied'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.overlay = false
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
confirmCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: confirmContribution,
|
||||
variables: {
|
||||
id: this.item.id,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.overlay = false
|
||||
this.updatePendingCreations(this.item.id)
|
||||
this.toastSuccess(this.$t('creation_form.toasted_created'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.overlay = false
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
updatePendingCreations(id) {
|
||||
this.items = this.items.filter((obj) => obj.id !== id)
|
||||
this.$store.commit('openCreationsMinus', 1)
|
||||
},
|
||||
showOverlay(item, variant) {
|
||||
this.overlay = true
|
||||
this.item = item
|
||||
this.variant = variant
|
||||
},
|
||||
updateStatus(id) {
|
||||
this.items.find((obj) => obj.id === id).messagesCount++
|
||||
this.items.find((obj) => obj.id === id).status = 'IN_PROGRESS'
|
||||
},
|
||||
formatDateOrDash(value) {
|
||||
return value ? this.$d(new Date(value), 'short') : '—'
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
[
|
||||
// open contributions
|
||||
{ key: 'bookmark', label: this.$t('delete') },
|
||||
{ key: 'deny', label: this.$t('deny') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('creation'),
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('created'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{ key: 'moderatorId', label: this.$t('moderator.moderator') },
|
||||
{ key: 'editCreation', label: this.$t('chat') },
|
||||
{ key: 'confirm', label: this.$t('save') },
|
||||
],
|
||||
[
|
||||
// confirmed contributions
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('creation'),
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('created'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('createdAt'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: this.$t('contributions.confirms'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{ key: 'confirmedBy', label: this.$t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: this.$t('chat') },
|
||||
],
|
||||
[
|
||||
// denied contributions
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('creation'),
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('created'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('createdAt'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'deniedAt',
|
||||
label: this.$t('contributions.denied'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{ key: 'deniedBy', label: this.$t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: this.$t('chat') },
|
||||
],
|
||||
[
|
||||
// deleted contributions
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('creation'),
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('created'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('createdAt'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'deletedAt',
|
||||
label: this.$t('contributions.deleted'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{ key: 'deletedBy', label: this.$t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: this.$t('chat') },
|
||||
],
|
||||
[
|
||||
// all contributions
|
||||
{ key: 'status', label: this.$t('status') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'amount',
|
||||
label: this.$t('creation'),
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
|
||||
{
|
||||
key: 'contributionDate',
|
||||
label: this.$t('created'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: this.$t('createdAt'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'confirmedAt',
|
||||
label: this.$t('contributions.confirms'),
|
||||
formatter: (value) => {
|
||||
return this.formatDateOrDash(value)
|
||||
},
|
||||
},
|
||||
{ key: 'confirmedBy', label: this.$t('moderator.moderator') },
|
||||
{ key: 'chatCreation', label: this.$t('chat') },
|
||||
],
|
||||
][this.tabIndex]
|
||||
},
|
||||
statusFilter() {
|
||||
return FILTER_TAB_MAP[this.tabIndex]
|
||||
},
|
||||
overlayTitle() {
|
||||
return `overlay.${this.variant}.title`
|
||||
},
|
||||
overlayText() {
|
||||
return `overlay.${this.variant}.text`
|
||||
},
|
||||
overlayQuestion() {
|
||||
return `overlay.${this.variant}.question`
|
||||
},
|
||||
overlayBtnText() {
|
||||
return `overlay.${this.variant}.yes`
|
||||
},
|
||||
overlayEvent() {
|
||||
return this[`${this.variant}Creation`]
|
||||
},
|
||||
overlayIcon() {
|
||||
switch (this.variant) {
|
||||
case 'confirm':
|
||||
return 'success'
|
||||
case 'deny':
|
||||
return 'warning'
|
||||
case 'delete':
|
||||
return 'danger'
|
||||
default:
|
||||
return 'info'
|
||||
}
|
||||
},
|
||||
showResubmissionCheckbox() {
|
||||
return this.tabIndex === 0
|
||||
},
|
||||
hideResubmission() {
|
||||
return this.showResubmissionCheckbox ? this.hideResubmissionModel : false
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
ListAllContributions: {
|
||||
query() {
|
||||
return adminListContributions
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.pageSize,
|
||||
statusFilter: this.statusFilter,
|
||||
query: this.query,
|
||||
noHashtag: this.noHashtag,
|
||||
hideResubmission: this.hideResubmission,
|
||||
}
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
update({ adminListContributions }) {
|
||||
this.rows = adminListContributions.contributionCount
|
||||
this.items = adminListContributions.contributionList
|
||||
if (this.statusFilter === FILTER_TAB_MAP[0]) {
|
||||
this.$store.commit('setOpenCreations', adminListContributions.contributionCount)
|
||||
}
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
},
|
||||
{
|
||||
fetchPolicy: 'no-cache',
|
||||
},
|
||||
)
|
||||
|
||||
watch([statusFilter, query, noHashtag, hideResubmission, currentPage], () => {
|
||||
refetch({
|
||||
currentPage: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
statusFilter: statusFilter.value,
|
||||
query: query.value,
|
||||
noHashtag: noHashtag.value,
|
||||
hideResubmission: hideResubmission.value,
|
||||
})
|
||||
})
|
||||
|
||||
onError((error) => {
|
||||
toastError(error.message)
|
||||
})
|
||||
|
||||
onResult(() => {
|
||||
rows.value = result.value.adminListContributions.contributionCount
|
||||
items.value = result.value.adminListContributions.contributionList
|
||||
if (statusFilter.value === FILTER_TAB_MAP[0]) {
|
||||
store.commit('setOpenCreations', result.adminListContributions.contributionCount)
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
mutate: deleteMutation,
|
||||
onDone: onDeleteDone,
|
||||
onError: onDeleteError,
|
||||
} = useMutation(adminDeleteContribution)
|
||||
|
||||
onDeleteDone(() => {
|
||||
overlay.value = false
|
||||
updatePendingCreations(item.value.id)
|
||||
toastSuccess(t('creation_form.toasted_delete'))
|
||||
})
|
||||
|
||||
onDeleteError((error) => {
|
||||
overlay.value = false
|
||||
toastError(error.message)
|
||||
})
|
||||
|
||||
const {
|
||||
mutate: denyMutation,
|
||||
onDone: onDenayDone,
|
||||
onError: onDenayError,
|
||||
} = useMutation(denyContribution)
|
||||
|
||||
onDenayDone(() => {
|
||||
overlay.value = false
|
||||
updatePendingCreations(item.value.id)
|
||||
toastSuccess(t('creation_form.toasted_denied'))
|
||||
})
|
||||
|
||||
onDenayError((error) => {
|
||||
overlay.value = false
|
||||
toastError(error.message)
|
||||
})
|
||||
|
||||
const {
|
||||
mutate: confirmMutation,
|
||||
onDone: onConfirmationDone,
|
||||
onError: onConfirmationError,
|
||||
} = useMutation(confirmContribution)
|
||||
|
||||
onConfirmationDone(() => {
|
||||
overlay.value = false
|
||||
updatePendingCreations(item.value.id)
|
||||
toastSuccess(t('creation_form.toasted_created'))
|
||||
})
|
||||
|
||||
onConfirmationError((error) => {
|
||||
overlay.value = false
|
||||
toastError(error.message)
|
||||
})
|
||||
|
||||
const reloadContribution = (id) => {
|
||||
useQuery(getContribution, { id })
|
||||
.onResult((result) => {
|
||||
const contribution = result.data.contribution
|
||||
const index = items.value.findIndex((obj) => obj.id === contribution.id)
|
||||
items.value[index] = contribution
|
||||
})
|
||||
.onError((error) => {
|
||||
overlay.value = false
|
||||
toastError(error.message)
|
||||
})
|
||||
}
|
||||
|
||||
const deleteCreation = () => {
|
||||
deleteMutation({
|
||||
id: item.value.id,
|
||||
})
|
||||
}
|
||||
|
||||
const denyCreation = () => {
|
||||
denyMutation({
|
||||
id: item.value.id,
|
||||
})
|
||||
}
|
||||
|
||||
const confirmCreation = () => {
|
||||
confirmMutation({
|
||||
id: item.value.id,
|
||||
})
|
||||
}
|
||||
|
||||
const updatePendingCreations = (id) => {
|
||||
items.value = items.value.filter((obj) => obj.id !== id)
|
||||
store.commit('openCreationsMinus', 1)
|
||||
}
|
||||
|
||||
const showOverlay = (selectedItem, selectedVariant) => {
|
||||
overlay.value = true
|
||||
item.value = selectedItem
|
||||
variant.value = selectedVariant
|
||||
}
|
||||
|
||||
const updateStatus = (id) => {
|
||||
const target = items.value.find((obj) => obj.id === id)
|
||||
if (target) {
|
||||
target.messagesCount++
|
||||
target.status = 'IN_PROGRESS'
|
||||
}
|
||||
}
|
||||
|
||||
const formatDateOrDash = (value) => {
|
||||
return value ? d(new Date(value), 'short') : '—'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#overlay {
|
||||
position: fixed;
|
||||
@ -477,12 +463,9 @@ export default {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
inset: 0;
|
||||
padding-left: 5%;
|
||||
background-color: rgba(12, 11, 11, 0.781);
|
||||
background-color: rgb(12 11 11 / 78.1%);
|
||||
z-index: 1000000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -2,69 +2,60 @@
|
||||
<div class="federation-visualize">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="h2">{{ $t('federation.gradidoInstances') }}</span>
|
||||
<b-button>
|
||||
<b-icon
|
||||
icon="arrow-clockwise"
|
||||
font-scale="2"
|
||||
:animation="animation"
|
||||
@click="$apollo.queries.allCommunities.refresh()"
|
||||
data-test="federation-communities-refresh-btn"
|
||||
></b-icon>
|
||||
</b-button>
|
||||
<BButton
|
||||
:animation="animation"
|
||||
data-test="federation-communities-refresh-btn"
|
||||
font-scale="2"
|
||||
@click="refetch"
|
||||
>
|
||||
<IBiArrowClockwise />
|
||||
<!-- <b-icon-->
|
||||
<!-- icon="arrow-clockwise"-->
|
||||
<!-- font-scale="2"-->
|
||||
<!-- :animation="animation"-->
|
||||
<!-- data-test="federation-communities-refresh-btn"-->
|
||||
<!-- @click="$apollo.queries.allCommunities.refresh()"-->
|
||||
<!-- ></b-icon>-->
|
||||
</BButton>
|
||||
</div>
|
||||
<b-list-group>
|
||||
<b-row>
|
||||
<b-col cols="1" class="ml-1">{{ $t('federation.verified') }}</b-col>
|
||||
<b-col class="ml-3">{{ $t('federation.url') }}</b-col>
|
||||
<b-col class="ml-3">{{ $t('federation.name') }}</b-col>
|
||||
<b-col cols="2">{{ $t('federation.lastAnnouncedAt') }}</b-col>
|
||||
<b-col cols="2">{{ $t('federation.createdAt') }}</b-col>
|
||||
</b-row>
|
||||
<b-list-group-item
|
||||
<BListGroup>
|
||||
<BRow>
|
||||
<BCol cols="1" class="ml-1">{{ $t('federation.verified') }}</BCol>
|
||||
<BCol class="ml-3">{{ $t('federation.url') }}</BCol>
|
||||
<BCol class="ml-3">{{ $t('federation.name') }}</BCol>
|
||||
<BCol cols="2">{{ $t('federation.lastAnnouncedAt') }}</BCol>
|
||||
<BCol cols="2">{{ $t('federation.createdAt') }}</BCol>
|
||||
</BRow>
|
||||
<BListGroupItem
|
||||
v-for="item in communities"
|
||||
:key="item.publicKey"
|
||||
:variant="!item.foreign ? 'primary' : 'warning'"
|
||||
>
|
||||
<community-visualize-item :item="item" />
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</BListGroupItem>
|
||||
</BListGroup>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import { computed, watch } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { allCommunities } from '@/graphql/allCommunities'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
import CommunityVisualizeItem from '../components/Federation/CommunityVisualizeItem.vue'
|
||||
const { toastError } = useAppToast()
|
||||
|
||||
export default {
|
||||
name: 'FederationVisualize',
|
||||
components: {
|
||||
CommunityVisualizeItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
oldPublicKey: '',
|
||||
communities: [],
|
||||
icon: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
animation() {
|
||||
return this.$apollo.queries.allCommunities.loading ? 'spin' : ''
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
allCommunities: {
|
||||
fetchPolicy: 'network-only',
|
||||
query() {
|
||||
return allCommunities
|
||||
},
|
||||
update({ allCommunities }) {
|
||||
this.communities = allCommunities
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const { result, loading, refetch, error } = useQuery(allCommunities, () => ({}), {
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
|
||||
const communities = computed(() => {
|
||||
return result.value?.allCommunities || []
|
||||
})
|
||||
|
||||
watch(error, () => {
|
||||
if (error.value) toastError(error.value.message)
|
||||
})
|
||||
|
||||
const animation = computed(() => (loading.value ? 'spin' : ''))
|
||||
</script>
|
||||
|
||||
@ -1,65 +1,61 @@
|
||||
<template>
|
||||
<div class="admin-overview">
|
||||
<b-card
|
||||
v-show="$store.state.openCreations > 0"
|
||||
<BCard
|
||||
v-show="openCreations > 0"
|
||||
border-variant="primary"
|
||||
:header="$t('open_creations')"
|
||||
header-bg-variant="danger"
|
||||
header-text-variant="white"
|
||||
align="center"
|
||||
>
|
||||
<b-card-text>
|
||||
<b-link to="creation-confirm">
|
||||
<h1>{{ $store.state.openCreations }}</h1>
|
||||
<BCardText>
|
||||
<BLink to="creation-confirm">
|
||||
<h1>{{ openCreations }}</h1>
|
||||
<h1>Layout test</h1>
|
||||
</b-link>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
<b-card
|
||||
v-show="$store.state.openCreations < 1"
|
||||
</BLink>
|
||||
</BCardText>
|
||||
</BCard>
|
||||
<BCard
|
||||
v-show="openCreations < 1"
|
||||
border-variant="success"
|
||||
:header="$t('not_open_creations')"
|
||||
header-bg-variant="success"
|
||||
header-text-variant="white"
|
||||
align="center"
|
||||
>
|
||||
<b-card-text>
|
||||
<b-link to="creation-confirm">
|
||||
<h1 data-test="open-creation">{{ $store.state.openCreations }}</h1>
|
||||
</b-link>
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
<BCardText>
|
||||
<BLink to="creation-confirm">
|
||||
<h1 data-test="open-creation">{{ openCreations }}</h1>
|
||||
</BLink>
|
||||
</BCardText>
|
||||
</BCard>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { adminListContributions } from '../graphql/adminListContributions'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { BCard, BCardText, BLink } from 'bootstrap-vue-next'
|
||||
|
||||
export default {
|
||||
name: 'overview',
|
||||
data() {
|
||||
return {
|
||||
statusFilter: ['IN_PROGRESS', 'PENDING'],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
AllContributions: {
|
||||
query() {
|
||||
return adminListContributions
|
||||
},
|
||||
variables() {
|
||||
// may be at some point we need a pagination here
|
||||
return {
|
||||
statusFilter: this.statusFilter,
|
||||
hideResubmission: true,
|
||||
}
|
||||
},
|
||||
update({ adminListContributions }) {
|
||||
this.$store.commit('setOpenCreations', adminListContributions.contributionCount)
|
||||
},
|
||||
error({ message }) {
|
||||
this.toastError(message)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const store = useStore()
|
||||
|
||||
const statusFilter = ref(['IN_PROGRESS', 'PENDING'])
|
||||
|
||||
const { result, error } = useQuery(adminListContributions, {
|
||||
statusFilter: statusFilter.value,
|
||||
hideResubmission: true,
|
||||
})
|
||||
|
||||
const openCreations = computed(() => result.value?.adminListContributions.contributionCount || 0)
|
||||
|
||||
onMounted(() => {
|
||||
if (result.value) {
|
||||
store.commit('setOpenCreations', openCreations.value)
|
||||
}
|
||||
|
||||
if (error.value) {
|
||||
// store.dispatch('toastError', error.value.message)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,150 +1,162 @@
|
||||
<template>
|
||||
<div class="user-search">
|
||||
<div class="user-search-first-div">
|
||||
<b-button class="unconfirmedRegisterMails" variant="light" @click="unconfirmedRegisterMails">
|
||||
<b-icon icon="envelope" variant="danger"></b-icon>
|
||||
<BButton class="unconfirmedRegisterMails" variant="light" @click="unconfirmedRegisterMails">
|
||||
<IBiEnvelope style="color: #f5365c" />
|
||||
{{
|
||||
filters.byActivated === null
|
||||
? $t('all_emails')
|
||||
: filters.byActivated === false
|
||||
? $t('unregistered_emails')
|
||||
: ''
|
||||
? $t('unregistered_emails')
|
||||
: ''
|
||||
}}
|
||||
</b-button>
|
||||
<b-button class="deletedUserSearch" variant="light" @click="deletedUserSearch">
|
||||
<b-icon icon="x-circle" variant="danger"></b-icon>
|
||||
</BButton>
|
||||
<BButton class="deletedUserSearch" variant="light" @click="deletedUserSearch">
|
||||
<IBiXCircle style="color: #f5365c" />
|
||||
{{
|
||||
filters.byDeleted === null
|
||||
? $t('all_emails')
|
||||
: filters.byDeleted === true
|
||||
? $t('deleted_user')
|
||||
: ''
|
||||
? $t('deleted_user')
|
||||
: ''
|
||||
}}
|
||||
</b-button>
|
||||
</BButton>
|
||||
</div>
|
||||
<label>{{ $t('user_search') }}</label>
|
||||
<user-query class="mb-4 mt-2" v-model="criteria" />
|
||||
<search-user-table
|
||||
<UserQuery v-model="criteria" class="mb-4 mt-2" />
|
||||
<SearchUserTable
|
||||
type="PageUserSearch"
|
||||
:items="searchResult"
|
||||
:fields="fields"
|
||||
@updateRoles="updateRoles"
|
||||
@updateDeletedAt="updateDeletedAt"
|
||||
@update-roles="updateRoles"
|
||||
@update-deleted-at="updateDeletedAt"
|
||||
/>
|
||||
<b-pagination
|
||||
pills
|
||||
size="lg"
|
||||
<BPagination
|
||||
v-model="currentPage"
|
||||
:per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
:hide-ellipsis="true"
|
||||
></b-pagination>
|
||||
<div></div>
|
||||
pills
|
||||
size="lg"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, watchEffect } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { searchUsers } from '../graphql/searchUsers.js'
|
||||
import useCreationMonths from '../composables/useCreationMonths'
|
||||
import SearchUserTable from '../components/Tables/SearchUserTable'
|
||||
import { searchUsers } from '../graphql/searchUsers'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
import UserQuery from '../components/UserQuery'
|
||||
import { BPagination, BButton } from 'bootstrap-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
|
||||
export default {
|
||||
name: 'UserSearch',
|
||||
mixins: [creationMonths],
|
||||
components: {
|
||||
SearchUserTable,
|
||||
UserQuery,
|
||||
const { t } = useI18n()
|
||||
|
||||
const searchResult = ref([])
|
||||
const criteria = ref('')
|
||||
const filters = reactive({
|
||||
byActivated: null,
|
||||
byDeleted: null,
|
||||
})
|
||||
const rows = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const perPage = ref(25)
|
||||
const response = ref()
|
||||
|
||||
const { creationLabel } = useCreationMonths()
|
||||
const { toastSuccess } = useAppToast()
|
||||
|
||||
const { result, refetch } = useQuery(searchUsers, {
|
||||
query: criteria.value,
|
||||
filters: filters,
|
||||
currentPage: currentPage.value,
|
||||
pageSize: perPage.value,
|
||||
order: 'DESC',
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
response.value = result.value
|
||||
|
||||
watchEffect(() => {
|
||||
if (result.value) {
|
||||
searchResult.value = result.value.searchUsers.userList
|
||||
rows.value = result.value.searchUsers.userCount
|
||||
}
|
||||
})
|
||||
|
||||
const updateRoles = (userId, roles) => {
|
||||
searchResult.value.find((obj) => obj.userId === userId).roles = roles
|
||||
}
|
||||
|
||||
const updateDeletedAt = (userId, deletedAt) => {
|
||||
searchResult.value.find((obj) => obj.userId === userId).deletedAt = deletedAt
|
||||
toastSuccess(deletedAt ? t('user_deleted') : t('user_recovered'))
|
||||
}
|
||||
|
||||
const unconfirmedRegisterMails = () => {
|
||||
filters.byActivated = filters.byActivated === null ? false : null
|
||||
refetch()
|
||||
}
|
||||
|
||||
const deletedUserSearch = () => {
|
||||
filters.byDeleted = filters.byDeleted === null ? true : null
|
||||
refetch()
|
||||
}
|
||||
|
||||
const fields = computed(() => [
|
||||
{ key: 'email', label: t('e_mail') },
|
||||
{ key: 'firstName', label: t('firstname') },
|
||||
{ key: 'lastName', label: t('lastname') },
|
||||
{
|
||||
key: 'creation',
|
||||
label: creationLabel(),
|
||||
formatter: (value, key, item) => {
|
||||
return value.join(' | ')
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showArrays: false,
|
||||
searchResult: [],
|
||||
criteria: '',
|
||||
filters: {
|
||||
byActivated: null,
|
||||
byDeleted: null,
|
||||
},
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 25,
|
||||
now: Date.now(),
|
||||
// { key: 'show_details', label: t('details') },
|
||||
// { key: 'confirm_mail', label: t('confirmed') },
|
||||
// { key: 'has_elopage', label: 'elopage' },
|
||||
// { key: 'transactions_list', label: t('transaction') },
|
||||
{ key: 'status', label: t('status') },
|
||||
])
|
||||
|
||||
watch(
|
||||
() => currentPage.value,
|
||||
async (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
await refetch({
|
||||
query: criteria.value,
|
||||
filters: filters,
|
||||
currentPage: newValue,
|
||||
pageSize: perPage.value,
|
||||
order: 'DESC',
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unconfirmedRegisterMails() {
|
||||
this.filters.byActivated = this.filters.byActivated === null ? false : null
|
||||
this.getUsers()
|
||||
},
|
||||
deletedUserSearch() {
|
||||
this.filters.byDeleted = this.filters.byDeleted === null ? true : null
|
||||
this.getUsers()
|
||||
},
|
||||
getUsers() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: searchUsers,
|
||||
variables: {
|
||||
query: this.criteria,
|
||||
filters: this.filters,
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
order: 'DESC',
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
.then((result) => {
|
||||
this.rows = result.data.searchUsers.userCount
|
||||
this.searchResult = result.data.searchUsers.userList
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
updateRoles(userId, roles) {
|
||||
this.searchResult.find((obj) => obj.userId === userId).roles = roles
|
||||
},
|
||||
updateDeletedAt(userId, deletedAt) {
|
||||
this.searchResult.find((obj) => obj.userId === userId).deletedAt = deletedAt
|
||||
this.toastSuccess(deletedAt ? this.$t('user_deleted') : this.$t('user_recovered'))
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => criteria.value,
|
||||
async (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
await refetch({
|
||||
query: newValue,
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.getUsers()
|
||||
},
|
||||
criteria() {
|
||||
this.getUsers()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'creation',
|
||||
label: this.creationLabel,
|
||||
formatter: (value, key, item) => {
|
||||
return value.join(' | ')
|
||||
},
|
||||
},
|
||||
// { key: 'show_details', label: this.$t('details') },
|
||||
// { key: 'confirm_mail', label: this.$t('confirmed') },
|
||||
// { key: 'has_elopage', label: 'elopage' },
|
||||
// { key: 'transactions_list', label: this.$t('transaction') },
|
||||
{ key: 'status', label: this.$t('status') },
|
||||
]
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.getUsers()
|
||||
},
|
||||
}
|
||||
)
|
||||
</script>
|
||||
<style>
|
||||
<style scoped>
|
||||
.user-search-first-div {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,6 +2,7 @@ import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
|
||||
import VueApollo from 'vue-apollo'
|
||||
import CONFIG from '../config'
|
||||
import store from '../store/store'
|
||||
import { provideApolloClient } from '@vue/apollo-composable'
|
||||
|
||||
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
|
||||
|
||||
@ -30,6 +31,8 @@ const apolloClient = new ApolloClient({
|
||||
cache: new InMemoryCache(),
|
||||
})
|
||||
|
||||
provideApolloClient(apolloClient)
|
||||
|
||||
export const apolloProvider = new VueApollo({
|
||||
defaultClient: apolloClient,
|
||||
})
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import routes from './routes'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const router = new VueRouter({
|
||||
base: '/admin',
|
||||
const router = createRouter({
|
||||
routes,
|
||||
linkActiveClass: 'active',
|
||||
mode: 'history',
|
||||
history: createWebHistory('/admin/'),
|
||||
scrollBehavior: (to, from, savedPosition) => {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
@ -16,7 +12,7 @@ const router = new VueRouter({
|
||||
if (to.hash) {
|
||||
return { selector: to.hash }
|
||||
}
|
||||
return { x: 0, y: 0 }
|
||||
return { left: 0, top: 0 }
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/statistic',
|
||||
name: 'statistic',
|
||||
component: () => import('@/pages/CommunityStatistic.vue'),
|
||||
},
|
||||
{
|
||||
@ -17,23 +18,28 @@ const routes = [
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
name: 'user',
|
||||
component: () => import('@/pages/UserSearch.vue'),
|
||||
},
|
||||
{
|
||||
path: '/creation-confirm',
|
||||
name: 'creation-confirm',
|
||||
component: () => import('@/pages/CreationConfirm.vue'),
|
||||
},
|
||||
{
|
||||
path: '/contribution-links',
|
||||
name: 'contribution-links',
|
||||
component: () => import('@/pages/ContributionLinks.vue'),
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
component: () => import('@/components/NotFoundPage.vue'),
|
||||
path: '/federation',
|
||||
name: 'federation',
|
||||
component: () => import('@/pages/FederationVisualize.vue'),
|
||||
},
|
||||
{
|
||||
path: '/federation',
|
||||
component: () => import('@/pages/FederationVisualize.vue'),
|
||||
path: '/:catchAll(.*)',
|
||||
name: 'NotFound',
|
||||
component: () => import('@/components/NotFoundPage.vue'),
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import Vuex from 'vuex'
|
||||
import Vue from 'vue'
|
||||
import { createStore } from 'vuex'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
import CONFIG from '../config'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export const mutations = {
|
||||
openCreationsPlus: (state, i) => {
|
||||
state.openCreations += i
|
||||
@ -34,7 +31,7 @@ export const actions = {
|
||||
},
|
||||
}
|
||||
|
||||
const store = new Vuex.Store({
|
||||
const store = createStore({
|
||||
plugins: [
|
||||
createPersistedState({
|
||||
key: 'gradido-admin',
|
||||
|
||||
62
admin/vite.config.js
Normal file
62
admin/vite.config.js
Normal file
@ -0,0 +1,62 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import commonjs from 'vite-plugin-commonjs'
|
||||
import Icons from 'unplugin-icons/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import IconsResolve from 'unplugin-icons/resolver'
|
||||
import { BootstrapVueNextResolver } from 'bootstrap-vue-next'
|
||||
import EnvironmentPlugin from 'vite-plugin-environment'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
export default defineConfig({
|
||||
base: '/admin/',
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 8080,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
assets: path.join(__dirname, 'src/assets'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Components({
|
||||
resolvers: [IconsResolve(), BootstrapVueNextResolver()],
|
||||
dts: true,
|
||||
}),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
}),
|
||||
EnvironmentPlugin({
|
||||
BUILD_COMMIT: null,
|
||||
PORT: null,
|
||||
COMMUNITY_HOST: null,
|
||||
URL_PROTOCOL: null,
|
||||
WALLET_URL: null,
|
||||
GRAPHQL_URL: null,
|
||||
GRAPHQL_PATH: null,
|
||||
WALLET_AUTH_PATH: null,
|
||||
WALLET_LOGIN_PATH: null,
|
||||
DEBUG_DISABLE_AUTH: null,
|
||||
CONFIG_VERSION: null,
|
||||
}),
|
||||
commonjs(),
|
||||
],
|
||||
build: {
|
||||
outDir: path.resolve(__dirname, './build'),
|
||||
},
|
||||
publicDir: '/admin',
|
||||
})
|
||||
@ -20,6 +20,23 @@ module.exports = {
|
||||
},
|
||||
lintOnSave: true,
|
||||
publicPath: '/admin',
|
||||
chainWebpack: (config) => {
|
||||
config.resolve.alias.set('vue', '@vue/compat')
|
||||
|
||||
config.module
|
||||
.rule('vue')
|
||||
.use('vue-loader')
|
||||
.tap((options) => {
|
||||
return {
|
||||
...options,
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
configureWebpack: {
|
||||
// Set up all the aliases we use in our app.
|
||||
resolve: {
|
||||
@ -35,14 +52,14 @@ module.exports = {
|
||||
// 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR),
|
||||
// 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE),
|
||||
// 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION),
|
||||
'process.env.BUILD_COMMIT': JSON.stringify(CONFIG.BUILD_COMMIT),
|
||||
'import.meta.env.BUILD_COMMIT': JSON.stringify(CONFIG.BUILD_COMMIT),
|
||||
// 'process.env.PORT': JSON.stringify(process.env.PORT),
|
||||
}),
|
||||
// generate webpack stats to allow analysis of the bundlesize
|
||||
new StatsPlugin('webpack.stats.json'),
|
||||
],
|
||||
infrastructureLogging: {
|
||||
level: 'warn', // 'none' | 'error' | 'warn' | 'info' | 'log' | 'verbose'
|
||||
level: 'info', // 'none' | 'error' | 'warn' | 'info' | 'log' | 'verbose'
|
||||
},
|
||||
},
|
||||
css: {
|
||||
|
||||
12751
admin/yarn.lock
12751
admin/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@ set +o allexport
|
||||
|
||||
# enable nvm
|
||||
export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
nvm use default
|
||||
|
||||
# NOTE: all config values will be in process.env when starting
|
||||
# the services and will therefore take precedence over the .env
|
||||
@ -204,6 +205,9 @@ echo 'Updating frontend' >> $UPDATE_HTML
|
||||
cd $PROJECT_ROOT/frontend
|
||||
# TODO maybe handle this differently?
|
||||
unset NODE_ENV
|
||||
nvm use
|
||||
nvm install
|
||||
npm i -g yarn
|
||||
yarn install
|
||||
yarn build
|
||||
# TODO maybe handle this differently?
|
||||
@ -214,11 +218,16 @@ echo 'Updating admin' >> $UPDATE_HTML
|
||||
cd $PROJECT_ROOT/admin
|
||||
# TODO maybe handle this differently?
|
||||
unset NODE_ENV
|
||||
nvm use
|
||||
nvm install
|
||||
npm i -g yarn
|
||||
yarn install
|
||||
yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
|
||||
nvm use default
|
||||
|
||||
# Install & build dht-node
|
||||
echo 'Updating dht-node' >> $UPDATE_HTML
|
||||
cd $PROJECT_ROOT/dht-node
|
||||
@ -239,6 +248,7 @@ yarn build
|
||||
# TODO maybe handle this differently?
|
||||
export NODE_ENV=production
|
||||
|
||||
nvm use default
|
||||
# start after building all to use up less ressources
|
||||
pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
#pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||
|
||||
@ -81,4 +81,4 @@ server {
|
||||
#}
|
||||
|
||||
#access_log /var/log/nginx/access.log main;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user