mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 1223-community_communication_concept2
# Conflicts: # docu/graphics/roadmap_publicity.drawio # docu/graphics/roadmap_publicity.png
This commit is contained in:
commit
e0d0056ab7
100
.github/workflows/publish.yml
vendored
100
.github/workflows/publish.yml
vendored
@ -106,82 +106,6 @@ jobs:
|
||||
name: docker-database-production_up
|
||||
path: /tmp/database_up.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD PRODUCTION LOGIN SERVER ##################################
|
||||
##############################################################################
|
||||
build_production_login_server:
|
||||
name: Docker Build Production - Login Server
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
##########################################################################
|
||||
# SET ENVS ###############################################################
|
||||
##########################################################################
|
||||
- name: ENV - VERSION
|
||||
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_DATE
|
||||
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_VERSION
|
||||
run: echo "BUILD_VERSION=${VERSION}.${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_COMMIT
|
||||
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
##########################################################################
|
||||
# LOGIN SERVER ###########################################################
|
||||
##########################################################################
|
||||
- name: Login Server | Build `production` image
|
||||
run: |
|
||||
docker build -t "gradido/login_server:latest" -t "gradido/login_server:production" -t "gradido/login_server:${VERSION}" -t "gradido/login_server:${BUILD_VERSION}" login_server/
|
||||
docker save "gradido/login_server" > /tmp/login_server.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-login-server-production
|
||||
path: /tmp/login_server.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD PRODUCTION COMMUNITY SERVER ##############################
|
||||
##############################################################################
|
||||
build_production_community_server:
|
||||
name: Docker Build Production - Community Server
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# SET ENVS ###############################################################
|
||||
##########################################################################
|
||||
- name: ENV - VERSION
|
||||
run: echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_DATE
|
||||
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_VERSION
|
||||
run: echo "BUILD_VERSION=${VERSION}.${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
|
||||
- name: ENV - BUILD_COMMIT
|
||||
run: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
##########################################################################
|
||||
# COMMUNITY SERVER #######################################################
|
||||
##########################################################################
|
||||
- name: Community Server | Build `production` image
|
||||
run: |
|
||||
docker build -t "gradido/community_server:latest" -t "gradido/community_server:production" -t "gradido/community_server:${VERSION}" -t "gradido/community_server:${BUILD_VERSION}" -f ./community_server/Dockerfile ./
|
||||
docker save "gradido/community_server" > /tmp/community_server.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-community-server-production
|
||||
path: /tmp/community_server.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD PRODUCTION MARIADB #######################################
|
||||
##############################################################################
|
||||
@ -248,7 +172,7 @@ jobs:
|
||||
##########################################################################
|
||||
- name: Nginx | Build `production` image
|
||||
run: |
|
||||
docker build -t "gradido/nginx:latest" -t "gradido/nginx:production" -t "gradido/nginx:${VERSION}" -t "gradido/nginx:${BUILD_VERSION}" -f ./nginx/Dockerfile ./
|
||||
docker build -t "gradido/nginx:latest" -t "gradido/nginx:production" -t "gradido/nginx:${VERSION}" -t "gradido/nginx:${BUILD_VERSION}" nginx/
|
||||
docker save "gradido/nginx" > /tmp/nginx.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -262,7 +186,7 @@ jobs:
|
||||
upload_to_dockerhub:
|
||||
name: Upload to Dockerhub
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_production_frontend, build_production_backend, build_production_database_up, build_production_login_server, build_production_community_server, build_production_mariadb, build_production_nginx]
|
||||
needs: [build_production_frontend, build_production_backend, build_production_database_up, build_production_mariadb, build_production_nginx]
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@ -296,20 +220,6 @@ jobs:
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/database_up.tar
|
||||
- name: Download Docker Image (Login Server)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-login-server-production
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/login_server.tar
|
||||
- name: Download Docker Image (Community Server)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-community-server-production
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/community_server.tar
|
||||
- name: Download Docker Image (MariaDB)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
@ -335,10 +245,6 @@ jobs:
|
||||
run: docker push --all-tags gradido/backend
|
||||
- name: Push database
|
||||
run: docker push --all-tags gradido/database
|
||||
- name: Push login_server
|
||||
run: docker push --all-tags gradido/login_server
|
||||
- name: Push community_server
|
||||
run: docker push --all-tags gradido/community_server
|
||||
- name: Push MariaDB
|
||||
run: docker push --all-tags gradido/mariadb
|
||||
- name: Push Nginx
|
||||
@ -399,7 +305,7 @@ jobs:
|
||||
- name: yarn install
|
||||
run: yarn install
|
||||
- name: generate changelog
|
||||
run: yarn auto-changelog --latest-version ${{ env.VERSION }} --unreleased-only
|
||||
run: yarn auto-changelog --commit-limit 0 --latest-version ${{ env.VERSION }} --unreleased-only
|
||||
- name: package-version-to-git-release
|
||||
continue-on-error: true # Will fail if tag exists
|
||||
id: create_release
|
||||
|
||||
282
.github/workflows/test.yml
vendored
282
.github/workflows/test.yml
vendored
@ -107,54 +107,6 @@ jobs:
|
||||
name: docker-database-test_up
|
||||
path: /tmp/database_up.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST LOGIN SERVER ########################################
|
||||
##############################################################################
|
||||
build_test_login_server:
|
||||
name: Docker Build Test - Login Server
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
##########################################################################
|
||||
# BUILD LOGIN SERVER DOCKER IMAGE ########################################
|
||||
##########################################################################
|
||||
- name: login server | Build `release` image
|
||||
run: |
|
||||
docker build -t "gradido/login_server:release" -f ./login_server/Dockerfile login_server/
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST COMMUNITY SERVER ####################################
|
||||
##############################################################################
|
||||
build_test_community_server:
|
||||
name: Docker Build Test - Community Server
|
||||
runs-on: ubuntu-latest
|
||||
#needs: [nothing]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# BUILD COMMUNITY SERVER DOCKER IMAGE ####################################
|
||||
##########################################################################
|
||||
- name: community server | Build `test` image
|
||||
run: |
|
||||
docker build -t "gradido/community_server:test" -f ./community_server/Dockerfile ./
|
||||
docker save "gradido/community_server:test" > /tmp/community_server.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: docker-community-server-test
|
||||
path: /tmp/community_server.tar
|
||||
|
||||
##############################################################################
|
||||
# JOB: DOCKER BUILD TEST MARIADB #############################################
|
||||
##############################################################################
|
||||
@ -199,7 +151,7 @@ jobs:
|
||||
##########################################################################
|
||||
- name: nginx | Build `test` image
|
||||
run: |
|
||||
docker build -t "gradido/nginx:test" -f ./nginx/Dockerfile ./
|
||||
docker build -t "gradido/nginx:test" nginx/
|
||||
docker save "gradido/nginx:test" > /tmp/nginx.tar
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -233,7 +185,7 @@ jobs:
|
||||
##########################################################################
|
||||
# LOCALES FRONTEND #######################################################
|
||||
##########################################################################
|
||||
- name: frontend | Locales
|
||||
- name: Frontend | Locales
|
||||
run: docker run --rm gradido/frontend:test yarn run locales
|
||||
|
||||
##############################################################################
|
||||
@ -262,9 +214,38 @@ jobs:
|
||||
##########################################################################
|
||||
# LINT FRONTEND ##########################################################
|
||||
##########################################################################
|
||||
- name: frontend | Lint
|
||||
- name: Frontend | Lint
|
||||
run: docker run --rm gradido/frontend:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: STYLELINT FRONTEND ####################################################
|
||||
##############################################################################
|
||||
stylelint_frontend:
|
||||
name: Stylelint - Frontend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_frontend]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Frontend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-frontend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/frontend.tar
|
||||
##########################################################################
|
||||
# STYLELINT FRONTEND #####################################################
|
||||
##########################################################################
|
||||
- name: Frontend | Stylelint
|
||||
run: docker run --rm gradido/frontend:test yarn run stylelint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LINT ADMIN INTERFACE ##################################################
|
||||
##############################################################################
|
||||
@ -295,7 +276,36 @@ jobs:
|
||||
run: docker run --rm gradido/admin:test yarn run lint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LOCALES ADMIN ######################################################
|
||||
# JOB: STYLELINT ADMIN INTERFACE ##############################################
|
||||
##############################################################################
|
||||
stylelint_admin:
|
||||
name: Stylelint - Admin Interface
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_admin]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Admin Interface)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-admin-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/admin.tar
|
||||
##########################################################################
|
||||
# STYLELINT ADMIN INTERFACE ##############################################
|
||||
##########################################################################
|
||||
- name: Admin Interface | Stylelint
|
||||
run: docker run --rm gradido/admin:test yarn run stylelint
|
||||
|
||||
##############################################################################
|
||||
# JOB: LOCALES ADMIN #########################################################
|
||||
##############################################################################
|
||||
locales_admin:
|
||||
name: Locales - Admin
|
||||
@ -428,7 +438,7 @@ jobs:
|
||||
report_name: Coverage Frontend
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 94
|
||||
min_coverage: 95
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
@ -470,7 +480,7 @@ jobs:
|
||||
report_name: Coverage Admin Interface
|
||||
type: lcov
|
||||
result_path: ./coverage/lcov.info
|
||||
min_coverage: 76
|
||||
min_coverage: 95
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
@ -479,7 +489,7 @@ jobs:
|
||||
unit_test_backend:
|
||||
name: Unit tests - Backend
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_backend,build_test_mariadb]
|
||||
needs: [build_test_mariadb]
|
||||
steps:
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
@ -496,20 +506,18 @@ jobs:
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/mariadb.tar
|
||||
- name: Download Docker Image (Backend)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-backend-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/backend.tar
|
||||
##########################################################################
|
||||
# UNIT TESTS BACKEND #####################################################
|
||||
##########################################################################
|
||||
- name: backend | docker-compose
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb database
|
||||
- name: backend | docker-compose mariadb
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
shell: bash
|
||||
- name: backend | docker-compose database
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
||||
- name: backend Unit tests | test
|
||||
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn CI_worklfow_test
|
||||
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test
|
||||
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
|
||||
##########################################################################
|
||||
# COVERAGE CHECK BACKEND #################################################
|
||||
@ -520,149 +528,7 @@ jobs:
|
||||
report_name: Coverage Backend
|
||||
type: lcov
|
||||
result_path: ./backend/coverage/lcov.info
|
||||
min_coverage: 40
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST LOGIN-SERVER ###############################################
|
||||
##############################################################################
|
||||
unit_test_login_server:
|
||||
name: Unit tests - Login-Server
|
||||
runs-on: ubuntu-latest
|
||||
needs: []
|
||||
services:
|
||||
mariadb:
|
||||
image: gradido/mariadb:test
|
||||
env:
|
||||
MARIADB_ALLOW_EMPTY_PASSWORD: 1
|
||||
MARIADB_USER: root
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping"
|
||||
--health-interval=6s
|
||||
--health-timeout=3s
|
||||
--health-retries=4
|
||||
steps:
|
||||
# - name: Debug service
|
||||
# run: echo "$(docker ps)"
|
||||
#- name: Debug container choosing script
|
||||
# run: echo "$(docker container ls | grep mariadb | awk '{ print $1 }')"
|
||||
- name: get mariadb container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep mariadb | awk '{ print $1 }')"
|
||||
id: mariadb_container
|
||||
- name: get automatic created network
|
||||
run: echo "::set-output name=id::$(docker network ls | grep github_network | awk '{ print $1 }')"
|
||||
id: network
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
# Database migration
|
||||
- name: Start database migration
|
||||
run: |
|
||||
docker build --target production_up -t "gradido/database:production_up" database/
|
||||
docker run --network ${{ steps.network.outputs.id }} --name=database --env NODE_ENV=production --env DB_HOST=mariadb --env DB_DATABASE=gradido_community_test -d gradido/database:production_up
|
||||
##########################################################################
|
||||
# Build Login-Server Test Docker image ###################################
|
||||
##########################################################################
|
||||
- name: login server | Build `test` image
|
||||
run: |
|
||||
docker build -t "gradido/login_server:test" -f ./login_server/Dockerfiles/ubuntu/Dockerfile.test login_server/
|
||||
##########################################################################
|
||||
# UNIT TESTS BACKEND LOGIN-SERVER #######################################
|
||||
##########################################################################
|
||||
- name: Login-Server | Unit tests
|
||||
run: |
|
||||
docker run --network container:$(docker container ls | grep mariadb | awk '{ print $1 }') -v ~/coverage:/code/build_cov/coverage -v $(pwd)/configs/login_server:/etc/grd_login gradido/login_server:test
|
||||
cp -r ~/coverage ./coverage
|
||||
##########################################################################
|
||||
# COVERAGE CHECK BACKEND LOGIN-SERVER ####################################
|
||||
##########################################################################
|
||||
- name: backend login | Coverage check
|
||||
uses: webcraftmedia/coverage-check-action@master
|
||||
with:
|
||||
report_name: Coverage Backend Login
|
||||
type: lcov
|
||||
result_path: ./coverage/coverage.info
|
||||
min_coverage: 34
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
# JOB: UNIT TEST COMMUNITY-SERVER ###########################################
|
||||
##############################################################################
|
||||
unit_test_community_server:
|
||||
name: Unit tests - Community Server
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_test_community_server]
|
||||
services:
|
||||
mariadb:
|
||||
image: gradido/mariadb:test
|
||||
env:
|
||||
MARIADB_ALLOW_EMPTY_PASSWORD: 1
|
||||
MARIADB_USER: root
|
||||
options: --health-cmd="mysqladmin ping"
|
||||
--health-interval=5s
|
||||
--health-timeout=5s
|
||||
--health-retries=3
|
||||
steps:
|
||||
- name: get mariadb container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep mariadb | awk '{ print $1 }')"
|
||||
id: mariadb_container
|
||||
- name: get automatic created network
|
||||
run: echo "::set-output name=id::$(docker network ls | grep github_network | awk '{ print $1 }')"
|
||||
id: network
|
||||
- name: Start database migration
|
||||
run: docker run --network ${{ steps.network.outputs.id }} --name=database --env NODE_ENV=production --env DB_HOST=mariadb --env DB_DATABASE=gradido_community_test -d gradido/database:production_up
|
||||
- name: get database migration container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep database | awk '{ print $1 }')"
|
||||
id: database_container
|
||||
- name: Start Login-Server
|
||||
run: docker run --network ${{ steps.network.outputs.id }} --name=login-server -d gradido/login_server:with-config
|
||||
- name: get login-server container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep login_server | awk '{ print $1 }')"
|
||||
id: login_server_container
|
||||
##########################################################################
|
||||
# CHECKOUT CODE ##########################################################
|
||||
##########################################################################
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
##########################################################################
|
||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
||||
##########################################################################
|
||||
- name: Download Docker Image (Community-Server)
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: docker-community-server-test
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/community_server.tar
|
||||
# for debugging login-server
|
||||
- name: check login-server
|
||||
run: docker logs ${{ steps.login_server_container.outputs.id }}
|
||||
- name: check mariadb
|
||||
run: docker logs ${{ steps.mariadb_container.outputs.id }}
|
||||
- name: check migration
|
||||
run: docker logs ${{ steps.database_container.outputs.id }}
|
||||
##########################################################################
|
||||
# UNIT TESTS BACKEND COMMUNITY-SERVER #######################################
|
||||
##########################################################################
|
||||
- name: community server | Unit tests
|
||||
run: |
|
||||
docker run --network ${{ steps.network.outputs.id }} -v ~/coverage:/var/www/cakephp/webroot/coverage gradido/community_server:test
|
||||
cp -r ~/coverage ./coverage
|
||||
#########################################################################
|
||||
# COVERAGE CHECK BACKEND COMMUNITY-SERVER ####################################
|
||||
##########################################################################
|
||||
- name: backend community | Coverage check
|
||||
uses: einhornimmond/coverage-check-action@master
|
||||
with:
|
||||
report_name: Coverage Backend Community
|
||||
type: phpunit
|
||||
result_path: ./coverage/coverage.info
|
||||
min_coverage: 10
|
||||
min_coverage: 54
|
||||
token: ${{ github.token }}
|
||||
|
||||
##########################################################################
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -5,3 +5,9 @@ nbproject
|
||||
.metadata
|
||||
/.env
|
||||
package-lock.json
|
||||
/deployment/bare_metal/.env
|
||||
/deployment/bare_metal/nginx/sites-available/gradido.conf
|
||||
/deployment/bare_metal/nginx/sites-available/update-page.conf
|
||||
/deployment/bare_metal/nginx/update-page/updating.html
|
||||
/deployment/bare_metal/log
|
||||
/deployment/bare_metal/backup
|
||||
|
||||
35
.gitmodules
vendored
35
.gitmodules
vendored
@ -1,36 +1 @@
|
||||
[submodule "gn"]
|
||||
path = gn
|
||||
url = https://github.com/gradido/gn.git
|
||||
branch = master
|
||||
|
||||
[submodule "login_server/dependencies/tinf"]
|
||||
path = login_server/dependencies/tinf
|
||||
url = https://github.com/jibsen/tinf.git
|
||||
[submodule "login_server/dependencies/mariadb-connector-c"]
|
||||
path = login_server/dependencies/mariadb-connector-c
|
||||
url = https://github.com/MariaDB/mariadb-connector-c.git
|
||||
[submodule "login_server/src/proto"]
|
||||
path = login_server/src/proto
|
||||
url = https://github.com/gradido/gradido_protocol.git
|
||||
[submodule "login_server/dependencies/spirit-po"]
|
||||
path = login_server/dependencies/spirit-po
|
||||
url = https://github.com/cbeck88/spirit-po.git
|
||||
[submodule "login_server/dependencies/poco"]
|
||||
path = login_server/dependencies/poco
|
||||
url = https://github.com/pocoproject/poco.git
|
||||
[submodule "login_server/dependencies/cmake-modules"]
|
||||
path = login_server/dependencies/cmake-modules
|
||||
url = https://github.com/viaduck/cmake-modules.git
|
||||
|
||||
[submodule "community_server/src/protobuf"]
|
||||
path = community_server/src/protobuf
|
||||
url = https://github.com/gradido/gradido_protocol.git
|
||||
[submodule "login_server/dependencies/libsodium"]
|
||||
path = login_server/dependencies/libsodium
|
||||
url = https://github.com/jedisct1/libsodium.git
|
||||
[submodule "login_server/src/proto"]
|
||||
path = login_server/src/proto
|
||||
url = https://github.com/gradido/gradido_protocol.git
|
||||
[submodule "login_server/dependencies/protobuf"]
|
||||
path = login_server/dependencies/protobuf
|
||||
url = https://github.com/protocolbuffers/protobuf.git
|
||||
|
||||
308
CHANGELOG.md
308
CHANGELOG.md
@ -4,12 +4,274 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [1.6.6](https://github.com/gradido/gradido/compare/1.6.5...1.6.6)
|
||||
|
||||
- Fix: Upper case email on register breaks account [`#1542`](https://github.com/gradido/gradido/pull/1542)
|
||||
- 1106 first transaction cannot be expanded [`#1432`](https://github.com/gradido/gradido/pull/1432)
|
||||
- added missing bootstrap scss. bootstrap/scss/bootstrap, plus more mis… [`#1540`](https://github.com/gradido/gradido/pull/1540)
|
||||
- feat: Seed Deleted User [`#1533`](https://github.com/gradido/gradido/pull/1533)
|
||||
- fix: No Creations for Deleted Users [`#1534`](https://github.com/gradido/gradido/pull/1534)
|
||||
- fix: Wrong Key Name for Recover User [`#1535`](https://github.com/gradido/gradido/pull/1535)
|
||||
- [Feature] : user deleted and undeleted functions for adminarea [`#1520`](https://github.com/gradido/gradido/pull/1520)
|
||||
- fix: Possible SQL Exception in User Search [`#1530`](https://github.com/gradido/gradido/pull/1530)
|
||||
- Feature: Make lint warnings unwanted [`#1529`](https://github.com/gradido/gradido/pull/1529)
|
||||
- 1459 list data again on confirm creation [`#1467`](https://github.com/gradido/gradido/pull/1467)
|
||||
- fix: Return Empty Array When No Pending Creations Are Present [`#1526`](https://github.com/gradido/gradido/pull/1526)
|
||||
- Fix: Correct path of index.js in production [`#1525`](https://github.com/gradido/gradido/pull/1525)
|
||||
- refactor: Get Open Creations by One Query [`#1524`](https://github.com/gradido/gradido/pull/1524)
|
||||
- Admin: Langsame Benutzer-Suche [`#1472`](https://github.com/gradido/gradido/pull/1472)
|
||||
- fix: Backend Unit Tests Running Again [`#1513`](https://github.com/gradido/gradido/pull/1513)
|
||||
- Refactor: Combine transaction tables [`#1523`](https://github.com/gradido/gradido/pull/1523)
|
||||
- Refactor: User resolver [`#1522`](https://github.com/gradido/gradido/pull/1522)
|
||||
- feature: Soft-Delete for users (backend) [`#1521`](https://github.com/gradido/gradido/pull/1521)
|
||||
- feature: Soft-Delete for users (database only) [`#1516`](https://github.com/gradido/gradido/pull/1516)
|
||||
- refactor: Improve Decay Display [`#1517`](https://github.com/gradido/gradido/pull/1517)
|
||||
- 404 page needs back to login button [`#1515`](https://github.com/gradido/gradido/pull/1515)
|
||||
- feature: show current version in admin footer [`#1514`](https://github.com/gradido/gradido/pull/1514)
|
||||
- fix: Never Sent Email Text [`#1512`](https://github.com/gradido/gradido/pull/1512)
|
||||
- refactor: static decay block [`#1405`](https://github.com/gradido/gradido/pull/1405)
|
||||
- refactor: Use Bootstrap Vue Toast [`#1499`](https://github.com/gradido/gradido/pull/1499)
|
||||
- fix: Catch GDT Server Errors [`#1479`](https://github.com/gradido/gradido/pull/1479)
|
||||
- Fix: Autochangelog - no commits [`#1498`](https://github.com/gradido/gradido/pull/1498)
|
||||
|
||||
#### [1.6.5](https://github.com/gradido/gradido/compare/1.6.4...1.6.5)
|
||||
|
||||
> 15 February 2022
|
||||
|
||||
- v1.6.5 [`#1497`](https://github.com/gradido/gradido/pull/1497)
|
||||
- Fix: Elopage Hook Crash 2 [`#1481`](https://github.com/gradido/gradido/pull/1481)
|
||||
|
||||
#### [1.6.4](https://github.com/gradido/gradido/compare/1.6.3...1.6.4)
|
||||
|
||||
> 14 February 2022
|
||||
|
||||
- v1.6.4 [`#1478`](https://github.com/gradido/gradido/pull/1478)
|
||||
- fix: Admin Email Confirmation Date and Time [`#1448`](https://github.com/gradido/gradido/pull/1448)
|
||||
- Fix: Do not log password or token to the console [`#1477`](https://github.com/gradido/gradido/pull/1477)
|
||||
- Fix: Elopage Hook Crash [`#1474`](https://github.com/gradido/gradido/pull/1474)
|
||||
- 538 unify all buttons [`#1455`](https://github.com/gradido/gradido/pull/1455)
|
||||
- 833 old error is shown for a second even if transaction is successful [`#1460`](https://github.com/gradido/gradido/pull/1460)
|
||||
- fix: Wrong Email Spelling in German [`#1446`](https://github.com/gradido/gradido/pull/1446)
|
||||
- fix: Redirect to Login after Register [`#1445`](https://github.com/gradido/gradido/pull/1445)
|
||||
- refactor: Split User Table Component in Admin Interface [`#1443`](https://github.com/gradido/gradido/pull/1443)
|
||||
|
||||
#### [1.6.3](https://github.com/gradido/gradido/compare/1.6.2...1.6.3)
|
||||
|
||||
> 9 February 2022
|
||||
|
||||
- v1.6.3 [`#1447`](https://github.com/gradido/gradido/pull/1447)
|
||||
- add .btn-outline-secondary in scss [`#1442`](https://github.com/gradido/gradido/pull/1442)
|
||||
- Profil settings and footer refactor [`#1440`](https://github.com/gradido/gradido/pull/1440)
|
||||
|
||||
#### [1.6.2](https://github.com/gradido/gradido/compare/1.6.1...1.6.2)
|
||||
|
||||
> 8 February 2022
|
||||
|
||||
- v1.6.2 [`#1438`](https://github.com/gradido/gradido/pull/1438)
|
||||
- updated_changelog_library [`#1437`](https://github.com/gradido/gradido/pull/1437)
|
||||
- admin interface does user have member area [`#1416`](https://github.com/gradido/gradido/pull/1416)
|
||||
- Refactor - Remove community_server [`#1408`](https://github.com/gradido/gradido/pull/1408)
|
||||
- 1389 transactions tabs are not well designed [`#1425`](https://github.com/gradido/gradido/pull/1425)
|
||||
- fix_community_name_description [`#1429`](https://github.com/gradido/gradido/pull/1429)
|
||||
- remove_unnecessary_repositories [`#1406`](https://github.com/gradido/gradido/pull/1406)
|
||||
- clean_database_users [`#1427`](https://github.com/gradido/gradido/pull/1427)
|
||||
- remove_gradido_node [`#1431`](https://github.com/gradido/gradido/pull/1431)
|
||||
- add updateTransactions function for GDD balance if reload page [`#1423`](https://github.com/gradido/gradido/pull/1423)
|
||||
- 1390 display error when navigating to send form without any gdd [`#1424`](https://github.com/gradido/gradido/pull/1424)
|
||||
- have an delete button for the search input [`#1413`](https://github.com/gradido/gradido/pull/1413)
|
||||
- reset all selected users in mass creation [`#1422`](https://github.com/gradido/gradido/pull/1422)
|
||||
- combine_user_tables [`#1411`](https://github.com/gradido/gradido/pull/1411)
|
||||
- feat: Test Table Row Details Toggling [`#1420`](https://github.com/gradido/gradido/pull/1420)
|
||||
- feat: Improved Tests for Mass Creation [`#1419`](https://github.com/gradido/gradido/pull/1419)
|
||||
- refactor: Mixin for Creation Labels [`#1409`](https://github.com/gradido/gradido/pull/1409)
|
||||
- Marque community_server as to be removed. [`#1407`](https://github.com/gradido/gradido/pull/1407)
|
||||
- database_transaction_signatures [`#1368`](https://github.com/gradido/gradido/pull/1368)
|
||||
- database_pending_creations [`#1367`](https://github.com/gradido/gradido/pull/1367)
|
||||
- fix_seed [`#1410`](https://github.com/gradido/gradido/pull/1410)
|
||||
- clean_database [`#1362`](https://github.com/gradido/gradido/pull/1362)
|
||||
- multiple creation already selected users remain saved [`#1376`](https://github.com/gradido/gradido/pull/1376)
|
||||
- fix: Localize Datetime in Admin Interface [`#1327`](https://github.com/gradido/gradido/pull/1327)
|
||||
- feat: Remove Login Server [`#1383`](https://github.com/gradido/gradido/pull/1383)
|
||||
- refactor: Tag Last Version with Login Server [`#1391`](https://github.com/gradido/gradido/pull/1391)
|
||||
- if an email is not confirmed, a user cannot be added to any multiple … [`#1374`](https://github.com/gradido/gradido/pull/1374)
|
||||
- cleanups_refactors [`#1404`](https://github.com/gradido/gradido/pull/1404)
|
||||
- 1365 clear bootstrap version for vue2, preparation for new template [`#1366`](https://github.com/gradido/gradido/pull/1366)
|
||||
- upgrade vue version from ^2.6.11 to 2.6.12 [`#1382`](https://github.com/gradido/gradido/pull/1382)
|
||||
- remove vue-qrcode from dashboard-plugin [`#1364`](https://github.com/gradido/gradido/pull/1364)
|
||||
- remove unused package from frontend [`#1360`](https://github.com/gradido/gradido/pull/1360)
|
||||
|
||||
#### [1.6.1](https://github.com/gradido/gradido/compare/1.6.0...1.6.1)
|
||||
|
||||
> 28 January 2022
|
||||
|
||||
- Hotfix elopage [`#1358`](https://github.com/gradido/gradido/pull/1358)
|
||||
- change standard text für creation [`#1343`](https://github.com/gradido/gradido/pull/1343)
|
||||
- Check if user email is activated to make a creation. [`#1356`](https://github.com/gradido/gradido/pull/1356)
|
||||
- fix: Creation Confirmation User Ids [`#1345`](https://github.com/gradido/gradido/pull/1345)
|
||||
|
||||
#### [1.6.0](https://github.com/gradido/gradido/compare/1.5.1...1.6.0)
|
||||
|
||||
> 27 January 2022
|
||||
|
||||
- v1.6.0 [`#1357`](https://github.com/gradido/gradido/pull/1357)
|
||||
- fix updatePendingCreation method [`#1346`](https://github.com/gradido/gradido/pull/1346)
|
||||
- klicktipp_config [`#1348`](https://github.com/gradido/gradido/pull/1348)
|
||||
- 1351 form to create incorrect due to change of bootstrap version [`#1352`](https://github.com/gradido/gradido/pull/1352)
|
||||
- first draft of releaseplan [`#1349`](https://github.com/gradido/gradido/pull/1349)
|
||||
- Rechtschreibfehler behoben, entgültig zu endgültig [`#1354`](https://github.com/gradido/gradido/pull/1354)
|
||||
- production_deployment [`#1341`](https://github.com/gradido/gradido/pull/1341)
|
||||
- import_production_data [`#1324`](https://github.com/gradido/gradido/pull/1324)
|
||||
- remove division / 100 for GDT [`#1339`](https://github.com/gradido/gradido/pull/1339)
|
||||
- fix: Language Set Correctly after Page Reload in Admin Interface [`#1334`](https://github.com/gradido/gradido/pull/1334)
|
||||
- fix: Set Locale after Login [`#1328`](https://github.com/gradido/gradido/pull/1328)
|
||||
- deployment_community_parameters [`#1331`](https://github.com/gradido/gradido/pull/1331)
|
||||
- bold text if acive page [`#1337`](https://github.com/gradido/gradido/pull/1337)
|
||||
- 1303 thanks page after set password [`#1335`](https://github.com/gradido/gradido/pull/1335)
|
||||
- Changed the toasted message for successful creating a creation. [`#1332`](https://github.com/gradido/gradido/pull/1332)
|
||||
- Added a required to the password repeat field, errors are shown immed… [`#1333`](https://github.com/gradido/gradido/pull/1333)
|
||||
- fix_rejected_login [`#1329`](https://github.com/gradido/gradido/pull/1329)
|
||||
- defined fields for Transactionlist in UserSearch in Adminarea, add Lo… [`#1322`](https://github.com/gradido/gradido/pull/1322)
|
||||
- fix: Render Localized Balance in NavBar [`#1321`](https://github.com/gradido/gradido/pull/1321)
|
||||
- 1227 vergaenglichkeit besser darstellen [`#1312`](https://github.com/gradido/gradido/pull/1312)
|
||||
- 1300 gray out register button further when disabled [`#1316`](https://github.com/gradido/gradido/pull/1316)
|
||||
- change Admin Gradido-Logo font color to white [`#1315`](https://github.com/gradido/gradido/pull/1315)
|
||||
- 1299 remove text from register publisher [`#1317`](https://github.com/gradido/gradido/pull/1317)
|
||||
- 1249 gdd gdt values look displaced or cut off [`#1318`](https://github.com/gradido/gradido/pull/1318)
|
||||
- 1307 admin rename wallet to my account [`#1314`](https://github.com/gradido/gradido/pull/1314)
|
||||
- 1011 visualize and refine the register process [`#1136`](https://github.com/gradido/gradido/pull/1136)
|
||||
- New roadmap [`#1219`](https://github.com/gradido/gradido/pull/1219)
|
||||
- fix: Redirect after Email Confirmation [`#1308`](https://github.com/gradido/gradido/pull/1308)
|
||||
- fix_docker_logging [`#1309`](https://github.com/gradido/gradido/pull/1309)
|
||||
- feat: Paginate User Table [`#1293`](https://github.com/gradido/gradido/pull/1293)
|
||||
- Adding button, to link back to the login page from the error not activated page. [`#1297`](https://github.com/gradido/gradido/pull/1297)
|
||||
- [WIP] new_deployment_stage2 [`#1290`](https://github.com/gradido/gradido/pull/1290)
|
||||
- add icons in menu and navbarmenu [`#1298`](https://github.com/gradido/gradido/pull/1298)
|
||||
- Withdrew horrizontal scrollbar [`#1295`](https://github.com/gradido/gradido/pull/1295)
|
||||
- transactionCount false or transactionCount error text hide [`#1259`](https://github.com/gradido/gradido/pull/1259)
|
||||
- 1265 mouse hovering over pencil in settings [`#1270`](https://github.com/gradido/gradido/pull/1270)
|
||||
- Fix redirect to /logout, now redirects to the wallet login. [`#1285`](https://github.com/gradido/gradido/pull/1285)
|
||||
- feat: Test and Refactor Send Email [`#1224`](https://github.com/gradido/gradido/pull/1224)
|
||||
- new_deployment [`#1220`](https://github.com/gradido/gradido/pull/1220)
|
||||
- Withdrew space at the end of an locale definition. [`#1283`](https://github.com/gradido/gradido/pull/1283)
|
||||
- Fix removing from masscreationlist now gives right item to the left l… [`#1284`](https://github.com/gradido/gradido/pull/1284)
|
||||
- onboarding process document [`#1148`](https://github.com/gradido/gradido/pull/1148)
|
||||
- Don't show the language-switch on the /register page. [`#1282`](https://github.com/gradido/gradido/pull/1282)
|
||||
- language_updates_bernd [`#1260`](https://github.com/gradido/gradido/pull/1260)
|
||||
- Admin Logout redirectes to the wallet login page. [`#1271`](https://github.com/gradido/gradido/pull/1271)
|
||||
- fix: Admin Creation List [`#1276`](https://github.com/gradido/gradido/pull/1276)
|
||||
- fix: Override Existing Token in Response Header [`#1278`](https://github.com/gradido/gradido/pull/1278)
|
||||
- fix: Pass Language to Admin Interface [`#1280`](https://github.com/gradido/gradido/pull/1280)
|
||||
- feat: Seed Garrick Ollivander [`#1277`](https://github.com/gradido/gradido/pull/1277)
|
||||
- fix: Fix Update of Open Creations [`#1273`](https://github.com/gradido/gradido/pull/1273)
|
||||
- fix: Change Password in User Profile [`#1266`](https://github.com/gradido/gradido/pull/1266)
|
||||
- Wallet shows the wrong gdt sum (gdtSum * 100) [`#1269`](https://github.com/gradido/gradido/pull/1269)
|
||||
- 1240 account not activated needs a button to login page [`#1261`](https://github.com/gradido/gradido/pull/1261)
|
||||
- reverse filter for unregistered emails [`#1256`](https://github.com/gradido/gradido/pull/1256)
|
||||
- close row-details automatically when switching to multiple creation page [`#1245`](https://github.com/gradido/gradido/pull/1245)
|
||||
- change creationdatas if change pending creation [`#1244`](https://github.com/gradido/gradido/pull/1244)
|
||||
- 1230 details für wiederholte email bestaetigen versenden [`#1255`](https://github.com/gradido/gradido/pull/1255)
|
||||
- 1221 month names of the open creation in multiple creation [`#1222`](https://github.com/gradido/gradido/pull/1222)
|
||||
- Documentation Template-Overview-2021 PDF [`#1194`](https://github.com/gradido/gradido/pull/1194)
|
||||
- Admin create multiple pending creations [`#1203`](https://github.com/gradido/gradido/pull/1203)
|
||||
- feat: Catch No Cookies Allowed [`#1187`](https://github.com/gradido/gradido/pull/1187)
|
||||
- Remove confirmation of own pending creation [`#1215`](https://github.com/gradido/gradido/pull/1215)
|
||||
- feat: Test Create User Mutation [`#1217`](https://github.com/gradido/gradido/pull/1217)
|
||||
- #751 Änderungen auf Federation und Community-Erstellprozess [`#969`](https://github.com/gradido/gradido/pull/969)
|
||||
- 1055 concept of operations requirements [`#1129`](https://github.com/gradido/gradido/pull/1129)
|
||||
- Admin Translations German English [`#1218`](https://github.com/gradido/gradido/pull/1218)
|
||||
- Roadmap [`#1213`](https://github.com/gradido/gradido/pull/1213)
|
||||
- pr_admin_refactor [`#1214`](https://github.com/gradido/gradido/pull/1214)
|
||||
- 1197 admin interface created transactions list [`#1202`](https://github.com/gradido/gradido/pull/1202)
|
||||
- migration_0006_login_users_collation [`#1207`](https://github.com/gradido/gradido/pull/1207)
|
||||
- wrong_login_is_not_password_reset [`#1179`](https://github.com/gradido/gradido/pull/1179)
|
||||
- Moved reverse of the getPendingCreations to the AdminResolver instead… [`#1185`](https://github.com/gradido/gradido/pull/1185)
|
||||
- Withdrew password argument of createUser. [`#1206`](https://github.com/gradido/gradido/pull/1206)
|
||||
- fix_optin_code_regeneration [`#1195`](https://github.com/gradido/gradido/pull/1195)
|
||||
- 1057 display gdd balance when sending [`#1149`](https://github.com/gradido/gradido/pull/1149)
|
||||
- 1145 refactor admin resolver from master [`#1164`](https://github.com/gradido/gradido/pull/1164)
|
||||
- Error handling in GddTransactionList.vue [`#1183`](https://github.com/gradido/gradido/pull/1183)
|
||||
- state-balance balanceDate can't get the date of the last transaction [`#1182`](https://github.com/gradido/gradido/pull/1182)
|
||||
- feat: Frontend Refactor and Tests [`#1186`](https://github.com/gradido/gradido/pull/1186)
|
||||
- fix: No Nodemon in Database Scripts [`#1167`](https://github.com/gradido/gradido/pull/1167)
|
||||
- feat: Test Creation in Admin Interface [`#1172`](https://github.com/gradido/gradido/pull/1172)
|
||||
- 533 refactor menu remove tim [`#1162`](https://github.com/gradido/gradido/pull/1162)
|
||||
- database_docker_fixes [`#1176`](https://github.com/gradido/gradido/pull/1176)
|
||||
- naming_conventions_pending_tasks [`#1184`](https://github.com/gradido/gradido/pull/1184)
|
||||
- fix_i18n [`#1180`](https://github.com/gradido/gradido/pull/1180)
|
||||
- fix_corrupted_sender_balance [`#1178`](https://github.com/gradido/gradido/pull/1178)
|
||||
- Withdrew * 10000 and / 10000 since we need to store the full value. [`#1181`](https://github.com/gradido/gradido/pull/1181)
|
||||
- catch error Client certificate revoked and pass error.session-expired… [`#1152`](https://github.com/gradido/gradido/pull/1152)
|
||||
- feat: Test Apollo Provider in Frontend [`#1161`](https://github.com/gradido/gradido/pull/1161)
|
||||
- fix: Pending Creations are Updated Without Page Reload [`#1160`](https://github.com/gradido/gradido/pull/1160)
|
||||
- Error on upgrade database after downgrade [`#1119`](https://github.com/gradido/gradido/pull/1119)
|
||||
- Login fix creation validation [`#1159`](https://github.com/gradido/gradido/pull/1159)
|
||||
- login_call_resetPassword [`#1130`](https://github.com/gradido/gradido/pull/1130)
|
||||
- register add tabindex-1 on password show [`#1158`](https://github.com/gradido/gradido/pull/1158)
|
||||
- feat: Seed Transaction Creations [`#1146`](https://github.com/gradido/gradido/pull/1146)
|
||||
- Admin confirm pending creation [`#1153`](https://github.com/gradido/gradido/pull/1153)
|
||||
- Admin delete pending creation query [`#1141`](https://github.com/gradido/gradido/pull/1141)
|
||||
- fix: Catch Expired Session when Coming from Admin Interface [`#1151`](https://github.com/gradido/gradido/pull/1151)
|
||||
- docu_release [`#1138`](https://github.com/gradido/gradido/pull/1138)
|
||||
- feat: Verify Login in Admin Interface [`#1150`](https://github.com/gradido/gradido/pull/1150)
|
||||
- Admin pending creation queries [`#1140`](https://github.com/gradido/gradido/pull/1140)
|
||||
- 1137 publisher id as input field on register [`#1147`](https://github.com/gradido/gradido/pull/1147)
|
||||
- Admin pending creation [`#1135`](https://github.com/gradido/gradido/pull/1135)
|
||||
- feat: Setup Data Seeding [`#1121`](https://github.com/gradido/gradido/pull/1121)
|
||||
- fix_admin_token_renewal [`#1139`](https://github.com/gradido/gradido/pull/1139)
|
||||
- backend_rights [`#1126`](https://github.com/gradido/gradido/pull/1126)
|
||||
- Login admin interface [`#1125`](https://github.com/gradido/gradido/pull/1125)
|
||||
- Adminbereich first step [`#1116`](https://github.com/gradido/gradido/pull/1116)
|
||||
- Login hook elopage [`#1112`](https://github.com/gradido/gradido/pull/1112)
|
||||
- Since we don't make a request to the login_server we need to catch if… [`#1131`](https://github.com/gradido/gradido/pull/1131)
|
||||
- feat: Add Server Users Entity [`#1127`](https://github.com/gradido/gradido/pull/1127)
|
||||
- Stale: 1002-language-selection-register [`#1113`](https://github.com/gradido/gradido/pull/1113)
|
||||
- Error for removing coin animation [`#1120`](https://github.com/gradido/gradido/pull/1120)
|
||||
- Adding a check that the user is activated before letting them login. [`#1099`](https://github.com/gradido/gradido/pull/1099)
|
||||
- Changed the Auto increment so that it is done after the rollback and … [`#1128`](https://github.com/gradido/gradido/pull/1128)
|
||||
- Login call unsecure login [`#1095`](https://github.com/gradido/gradido/pull/1095)
|
||||
- feat: Setup Admin Interface [`#1045`](https://github.com/gradido/gradido/pull/1045)
|
||||
- login_call_updateUserInfos [`#1084`](https://github.com/gradido/gradido/pull/1084)
|
||||
- fix: Await Resolved Promises in Backend Unit Tests [`#1079`](https://github.com/gradido/gradido/pull/1079)
|
||||
- feat: Raise Test Coverage Frontend [`#1102`](https://github.com/gradido/gradido/pull/1102)
|
||||
- login_call_logout [`#1096`](https://github.com/gradido/gradido/pull/1096)
|
||||
- login_call_hasElopage [`#1083`](https://github.com/gradido/gradido/pull/1083)
|
||||
- login_call_create_user [`#1070`](https://github.com/gradido/gradido/pull/1070)
|
||||
- 1036 register page breaks without community [`#1043`](https://github.com/gradido/gradido/pull/1043)
|
||||
- Apollo create transactions without signation [`#1044`](https://github.com/gradido/gradido/pull/1044)
|
||||
- change getCustomRepository to createUser function [`#1046`](https://github.com/gradido/gradido/pull/1046)
|
||||
- fresh_install_instructions [`#1065`](https://github.com/gradido/gradido/pull/1065)
|
||||
- login_call_check_username [`#1037`](https://github.com/gradido/gradido/pull/1037)
|
||||
- feat: Setup Unit Tests for Resolvers in Backend [`#951`](https://github.com/gradido/gradido/pull/951)
|
||||
- Login hotfixes 1.5.1 [`#1075`](https://github.com/gradido/gradido/pull/1075)
|
||||
- vscode_suggest_extensions [`#1073`](https://github.com/gradido/gradido/pull/1073)
|
||||
- Documentation on the coin creation and the steps to have the accounts… [`#1052`](https://github.com/gradido/gradido/pull/1052)
|
||||
- fix open community server user account creation [`#1072`](https://github.com/gradido/gradido/pull/1072)
|
||||
- when creating the register page, everything in the form is set to blank [`#1025`](https://github.com/gradido/gradido/pull/1025)
|
||||
- Spelling error fixed 'Berechnungsformel' [`#1048`](https://github.com/gradido/gradido/pull/1048)
|
||||
- checkEmail.vue page text is displayed correctly now [`#1051`](https://github.com/gradido/gradido/pull/1051)
|
||||
- Adminarea old [`#1058`](https://github.com/gradido/gradido/pull/1058)
|
||||
- migrate_login_database [`#1031`](https://github.com/gradido/gradido/pull/1031)
|
||||
- Text changes for german and english. [`#1041`](https://github.com/gradido/gradido/pull/1041)
|
||||
- refactor: Transaction Component [`#1026`](https://github.com/gradido/gradido/pull/1026)
|
||||
- 1017 fixe dashboard layout safari [`#1038`](https://github.com/gradido/gradido/pull/1038)
|
||||
- 707 separate account overview and send [`#970`](https://github.com/gradido/gradido/pull/970)
|
||||
- Link to the funding contributions of gradido.net memberships [`#984`](https://github.com/gradido/gradido/pull/984)
|
||||
- fix: Decay Rounded to Ceil [`#1021`](https://github.com/gradido/gradido/pull/1021)
|
||||
- fix: Flaky Dashboard Layout Test on Logout [`#1024`](https://github.com/gradido/gradido/pull/1024)
|
||||
- Move Entity models into database [`#956`](https://github.com/gradido/gradido/pull/956)
|
||||
- Text 'No decay' replaced by the number 0.00 [`#1023`](https://github.com/gradido/gradido/pull/1023)
|
||||
- change text 'eingetraten' to 'eingetragen' [`#1022`](https://github.com/gradido/gradido/pull/1022)
|
||||
- fix: Test Default Publisher ID in Sidebar Menu [`#987`](https://github.com/gradido/gradido/pull/987)
|
||||
- increase_frontend_coverage [`#1020`](https://github.com/gradido/gradido/pull/1020)
|
||||
- analyse_bundle [`#1019`](https://github.com/gradido/gradido/pull/1019)
|
||||
- release_issue_template [`#1013`](https://github.com/gradido/gradido/pull/1013)
|
||||
- fix_changelog [`#1014`](https://github.com/gradido/gradido/pull/1014)
|
||||
|
||||
#### [1.5.1](https://github.com/gradido/gradido/compare/1.5.0...1.5.1)
|
||||
|
||||
> 15 October 2021
|
||||
|
||||
- fix isExitInDb [`#994`](https://github.com/gradido/gradido/pull/994)
|
||||
- fix [`80228ef`](https://github.com/gradido/gradido/commit/80228ef842d4087ea4b80934b15b8112611e3e33)
|
||||
|
||||
#### [1.5.0](https://github.com/gradido/gradido/compare/1.4.0...1.5.0)
|
||||
|
||||
@ -86,9 +348,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- update docker files [`#830`](https://github.com/gradido/gradido/pull/830)
|
||||
- added publisher_id field to user [`#245`](https://github.com/gradido/gradido/pull/245)
|
||||
- webpack update [`#811`](https://github.com/gradido/gradido/pull/811)
|
||||
- resolvers [`562ad9a`](https://github.com/gradido/gradido/commit/562ad9ae31d97f90a371452bed1ffe10ebf2d3a5)
|
||||
- deleted inputs (now args) [`8ab542a`](https://github.com/gradido/gradido/commit/8ab542a28acf6b78d7a9e7fe7757363d225f7b4f)
|
||||
- fix UserCard_CoinAnimation to properly use the store, have 100% coverage and other minor fixes & simplifications [`ce826de`](https://github.com/gradido/gradido/commit/ce826deb1d6d92caba514713539dca2da3f74de7)
|
||||
|
||||
#### [1.4.0](https://github.com/gradido/gradido/compare/1.3.1...1.4.0)
|
||||
|
||||
@ -130,9 +389,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- feat: Vue Apollo Client [`#701`](https://github.com/gradido/gradido/pull/701)
|
||||
- change text from Geld to Gradidos [`#711`](https://github.com/gradido/gradido/pull/711)
|
||||
- fix fix [`#728`](https://github.com/gradido/gradido/pull/728)
|
||||
- sort locales [`ec12a28`](https://github.com/gradido/gradido/commit/ec12a28f81577d530f58b42b7f8c2c7d20dffd64)
|
||||
- feat: Unify and Sort Locales [`aba4f4d`](https://github.com/gradido/gradido/commit/aba4f4d20e0a13016e3528a1c5c30c111eb3a9f1)
|
||||
- feat: Increase Coverage [`3c061bc`](https://github.com/gradido/gradido/commit/3c061bcb8d1a3a47442ed6a351e1428e15b314aa)
|
||||
|
||||
#### [1.3.1](https://github.com/gradido/gradido/compare/1.3.0...1.3.1)
|
||||
|
||||
@ -141,9 +397,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- fix: Translations and Formula Display [`#727`](https://github.com/gradido/gradido/pull/727)
|
||||
- 612 docu structure [`#688`](https://github.com/gradido/gradido/pull/688)
|
||||
- Community update for gdt list GDT transaction format [`#726`](https://github.com/gradido/gradido/pull/726)
|
||||
- [#612] new directory structure in /docu/Concepts [`10bf3b0`](https://github.com/gradido/gradido/commit/10bf3b0cdfa6c44f879be0155e93f636601a051b)
|
||||
- #612 additional documents [`ac0ed4f`](https://github.com/gradido/gradido/commit/ac0ed4fee81caff26d09b5de47dd130f12abdb45)
|
||||
- #612 docu restructuring [`e67e1c4`](https://github.com/gradido/gradido/commit/e67e1c41e78264698e6fae4cf1d29751de7e7b29)
|
||||
|
||||
#### [1.3.0](https://github.com/gradido/gradido/compare/1.2.1...1.3.0)
|
||||
|
||||
@ -165,9 +418,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Backend Setup [`#584`](https://github.com/gradido/gradido/pull/584)
|
||||
- text-size in textarea and font-variante if focus [`#677`](https://github.com/gradido/gradido/pull/677)
|
||||
- 680 app large maximum width [`#681`](https://github.com/gradido/gradido/pull/681)
|
||||
- linting, server is working [`34b30b2`](https://github.com/gradido/gradido/commit/34b30b216b6fafcb5b686d4b023b05f2e9766bdf)
|
||||
- server stack seems to work. Graphql does not load properly yet [`43f7cf8`](https://github.com/gradido/gradido/commit/43f7cf87679713d436a64d569d6af1594a12ee33)
|
||||
- initial commit, base packages [`fdf0979`](https://github.com/gradido/gradido/commit/fdf0979830fece04208a6b3bb06bb5323a3c149b)
|
||||
|
||||
#### [1.2.1](https://github.com/gradido/gradido/compare/1.2.0...1.2.1)
|
||||
|
||||
@ -178,9 +428,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- fix unneccessary migration run on fresh (docker) setup [`#654`](https://github.com/gradido/gradido/pull/654)
|
||||
- move back decay as standalone transaction in old frontend [`#656`](https://github.com/gradido/gradido/pull/656)
|
||||
- fix display error with creation [`#652`](https://github.com/gradido/gradido/pull/652)
|
||||
- release [`a0b8056`](https://github.com/gradido/gradido/commit/a0b8056c17b22570a1b1dbb6fa6ce71e561b04af)
|
||||
- update content for frontend [`d37ce09`](https://github.com/gradido/gradido/commit/d37ce0949ef97d2a6c6ffaf0be31db9f6d92e743)
|
||||
- exchange positions [`bc000ef`](https://github.com/gradido/gradido/commit/bc000efd87c9701480c4aeaa7b819ab49bfe8f01)
|
||||
|
||||
#### [1.2.0](https://github.com/gradido/gradido/compare/1.1.1...1.2.0)
|
||||
|
||||
@ -219,9 +466,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Update Password Reset E-Mail Subject Encoding [`#579`](https://github.com/gradido/gradido/pull/579)
|
||||
- move decay between transactions into the transactions [`#483`](https://github.com/gradido/gradido/pull/483)
|
||||
- fix #591 [`#591`](https://github.com/gradido/gradido/issues/591)
|
||||
- fix style decay startblick [`cc7778b`](https://github.com/gradido/gradido/commit/cc7778b55d1baaa7be2d9440480e0fb27bb9a930)
|
||||
- Remove dynamic cast because it lead to errors again and agin (Poco::AutoPtr don't work correct with that) [`0db5912`](https://github.com/gradido/gradido/commit/0db5912a67158be8f313c01f06350f8339cb0e28)
|
||||
- Remove dynamic cast because it lead to errors again and agin (Poco::AutoPtr don't work correct with that) [`cee7d7a`](https://github.com/gradido/gradido/commit/cee7d7ac3c4c8c1f481cc3a87fb15422c858413b)
|
||||
|
||||
#### [1.1.1](https://github.com/gradido/gradido/compare/1.1.0...1.1.1)
|
||||
|
||||
@ -232,9 +476,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- update transfer email text [`#574`](https://github.com/gradido/gradido/pull/574)
|
||||
- update mysql because tuple has changed [`#576`](https://github.com/gradido/gradido/pull/576)
|
||||
- Login fix pending transactions [`#578`](https://github.com/gradido/gradido/pull/578)
|
||||
- add test to prevent bug in future [`630d667`](https://github.com/gradido/gradido/commit/630d667e996870a1bf9aa9586b0467d58419e525)
|
||||
- use standard path. add nginx example [`ac249b4`](https://github.com/gradido/gradido/commit/ac249b46830a8039aec52d30b48084b50a264b6f)
|
||||
- add autodeploy bash scripts [`f49cf4d`](https://github.com/gradido/gradido/commit/f49cf4d7f8054d87efa1e12055a7ef0c6d3b9872)
|
||||
|
||||
#### [1.1.0](https://github.com/gradido/gradido/compare/1.0.2...1.1.0)
|
||||
|
||||
@ -276,9 +517,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- login without hedera [`#478`](https://github.com/gradido/gradido/pull/478)
|
||||
- fix: Show Correct Version Number in Footer [`#475`](https://github.com/gradido/gradido/pull/475)
|
||||
- refactor: Remove Element-UI [`#476`](https://github.com/gradido/gradido/pull/476)
|
||||
- remove components Charts, Notification, SearchUser, ButtonCheckbox, Button RadioGroup, Breadcrumb [`159bff7`](https://github.com/gradido/gradido/commit/159bff71df20a5c48f93389b2f990f7fe54e53b9)
|
||||
- fix bug, update dockerfiles to use dependencies without grpc [`dedcebd`](https://github.com/gradido/gradido/commit/dedcebdb95ee0f3dfd2ad62074d4181af38476a2)
|
||||
- add warning to able to forward warnings from community server to client [`2fc3fe9`](https://github.com/gradido/gradido/commit/2fc3fe94a09bae199bf2f34f9df90e8fc3879c2b)
|
||||
|
||||
#### [1.0.2](https://github.com/gradido/gradido/compare/1.0.1...1.0.2)
|
||||
|
||||
@ -305,17 +543,12 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- add dynamic error email if transaction failed [`#452`](https://github.com/gradido/gradido/pull/452)
|
||||
- ceil the last decay [`#449`](https://github.com/gradido/gradido/pull/449)
|
||||
- feat: Raise Coverage of Frontend Unit Tets to 18% [`#447`](https://github.com/gradido/gradido/pull/447)
|
||||
- parse cpsp files automatic in build [`a4a12bb`](https://github.com/gradido/gradido/commit/a4a12bb62b4000e035ff15e17c5a5f5861653ff6)
|
||||
- translate german html encoded error messages to english and use gettext for automatic translation [`d339627`](https://github.com/gradido/gradido/commit/d33962736d94c1cb7a12ff775bc2c8d7505d646e)
|
||||
- 100% coverage of GddTransactionList [`96fb245`](https://github.com/gradido/gradido/commit/96fb245821c69f4d321204a663247d5eee60d92f)
|
||||
|
||||
#### [1.0.1](https://github.com/gradido/gradido/compare/1.0.0...1.0.1)
|
||||
|
||||
> 14 May 2021
|
||||
|
||||
- Login crash fix [`#444`](https://github.com/gradido/gradido/pull/444)
|
||||
- add try catch blocks to prevent login-server from crashing [`22ff220`](https://github.com/gradido/gradido/commit/22ff22072956f8b843037c75c5b16b7ff5d6a2a3)
|
||||
- fix [`14a4243`](https://github.com/gradido/gradido/commit/14a424347817b1fe6912a113bffd70e55d688112)
|
||||
|
||||
### [1.0.0](https://github.com/gradido/gradido/compare/0.9.4...1.0.0)
|
||||
|
||||
@ -435,9 +668,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Background color change [`#117`](https://github.com/gradido/gradido/pull/117)
|
||||
- Delete unused files [`#116`](https://github.com/gradido/gradido/pull/116)
|
||||
- store aufräumen teil 1 [`#115`](https://github.com/gradido/gradido/pull/115)
|
||||
- add migrations table for automatic table data migration [`40a9a8c`](https://github.com/gradido/gradido/commit/40a9a8c2b587f5bef0fcc54136ed7bd13dd91b2b)
|
||||
- update yarn.lock after running yarn install [`7f38c80`](https://github.com/gradido/gradido/commit/7f38c801213ad886e9d34a8d43b00ae423f5f2a0)
|
||||
- use new function for balance overview in old frontend, update balance in session on every php-request [`97c570c`](https://github.com/gradido/gradido/commit/97c570c08cc51ed17a69eb8be8d987f95f3c2ce0)
|
||||
|
||||
#### [0.9.4](https://github.com/gradido/gradido/compare/0.9.3...0.9.4)
|
||||
|
||||
@ -445,9 +675,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
- Vue with nginx [`#84`](https://github.com/gradido/gradido/pull/84)
|
||||
- Build on run [`#103`](https://github.com/gradido/gradido/pull/103)
|
||||
- update debug docker to use dependencies container pushed to docker hub [`1f002f4`](https://github.com/gradido/gradido/commit/1f002f4ed0b12d4b2bf63efceabe546d0c5b58ea)
|
||||
- removed email tasks complete [`8a143be`](https://github.com/gradido/gradido/commit/8a143be8423d7bd894d4f512848895df8b9694b0)
|
||||
- build login-server on docker-compose up in a docker volume so it rebuild only neccessary parts if some c++ files have changed [`0da5279`](https://github.com/gradido/gradido/commit/0da527917523530186e6effe63dc001fc99bd3e3)
|
||||
|
||||
#### [0.9.3](https://github.com/gradido/gradido/compare/0.9.2...0.9.3)
|
||||
|
||||
@ -476,9 +703,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Add Feature in user search old frontend because Support has requested the feature long ago [`#56`](https://github.com/gradido/gradido/pull/56)
|
||||
- sprache angepasst, for login, pwd, sigin [`#54`](https://github.com/gradido/gradido/pull/54)
|
||||
- Improve workflows [`#53`](https://github.com/gradido/gradido/pull/53)
|
||||
- setup eslint with tougher rules [`1f13507`](https://github.com/gradido/gradido/commit/1f13507eacfd93c2248fb841de5f481c9eb1e6bd)
|
||||
- semicolon rule implemented [`6762a02`](https://github.com/gradido/gradido/commit/6762a028f2a3e4f2713b26bed81029defe686ad7)
|
||||
- dev meeting, bernd [`a99de7f`](https://github.com/gradido/gradido/commit/a99de7f5d1f7557c0877eae565aa4263d65aaaf3)
|
||||
|
||||
#### [0.9.2](https://github.com/gradido/gradido/compare/0.9.1...0.9.2)
|
||||
|
||||
@ -488,9 +712,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Reload after login fixed [`#50`](https://github.com/gradido/gradido/pull/50)
|
||||
- Monorepo login server [`#48`](https://github.com/gradido/gradido/pull/48)
|
||||
- Stage0 [`#3`](https://github.com/gradido/gradido/pull/3)
|
||||
- Add auto-sign Transaction functionality [`5592275`](https://github.com/gradido/gradido/commit/55922753a7ffd9552be132501d744da491c409b5)
|
||||
- read in login the real client ip X-Real-IP from nginx forwarded not from community server [`512d307`](https://github.com/gradido/gradido/commit/512d307a19b955bb6e26ae8b274def354829b50f)
|
||||
- move check if all passwords allow direct into pwdValidation so it will work with every code which ask for password [`e2c38c1`](https://github.com/gradido/gradido/commit/e2c38c1a0fc25a4a2bc922c4bbc44d86b6d00d8b)
|
||||
|
||||
#### 0.9.1
|
||||
|
||||
@ -502,6 +723,3 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- [WIP] 2 create a dockerfile for the frontend application [`#6`](https://github.com/gradido/gradido/pull/6)
|
||||
- Master - first step [`#1`](https://github.com/gradido/gradido/pull/1)
|
||||
- Add docker compose [`#7`](https://github.com/gradido/gradido/pull/7)
|
||||
- style 404 side :) [`c7bdf89`](https://github.com/gradido/gradido/commit/c7bdf8978594b932615e48f9bb1c19d3c3bf3fcf)
|
||||
- publish workflow test [`df6f66f`](https://github.com/gradido/gradido/commit/df6f66ffe70baa9ed3f70b460a6c0c14011bb944)
|
||||
- many translations. translation structure [`bf68547`](https://github.com/gradido/gradido/commit/bf685479767d19c246c4d6abe3577dc3cb666346)
|
||||
|
||||
@ -60,14 +60,12 @@ docker-compose -f docker-compose.yml up
|
||||
- [frontend](./frontend) Wallet frontend
|
||||
- [backend](./backend) GraphQL & Business logic backend
|
||||
- [mariadb](./mariadb) Database backend
|
||||
- [login_server](./login_server) User credential storage & business logic backend
|
||||
- [community_server](./community_server/) Business logic backend
|
||||
|
||||
We are currently restructuring the service to reduce dependencies and unify business logic into one place. Furthermore the databases defined for each service will be unified into one.
|
||||
|
||||
### Open the wallet
|
||||
|
||||
Once you have `docker-compose` up and running, you can open [http://localhost/vue](http://localhost/vue) and create yourself a new wallet account.
|
||||
Once you have `docker-compose` up and running, you can open [http://localhost/](http://localhost/) and create yourself a new wallet account.
|
||||
|
||||
## How to release
|
||||
|
||||
@ -90,7 +88,7 @@ Note: The Changelog will be regenerated with all tags on release on the external
|
||||
| Problem | Issue | Solution | Description |
|
||||
| ------- | ----- | -------- | ----------- |
|
||||
| docker-compose raises database connection errors | [#1062](https://github.com/gradido/gradido/issues/1062) | End `ctrl+c` and restart the `docker-compose up` after a successful build | Several Database connection related errors occur in the docker-compose log. |
|
||||
| Wallet page is empty | [#1063](https://github.com/gradido/gradido/issues/1063) | Accept Cookies and Local Storage in your Browser | The page stays empty when navigating to [http://localhost/vue](http://localhost/vue) |
|
||||
| Wallet page is empty | [#1063](https://github.com/gradido/gradido/issues/1063) | Accept Cookies and Local Storage in your Browser | The page stays empty when navigating to [http://localhost/](http://localhost/) |
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
node_modules
|
||||
.git
|
||||
.gitignore
|
||||
.gitignore
|
||||
!.eslintignore
|
||||
@ -1,3 +1,6 @@
|
||||
CONFIG_VERSION=v1.2022-03-18
|
||||
|
||||
GRAPHQL_URI=http://localhost:4000/graphql
|
||||
WALLET_AUTH_URL=http://localhost/vue/authenticate?token=$1
|
||||
WALLET_AUTH_URL=http://localhost/authenticate?token={token}
|
||||
WALLET_URL=http://localhost/login
|
||||
DEBUG_DISABLE_AUTH=false
|
||||
6
admin/.env.template
Normal file
6
admin/.env.template
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_VERSION=$ADMIN_CONFIG_VERSION
|
||||
|
||||
GRAPHQL_URI=$GRAPHQL_URI
|
||||
WALLET_AUTH_URL=$WALLET_AUTH_URL
|
||||
WALLET_URL=$WALLET_URL
|
||||
DEBUG_DISABLE_AUTH=false
|
||||
@ -1,4 +1,3 @@
|
||||
node_modules
|
||||
coverage
|
||||
**/*.min.js
|
||||
dist
|
||||
node_modules/
|
||||
dist/
|
||||
coverage/
|
||||
@ -8,14 +8,42 @@ module.exports = {
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
extends: ['standard', 'plugin:vue/essential', 'plugin:prettier/recommended'],
|
||||
extends: [
|
||||
'standard',
|
||||
'plugin:vue/essential',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@intlify/vue-i18n/recommended',
|
||||
],
|
||||
// required to lint *.vue files
|
||||
plugins: ['vue', 'prettier', 'jest'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.json'],
|
||||
extends: ['plugin:@intlify/vue-i18n/recommended'],
|
||||
},
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
'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,
|
||||
},
|
||||
],
|
||||
'@intlify/vue-i18n/no-dynamic-keys': 'error',
|
||||
'@intlify/vue-i18n/no-unused-keys': [
|
||||
'error',
|
||||
{
|
||||
src: './src',
|
||||
extensions: ['.js', '.vue'],
|
||||
ignores: [],
|
||||
enableFix: false,
|
||||
},
|
||||
],
|
||||
'@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
@ -23,4 +51,12 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
'vue-i18n': {
|
||||
localeDir: './src/locales/*.json',
|
||||
// Specify the version of `vue-i18n` you are using.
|
||||
// If not specified, the message will be parsed twice.
|
||||
messageSyntaxVersion: '^8.26.5',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
3
admin/.gitignore
vendored
3
admin/.gitignore
vendored
@ -2,7 +2,8 @@ node_modules/
|
||||
dist/
|
||||
.cache/
|
||||
|
||||
.env
|
||||
/.env
|
||||
/.env.bak
|
||||
|
||||
# coverage folder
|
||||
coverage/
|
||||
|
||||
18
admin/.stylelintrc.js
Normal file
18
admin/.stylelintrc.js
Normal file
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: ["stylelint-config-standard-scss", "stylelint-config-recommended-vue"],
|
||||
overrides: [
|
||||
{
|
||||
files: "**/*.{scss}",
|
||||
customSyntax: "postcss-scss",
|
||||
extends: ["stylelint-config-standard-scss"],
|
||||
},
|
||||
{
|
||||
files: "**/*.vue",
|
||||
customSyntax: "postcss-html",
|
||||
extends: ["stylelint-config-recommended-vue"],
|
||||
}
|
||||
]
|
||||
|
||||
};
|
||||
@ -3,17 +3,19 @@
|
||||
"description": "Administraion Interface for Gradido",
|
||||
"main": "index.js",
|
||||
"author": "Moriz Wahl",
|
||||
"version": "0.1.0",
|
||||
"version": "1.6.6",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"start": "node run/server.js",
|
||||
"serve": "vue-cli-service serve --open",
|
||||
"dev": "yarn run serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "eslint --ext .js,.vue .",
|
||||
"test": "jest --coverage",
|
||||
"locales": "scripts/missing-keys.sh && scripts/sort.sh"
|
||||
"dev": "yarn run serve",
|
||||
"analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json",
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.vue,.json .",
|
||||
"stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'",
|
||||
"test": "TZ=UTC jest --coverage",
|
||||
"locales": "scripts/sort.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
@ -28,28 +30,28 @@
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-vue": "^2.0.2",
|
||||
"bootstrap": "^5.1.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "^2.21.2",
|
||||
"core-js": "^3.6.5",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^15.6.1",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "26.6.3",
|
||||
"moment": "^2.29.1",
|
||||
"portal-vue": "^2.1.7",
|
||||
"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-moment": "^4.1.0",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue-toasted": "^1.1.28",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-persistedstate": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.15.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",
|
||||
@ -66,6 +68,13 @@
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-vue": "^7.20.0",
|
||||
"jest-environment-jsdom-sixteen": "^2.0.0",
|
||||
"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"
|
||||
},
|
||||
"browserslist": [
|
||||
|
||||
BIN
admin/public/img/elopage_favicon.png
Normal file
BIN
admin/public/img/elopage_favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,15 +1,21 @@
|
||||
// Imports
|
||||
const express = require('express')
|
||||
const serveStatic = require('serve-static')
|
||||
const path = require('path')
|
||||
|
||||
// Port
|
||||
// Host & Port
|
||||
const hostname = '127.0.0.1'
|
||||
const port = process.env.PORT || 8080
|
||||
|
||||
// Express Server
|
||||
const app = express()
|
||||
// eslint-disable-next-line node/no-path-concat
|
||||
app.use(serveStatic(__dirname + '/../dist'))
|
||||
app.listen(port)
|
||||
// Serve files
|
||||
app.use(express.static(path.join(__dirname, '../dist')))
|
||||
// Default to index.html
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '../dist/index.html'))
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`http://admin:${port} server started.`)
|
||||
app.listen(port, hostname, () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening at http://%s:%s/', hostname, port)
|
||||
})
|
||||
|
||||
@ -13,3 +13,11 @@ export default {
|
||||
components: { defaultLayout },
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
.pointer:hover {
|
||||
background-color: rgb(216, 213, 213);
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,24 +1,21 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ConfirmRegisterMailFormular from './ConfirmRegisterMailFormular.vue'
|
||||
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloMutateMock = jest.fn().mockResolvedValue()
|
||||
const toastSuccessMock = jest.fn()
|
||||
const toastErrorMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
$toasted: {
|
||||
success: toastSuccessMock,
|
||||
error: toastErrorMock,
|
||||
},
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
checked: false,
|
||||
email: 'bob@baumeister.de',
|
||||
dateLastSend: '',
|
||||
}
|
||||
@ -53,7 +50,7 @@ describe('ConfirmRegisterMailFormular', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('unregister_mail.success')
|
||||
expect(toastSuccessSpy).toBeCalledWith('unregister_mail.success')
|
||||
})
|
||||
})
|
||||
|
||||
@ -65,7 +62,7 @@ describe('ConfirmRegisterMailFormular', () => {
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorMock).toBeCalledWith('unregister_mail.error')
|
||||
expect(toastErrorSpy).toBeCalledWith('unregister_mail.error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,19 +1,24 @@
|
||||
<template>
|
||||
<div class="component-confirm-register-mail">
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<div class="h5">
|
||||
{{ $t('unregister_mail.text', { date: dateLastSend, mail: email }) }}
|
||||
</div>
|
||||
<div v-if="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 })
|
||||
}}
|
||||
|
||||
<!-- 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>
|
||||
<!-- 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -23,6 +28,9 @@ import { sendActivationEmail } from '../graphql/sendActivationEmail'
|
||||
export default {
|
||||
name: 'ConfirmRegisterMail',
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
},
|
||||
@ -40,10 +48,10 @@ export default {
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$toasted.success(this.$t('unregister_mail.success', { email: this.email }))
|
||||
this.toastSuccess(this.$t('unregister_mail.success', { email: this.email }))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(this.$t('unregister_mail.error', { message: error.message }))
|
||||
this.toastError(this.$t('unregister_mail.error', { message: error.message }))
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,15 +1,45 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<div>
|
||||
<hr />
|
||||
<br />
|
||||
<div class="text-center">
|
||||
{{ $t('gradido_admin_footer') }}
|
||||
<div><small>Version: 1.0.0</small></div>
|
||||
</div>
|
||||
<b-row align-v="center" class="mt-4 justify-content-lg-between">
|
||||
<b-col>
|
||||
<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"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('footer.copyright.link') }}
|
||||
</a>
|
||||
{{ $t('math.pipe') }}
|
||||
<a href="https://github.com/gradido/gradido/releases/latest" target="_blank">
|
||||
{{ $t('footer.app_version', { version }) }}
|
||||
</a>
|
||||
<a
|
||||
v-if="hash"
|
||||
:href="'https://github.com/gradido/gradido/commit/' + hash"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('footer.short_hash', { shortHash }) }}
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CONFIG from '../config'
|
||||
|
||||
export default {
|
||||
name: 'ContentFooter',
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear(),
|
||||
version: CONFIG.APP_VERSION,
|
||||
hash: CONFIG.BUILD_COMMIT,
|
||||
shortHash: CONFIG.BUILD_COMMIT_SHORT,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,39 +1,25 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import CreationFormular from './CreationFormular.vue'
|
||||
import { createPendingCreation } from '../graphql/createPendingCreation'
|
||||
import { createPendingCreations } from '../graphql/createPendingCreations'
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
verifyLogin: {
|
||||
name: 'success',
|
||||
id: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
const apolloMutateMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
createPendingCreation: [0, 0, 0],
|
||||
},
|
||||
})
|
||||
const stateCommitMock = jest.fn()
|
||||
const toastedErrorMock = jest.fn()
|
||||
const toastedSuccessMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$moment: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
subtract: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
}
|
||||
}),
|
||||
}
|
||||
$t: jest.fn((t, options) => (options ? [t, options] : t)),
|
||||
$d: jest.fn((d) => {
|
||||
const date = new Date(d)
|
||||
return date.toISOString().split('T')[0]
|
||||
}),
|
||||
$apollo: {
|
||||
query: apolloMock,
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
$store: {
|
||||
@ -45,16 +31,17 @@ const mocks = {
|
||||
},
|
||||
},
|
||||
},
|
||||
$toasted: {
|
||||
error: toastedErrorMock,
|
||||
success: toastedSuccessMock,
|
||||
},
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
type: '',
|
||||
creation: [],
|
||||
itemsMassCreation: {},
|
||||
}
|
||||
|
||||
const now = new Date(Date.now())
|
||||
const getCreationDate = (sub) => {
|
||||
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
describe('CreationFormular', () => {
|
||||
@ -73,21 +60,24 @@ describe('CreationFormular', () => {
|
||||
expect(wrapper.find('.component-creation-formular').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('server sends back moderator data', () => {
|
||||
it('called store commit with mocked data', () => {
|
||||
expect(stateCommitMock).toBeCalledWith('moderator', { name: 'success', id: 0 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('server throws error for moderator data call', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
apolloMock.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||
wrapper = Wrapper()
|
||||
describe('text and value form props', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper = mount(CreationFormular, {
|
||||
localVue,
|
||||
mocks,
|
||||
propsData: {
|
||||
creationUserData: { memo: 'Memo from property', amount: 42 },
|
||||
...propsData,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('has called store commit with fake data', () => {
|
||||
expect(stateCommitMock).toBeCalledWith('moderator', { id: 0, name: 'Test Moderator' })
|
||||
it('has text taken from props', () => {
|
||||
expect(wrapper.vm.text).toBe('Memo from property')
|
||||
})
|
||||
|
||||
it('has value taken from props', () => {
|
||||
expect(wrapper.vm.value).toBe(42)
|
||||
})
|
||||
})
|
||||
|
||||
@ -96,68 +86,22 @@ describe('CreationFormular', () => {
|
||||
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
|
||||
})
|
||||
|
||||
describe('with mass creation', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.setProps({ type: 'massCreation', creation: [200, 400, 600] })
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 90 })
|
||||
})
|
||||
|
||||
describe('first radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
|
||||
})
|
||||
|
||||
it('emits update-radio-selected with index 0', () => {
|
||||
expect(wrapper.emitted()['update-radio-selected']).toEqual([
|
||||
[expect.arrayContaining([0])],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('second radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
})
|
||||
|
||||
it('emits update-radio-selected with index 1', () => {
|
||||
expect(wrapper.emitted()['update-radio-selected']).toEqual([
|
||||
[expect.arrayContaining([1])],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('third radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
|
||||
})
|
||||
|
||||
it('emits update-radio-selected with index 2', () => {
|
||||
expect(wrapper.emitted()['update-radio-selected']).toEqual([
|
||||
[expect.arrayContaining([2])],
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with single creation', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.setProps({ type: 'singleCreation', creation: [200, 400, 600] })
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 90 })
|
||||
await wrapper.setProps({
|
||||
type: 'singleCreation',
|
||||
creation: [200, 400, 600],
|
||||
item: { email: 'benjamin@bluemchen.de' },
|
||||
})
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await wrapper.find('input[type="number"]').setValue(90)
|
||||
})
|
||||
|
||||
describe('first radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
|
||||
})
|
||||
|
||||
it('sets rangeMin to 0', () => {
|
||||
expect(wrapper.vm.rangeMin).toBe(0)
|
||||
await wrapper.find('textarea').setValue('Test create coins')
|
||||
})
|
||||
|
||||
it('sets rangeMax to 200', () => {
|
||||
@ -170,18 +114,50 @@ describe('CreationFormular', () => {
|
||||
})
|
||||
|
||||
it('sends ... to apollo', () => {
|
||||
expect(apolloMutateMock).toBeCalled()
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: createPendingCreation,
|
||||
variables: {
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creationDate: getCreationDate(2),
|
||||
amount: 90,
|
||||
memo: 'Test create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update-user-data', () => {
|
||||
expect(wrapper.emitted('update-user-data')).toEqual([
|
||||
[{ email: 'benjamin@bluemchen.de' }, [0, 0, 0]],
|
||||
])
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith([
|
||||
'creation_form.toasted',
|
||||
{ email: 'benjamin@bluemchen.de', value: '90' },
|
||||
])
|
||||
})
|
||||
|
||||
it('updates open creations in store', () => {
|
||||
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 1)
|
||||
})
|
||||
|
||||
it('resets the form data', () => {
|
||||
expect(wrapper.vm.value).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendForm', () => {
|
||||
describe('sendForm with server error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouch!')
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
|
||||
@ -311,7 +287,7 @@ describe('CreationFormular', () => {
|
||||
})
|
||||
|
||||
it('toast success message', () => {
|
||||
expect(toastedSuccessMock).toBeCalled()
|
||||
expect(toastSuccessSpy).toBeCalled()
|
||||
})
|
||||
|
||||
it('store commit openCreationPlus', () => {
|
||||
@ -359,6 +335,124 @@ describe('CreationFormular', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mass creation with success', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockResolvedValue({
|
||||
data: {
|
||||
createPendingCreations: {
|
||||
success: true,
|
||||
successfulCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'],
|
||||
failedCreation: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
await wrapper.setProps({
|
||||
type: 'massCreation',
|
||||
creation: [200, 400, 600],
|
||||
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
|
||||
})
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await wrapper.find('textarea').setValue('Test mass create coins')
|
||||
await wrapper.find('input[type="number"]').setValue(200)
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: createPendingCreations,
|
||||
variables: {
|
||||
pendingCreations: [
|
||||
{
|
||||
email: 'bob@baumeister.de',
|
||||
creationDate: getCreationDate(1),
|
||||
amount: 200,
|
||||
memo: 'Test mass create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
{
|
||||
email: 'bibi@bloxberg.de',
|
||||
creationDate: getCreationDate(1),
|
||||
amount: 200,
|
||||
memo: 'Test mass create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('updates open creations in store', () => {
|
||||
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 2)
|
||||
})
|
||||
|
||||
it('emits remove-all-bookmark', () => {
|
||||
expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('mass creation with success but all failed', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockResolvedValue({
|
||||
data: {
|
||||
createPendingCreations: {
|
||||
success: true,
|
||||
successfulCreation: [],
|
||||
failedCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'],
|
||||
},
|
||||
},
|
||||
})
|
||||
await wrapper.setProps({
|
||||
type: 'massCreation',
|
||||
creation: [200, 400, 600],
|
||||
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
|
||||
})
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await wrapper.find('textarea').setValue('Test mass create coins')
|
||||
await wrapper.find('input[type="number"]').setValue(200)
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('updates open creations in store', () => {
|
||||
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 0)
|
||||
})
|
||||
|
||||
it('emits remove all bookmarks', () => {
|
||||
expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('emits toast failed creations with two emails', () => {
|
||||
expect(wrapper.emitted('toast-failed-creations')).toEqual([
|
||||
[['bob@baumeister.de', 'bibi@bloxberg.de']],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('mass creation with error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockRejectedValue({
|
||||
message: 'Oh no!',
|
||||
})
|
||||
await wrapper.setProps({
|
||||
type: 'massCreation',
|
||||
creation: [200, 400, 600],
|
||||
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
|
||||
})
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await wrapper.find('textarea').setValue('Test mass create coins')
|
||||
await wrapper.find('input[type="number"]').setValue(200)
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh no!')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,53 +3,19 @@
|
||||
{{ $t('creation_form.form') }}
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<b-form ref="creationForm">
|
||||
<b-row class="m-4">
|
||||
<div class="ml-4">
|
||||
<label>{{ $t('creation_form.select_month') }}</label>
|
||||
<b-col class="text-left">
|
||||
<b-form-radio
|
||||
id="beforeLastMonth"
|
||||
v-model="radioSelected"
|
||||
:value="beforeLastMonth"
|
||||
:disabled="creation[0] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(beforeLastMonth, 0, creation[0])"
|
||||
>
|
||||
<label for="beforeLastMonth">
|
||||
{{ beforeLastMonth.short }} {{ creation[0] != null ? creation[0] + ' GDD' : '' }}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-radio
|
||||
id="lastMonth"
|
||||
v-model="radioSelected"
|
||||
:value="lastMonth"
|
||||
:disabled="creation[1] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(lastMonth, 1, creation[1])"
|
||||
>
|
||||
<label for="lastMonth">
|
||||
{{ lastMonth.short }} {{ creation[1] != null ? creation[1] + ' GDD' : '' }}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-form-radio
|
||||
id="currentMonth"
|
||||
v-model="radioSelected"
|
||||
:value="currentMonth"
|
||||
:disabled="creation[2] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(currentMonth, 2, creation[2])"
|
||||
>
|
||||
<label for="currentMonth">
|
||||
{{ currentMonth.short }} {{ creation[2] != null ? creation[2] + ' GDD' : '' }}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
</div>
|
||||
<b-row class="ml-4">
|
||||
<b-form-radio-group
|
||||
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="createdIndex != null">
|
||||
<b-row class="m-4" v-show="selected !== ''">
|
||||
<label>{{ $t('creation_form.select_value') }}</label>
|
||||
<div>
|
||||
<b-input-group prepend="GDD" append=".00">
|
||||
@ -60,7 +26,6 @@
|
||||
:max="rangeMax"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
|
||||
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
|
||||
<b-form-input
|
||||
type="range"
|
||||
@ -72,7 +37,7 @@
|
||||
</b-input-group>
|
||||
</div>
|
||||
</b-row>
|
||||
<b-row class="m-4">
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.enter_text') }}</label>
|
||||
<div>
|
||||
<b-form-textarea
|
||||
@ -83,9 +48,9 @@
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-center">
|
||||
<b-col class="text-left">
|
||||
<b-button type="reset" variant="danger" @click="$refs.creationForm.reset()">
|
||||
{{ $t('creation_form.reset') }}
|
||||
</b-button>
|
||||
@ -98,18 +63,17 @@
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
@click="submitCreation"
|
||||
:disabled="radioSelected === '' || value <= 0 || text.length < 10"
|
||||
: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="radioSelected === '' || value <= 0 || text.length < 10"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
>
|
||||
{{ $t('creation_form.submit_creation') }}
|
||||
</b-button>
|
||||
@ -121,11 +85,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { verifyLogin } from '../graphql/verifyLogin'
|
||||
import { createPendingCreation } from '../graphql/createPendingCreation'
|
||||
import { createPendingCreations } from '../graphql/createPendingCreations'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
export default {
|
||||
name: 'CreationFormular',
|
||||
mixins: [creationMonths],
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
@ -164,142 +129,97 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioSelected: '',
|
||||
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
|
||||
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
|
||||
rangeMin: 0,
|
||||
rangeMax: 1000,
|
||||
currentMonth: {
|
||||
short: this.$moment().format('MMMM'),
|
||||
long: this.$moment().format('YYYY-MM-DD'),
|
||||
year: this.$moment().format('YYYY'),
|
||||
},
|
||||
lastMonth: {
|
||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
|
||||
year: this.$moment().subtract(1, 'month').format('YYYY'),
|
||||
},
|
||||
beforeLastMonth: {
|
||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
|
||||
year: this.$moment().subtract(2, 'month').format('YYYY'),
|
||||
},
|
||||
submitObj: null,
|
||||
isdisabled: true,
|
||||
createdIndex: null,
|
||||
selected: '',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// Auswählen eines Zeitraumes
|
||||
updateRadioSelected(name, index, openCreation) {
|
||||
this.createdIndex = index
|
||||
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
|
||||
// Wenn Mehrfachschöpfung
|
||||
if (this.type === 'massCreation') {
|
||||
// An Creation.vue emitten und radioSelectedMass aktualisieren
|
||||
this.$emit('update-radio-selected', [name, index])
|
||||
} else if (this.type === 'singleCreation') {
|
||||
if (this.type === 'singleCreation') {
|
||||
this.rangeMin = 0
|
||||
// Der maximale offene Betrag an GDD die für ein User noch geschöpft werden kann
|
||||
this.rangeMax = openCreation
|
||||
this.rangeMax = name.creation
|
||||
}
|
||||
},
|
||||
submitCreation() {
|
||||
let submitObj = []
|
||||
if (this.type === 'massCreation') {
|
||||
// Die anzahl der Mitglieder aus der Mehrfachschöpfung
|
||||
const i = Object.keys(this.items).length
|
||||
// hinweis das eine Mehrfachschöpfung ausgeführt wird an (Anzahl der MItgleider an die geschöpft wird)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('SUBMIT CREATION => ' + this.type + ' >> für VIELE ' + i + ' Mitglieder')
|
||||
this.submitObj = []
|
||||
this.items.forEach((item) => {
|
||||
this.submitObj.push({
|
||||
submitObj.push({
|
||||
email: item.email,
|
||||
creationDate: this.radioSelected.long,
|
||||
creationDate: this.selected.date,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
moderator: Number(this.$store.state.moderator.id),
|
||||
})
|
||||
})
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('MehrfachSCHÖPFUNG ABSENDEN FÜR >> ' + i + ' Mitglieder')
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: createPendingCreations,
|
||||
variables: {
|
||||
pendingCreations: this.submitObj,
|
||||
pendingCreations: submitObj,
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
.then((result) => {
|
||||
const failedCreations = []
|
||||
this.$store.commit(
|
||||
'openCreationsPlus',
|
||||
result.data.createPendingCreations.successfulCreation.length,
|
||||
)
|
||||
if (result.data.createPendingCreations.failedCreation.length > 0) {
|
||||
result.data.createPendingCreations.failedCreation.forEach((failed) => {
|
||||
this.$toasted.error('Could not created PendingCreation for ' + failed)
|
||||
result.data.createPendingCreations.failedCreation.forEach((email) => {
|
||||
failedCreations.push(email)
|
||||
})
|
||||
}
|
||||
this.$emit('remove-all-bookmark')
|
||||
this.$emit('toast-failed-creations', failedCreations)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
} else if (this.type === 'singleCreation') {
|
||||
this.submitObj = {
|
||||
submitObj = {
|
||||
email: this.item.email,
|
||||
creationDate: this.radioSelected.long,
|
||||
creationDate: this.selected.date,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
moderator: Number(this.$store.state.moderator.id),
|
||||
}
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: createPendingCreation,
|
||||
variables: this.submitObj,
|
||||
variables: submitObj,
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('update-user-data', this.item, result.data.createPendingCreation)
|
||||
this.$toasted.success(
|
||||
this.$store.commit('openCreationsPlus', 1)
|
||||
this.toastSuccess(
|
||||
this.$t('creation_form.toasted', {
|
||||
value: this.value,
|
||||
email: this.item.email,
|
||||
}),
|
||||
)
|
||||
this.$store.commit('openCreationsPlus', 1)
|
||||
this.submitObj = null
|
||||
this.createdIndex = null
|
||||
// das creation Formular reseten
|
||||
// what is this? Tests says that this.text is not reseted
|
||||
this.$refs.creationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
this.value = 0
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.submitObj = null
|
||||
// das creation Formular reseten
|
||||
this.toastError(error.message)
|
||||
this.$refs.creationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
this.value = 0
|
||||
})
|
||||
}
|
||||
},
|
||||
searchModeratorData() {
|
||||
this.$apollo
|
||||
.query({ query: verifyLogin })
|
||||
.then((result) => {
|
||||
this.$store.commit('moderator', result.data.verifyLogin)
|
||||
})
|
||||
.catch(() => {
|
||||
this.$store.commit('moderator', { id: 0, name: 'Test Moderator' })
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.searchModeratorData()
|
||||
watch: {
|
||||
selected() {
|
||||
this.updateRadioSelected(this.selected)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,71 +1,49 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import CreationTransactionListFormular from './CreationTransactionListFormular.vue'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
transactionList: {
|
||||
transactions: [
|
||||
{
|
||||
type: 'created',
|
||||
balance: 100,
|
||||
decayStart: 0,
|
||||
decayEnd: 0,
|
||||
decayDuration: 0,
|
||||
memo: 'Testing',
|
||||
transactionId: 1,
|
||||
name: 'Bibi',
|
||||
email: 'bibi@bloxberg.de',
|
||||
date: new Date(),
|
||||
decay: {
|
||||
balance: 0.01,
|
||||
decayStart: 0,
|
||||
decayEnd: 0,
|
||||
decayDuration: 0,
|
||||
decayStartBlock: 0,
|
||||
},
|
||||
creationTransactionList: [
|
||||
{
|
||||
id: 1,
|
||||
amount: 100,
|
||||
balanceDate: 0,
|
||||
creationDate: new Date(),
|
||||
memo: 'Testing',
|
||||
linkedUser: {
|
||||
firstName: 'Gradido',
|
||||
lastName: 'Akademie',
|
||||
},
|
||||
{
|
||||
type: 'created',
|
||||
balance: 200,
|
||||
decayStart: 0,
|
||||
decayEnd: 0,
|
||||
decayDuration: 0,
|
||||
memo: 'Testing 2',
|
||||
transactionId: 2,
|
||||
name: 'Bibi',
|
||||
email: 'bibi@bloxberg.de',
|
||||
date: new Date(),
|
||||
decay: {
|
||||
balance: 0.01,
|
||||
decayStart: 0,
|
||||
decayEnd: 0,
|
||||
decayDuration: 0,
|
||||
decayStartBlock: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
amount: 200,
|
||||
balanceDate: 0,
|
||||
creationDate: new Date(),
|
||||
memo: 'Testing 2',
|
||||
linkedUser: {
|
||||
firstName: 'Gradido',
|
||||
lastName: 'Akademie',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const toastedErrorMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$d: jest.fn((t) => t),
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
$toasted: {
|
||||
global: {
|
||||
error: toastedErrorMock,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
userId: 1,
|
||||
fields: ['date', 'balance', 'name', 'memo', 'decay'],
|
||||
}
|
||||
|
||||
describe('CreationTransactionListFormular', () => {
|
||||
@ -87,7 +65,6 @@ describe('CreationTransactionListFormular', () => {
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
order: 'DESC',
|
||||
onlyCreations: true,
|
||||
userId: 1,
|
||||
},
|
||||
}),
|
||||
@ -109,7 +86,7 @@ describe('CreationTransactionListFormular', () => {
|
||||
})
|
||||
|
||||
it('toast error', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('OUCH!')
|
||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="component-creation-transaction-list">
|
||||
{{ $t('transactionlist.title') }}
|
||||
<b-table striped hover :items="items"></b-table>
|
||||
<b-table striped hover :fields="fields" :items="items"></b-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { transactionList } from '../graphql/transactionList'
|
||||
import { creationTransactionList } from '../graphql/creationTransactionList'
|
||||
export default {
|
||||
name: 'CreationTransactionList',
|
||||
props: {
|
||||
@ -13,6 +13,37 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{
|
||||
key: 'creationDate',
|
||||
label: this.$t('transactionlist.date'),
|
||||
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: 'linkedUser',
|
||||
label: this.$t('transactionlist.community'),
|
||||
formatter: (value, key, item) => {
|
||||
return `${value.firstName} ${value.lastName}`
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: this.$t('transactionlist.memo') },
|
||||
{
|
||||
key: 'balanceDate',
|
||||
label: this.$t('transactionlist.balanceDate'),
|
||||
formatter: (value, key, item) => {
|
||||
return this.$d(new Date(value))
|
||||
},
|
||||
},
|
||||
],
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
@ -20,20 +51,19 @@ export default {
|
||||
getTransactions() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: transactionList,
|
||||
query: creationTransactionList,
|
||||
variables: {
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
order: 'DESC',
|
||||
onlyCreations: true,
|
||||
userId: parseInt(this.userId),
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.items = result.data.transactionList.transactions
|
||||
this.items = result.data.creationTransactionList
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.global.error(error.message)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
248
admin/src/components/DeletedUserFormular.spec.js
Normal file
248
admin/src/components/DeletedUserFormular.spec.js
Normal file
@ -0,0 +1,248 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import DeletedUserFormular from './DeletedUserFormular.vue'
|
||||
import { deleteUser } from '../graphql/deleteUser'
|
||||
import { unDeleteUser } from '../graphql/unDeleteUser'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const date = new Date()
|
||||
|
||||
const apolloMutateMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
deleteUser: date,
|
||||
},
|
||||
})
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
moderator: {
|
||||
id: 0,
|
||||
name: 'test moderator',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
item: {},
|
||||
}
|
||||
|
||||
describe('DeletedUserFormular', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(DeletedUserFormular, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a DIV element with the class.delete-user-formular', () => {
|
||||
expect(wrapper.find('.deleted-user-formular').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete self', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({
|
||||
item: {
|
||||
userId: 0,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('shows a text that you cannot delete yourself', () => {
|
||||
expect(wrapper.text()).toBe('removeNotSelf')
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete other user', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({
|
||||
item: {
|
||||
userId: 1,
|
||||
deletedAt: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('has a checkbox', () => {
|
||||
expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('shows the text "delete_user"', () => {
|
||||
expect(wrapper.text()).toBe('delete_user')
|
||||
})
|
||||
|
||||
describe('click on checkbox', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('input[type="checkbox"]').setChecked()
|
||||
})
|
||||
|
||||
it('has a confirmation button', () => {
|
||||
expect(wrapper.find('button').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the button text "delete_user"', () => {
|
||||
expect(wrapper.find('button').text()).toBe('delete_user')
|
||||
})
|
||||
|
||||
describe('confirm delete with success', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: deleteUser,
|
||||
variables: {
|
||||
userId: 1,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update deleted At', () => {
|
||||
expect(wrapper.emitted('updateDeletedAt')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.arrayContaining([
|
||||
{
|
||||
userId: 1,
|
||||
deletedAt: date,
|
||||
},
|
||||
]),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('unchecks the checkbox', () => {
|
||||
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm delete with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
|
||||
await wrapper.find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh no!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('click on checkbox again', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('input[type="checkbox"]').setChecked(false)
|
||||
})
|
||||
|
||||
it('has no confirmation button anymore', () => {
|
||||
expect(wrapper.find('button').exists()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('recover user', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setProps({
|
||||
item: {
|
||||
userId: 1,
|
||||
deletedAt: date,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('has a checkbox', () => {
|
||||
expect(wrapper.find('input[type="checkbox"]').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('shows the text "undelete_user"', () => {
|
||||
expect(wrapper.text()).toBe('undelete_user')
|
||||
})
|
||||
|
||||
describe('click on checkbox', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockResolvedValue({
|
||||
data: {
|
||||
unDeleteUser: null,
|
||||
},
|
||||
})
|
||||
await wrapper.find('input[type="checkbox"]').setChecked()
|
||||
})
|
||||
|
||||
it('has a confirmation button', () => {
|
||||
expect(wrapper.find('button').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has the button text "undelete_user"', () => {
|
||||
expect(wrapper.find('button').text()).toBe('undelete_user')
|
||||
})
|
||||
|
||||
describe('confirm recover with success', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: unDeleteUser,
|
||||
variables: {
|
||||
userId: 1,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update deleted At', () => {
|
||||
expect(wrapper.emitted('updateDeletedAt')).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.arrayContaining([
|
||||
{
|
||||
userId: 1,
|
||||
deletedAt: null,
|
||||
},
|
||||
]),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('unchecks the checkbox', () => {
|
||||
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm recover with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
|
||||
await wrapper.find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh no!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('click on checkbox again', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('input[type="checkbox"]').setChecked(false)
|
||||
})
|
||||
|
||||
it('has no confirmation button anymore', () => {
|
||||
expect(wrapper.find('button').exists()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
85
admin/src/components/DeletedUserFormular.vue
Normal file
85
admin/src/components/DeletedUserFormular.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="deleted-user-formular">
|
||||
<div v-if="item.userId === $store.state.moderator.id" class="mt-5 mb-5">
|
||||
{{ $t('removeNotSelf') }}
|
||||
</div>
|
||||
<div v-else class="mt-5">
|
||||
<b-form-checkbox switch size="lg" v-model="checked">
|
||||
<div>{{ item.deletedAt ? $t('undelete_user') : $t('delete_user') }}</div>
|
||||
</b-form-checkbox>
|
||||
|
||||
<div class="mt-3 mb-5">
|
||||
<b-button v-if="checked && item.deletedAt === null" variant="danger" @click="deleteUser">
|
||||
{{ $t('delete_user') }}
|
||||
</b-button>
|
||||
<b-button v-if="checked && item.deletedAt !== null" variant="success" @click="unDeleteUser">
|
||||
{{ $t('undelete_user') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { deleteUser } from '../graphql/deleteUser'
|
||||
import { unDeleteUser } from '../graphql/unDeleteUser'
|
||||
|
||||
export default {
|
||||
name: 'DeletedUser',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
checked: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteUser() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: deleteUser,
|
||||
variables: {
|
||||
userId: this.item.userId,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('updateDeletedAt', {
|
||||
userId: this.item.userId,
|
||||
deletedAt: result.data.deleteUser,
|
||||
})
|
||||
this.checked = false
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
unDeleteUser() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: unDeleteUser,
|
||||
variables: {
|
||||
userId: this.item.userId,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.toastSuccess(this.$t('user_recovered'))
|
||||
this.$emit('updateDeletedAt', {
|
||||
userId: this.item.userId,
|
||||
deletedAt: result.data.unDeleteUser,
|
||||
})
|
||||
this.checked = false
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.input-group-text {
|
||||
background-color: rgb(255, 252, 205);
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import EditCreationFormular from './EditCreationFormular.vue'
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
@ -7,28 +8,21 @@ const apolloMutateMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
updatePendingCreation: {
|
||||
creation: [0, 0, 0],
|
||||
amount: 500,
|
||||
date: new Date(),
|
||||
memo: 'qwertzuiopasdfghjkl',
|
||||
memo: 'Test Schöpfung 2',
|
||||
moderator: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const stateCommitMock = jest.fn()
|
||||
const toastedErrorMock = jest.fn()
|
||||
const toastedSuccessMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$moment: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
subtract: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
}
|
||||
}),
|
||||
}
|
||||
$d: jest.fn((d) => {
|
||||
const date = new Date(d)
|
||||
return date.toISOString().split('T')[0]
|
||||
}),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
@ -42,10 +36,12 @@ const mocks = {
|
||||
},
|
||||
commit: stateCommitMock,
|
||||
},
|
||||
$toasted: {
|
||||
error: toastedErrorMock,
|
||||
success: toastedSuccessMock,
|
||||
},
|
||||
}
|
||||
|
||||
const now = new Date(Date.now())
|
||||
const getCreationDate = (sub) => {
|
||||
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
|
||||
return date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
@ -53,7 +49,7 @@ const propsData = {
|
||||
creationUserData: {
|
||||
memo: 'Test schöpfung 1',
|
||||
amount: 100,
|
||||
date: '2021-12-01',
|
||||
date: getCreationDate(0),
|
||||
},
|
||||
item: {
|
||||
id: 0,
|
||||
@ -82,196 +78,79 @@ describe('EditCreationFormular', () => {
|
||||
expect(wrapper.findAll('input[type="radio"]').length).toBe(3)
|
||||
})
|
||||
|
||||
describe('with single creation', () => {
|
||||
it('has the third radio button checked', () => {
|
||||
expect(wrapper.findAll('input[type="radio"]').at(0).element.checked).toBeFalsy()
|
||||
expect(wrapper.findAll('input[type="radio"]').at(1).element.checked).toBeFalsy()
|
||||
expect(wrapper.findAll('input[type="radio"]').at(2).element.checked).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has rangeMax of 700', () => {
|
||||
expect(wrapper.find('input[type="number"]').attributes('max')).toBe('700')
|
||||
})
|
||||
|
||||
describe('change and save memo and value with success', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.setProps({ creation: [200, 400, 600] })
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 90 })
|
||||
await wrapper.find('input[type="number"]').setValue(500)
|
||||
await wrapper.find('textarea').setValue('Test Schöpfung 2')
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
describe('first radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
|
||||
})
|
||||
|
||||
it('sets rangeMin to 0', () => {
|
||||
expect(wrapper.vm.rangeMin).toBe(0)
|
||||
})
|
||||
|
||||
it('sets rangeMax to 200', () => {
|
||||
expect(wrapper.vm.rangeMax).toBe(200)
|
||||
})
|
||||
|
||||
describe('sendForm', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('sends ... to apollo', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
amount: 90,
|
||||
creationDate: 'YYYY-MM-01',
|
||||
email: 'bob@baumeister.de',
|
||||
id: 0,
|
||||
memo: 'Test create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('emits update-user-data', () => {
|
||||
expect(wrapper.emitted('update-user-data')).toBeTruthy()
|
||||
expect(wrapper.emitted('update-user-data')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 0,
|
||||
email: 'bob@baumeister.de',
|
||||
},
|
||||
[0, 0, 0],
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('toast success message', () => {
|
||||
expect(toastedSuccessMock).toBeCalledWith('creation_form.toasted_update')
|
||||
})
|
||||
|
||||
describe('sendForm with error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockRejectedValue({
|
||||
message: 'Ouch!',
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
await wrapper.setProps({ type: 'singleCreation', creation: [200, 400, 600] })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 90 })
|
||||
await wrapper.findAll('input[type="radio"]').at(0).setChecked()
|
||||
await wrapper.setData({ rangeMin: 100 })
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('toast error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
})
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
id: 0,
|
||||
email: 'bob@baumeister.de',
|
||||
creationDate: getCreationDate(0),
|
||||
amount: 500,
|
||||
memo: 'Test Schöpfung 2',
|
||||
moderator: 0,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('second radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
})
|
||||
|
||||
it('sets rangeMin to 0', () => {
|
||||
expect(wrapper.vm.rangeMin).toBe(0)
|
||||
})
|
||||
|
||||
it('sets rangeMax to 400', () => {
|
||||
expect(wrapper.vm.rangeMax).toBe(400)
|
||||
})
|
||||
|
||||
describe('sendForm', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('sends ... to apollo', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
amount: 90,
|
||||
creationDate: 'YYYY-MM-01',
|
||||
email: 'bob@baumeister.de',
|
||||
id: 0,
|
||||
memo: 'Test create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('sendForm with error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockRejectedValue({
|
||||
message: 'Ouch!',
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
await wrapper.setProps({ creation: [200, 400, 600] })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 100 })
|
||||
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('toast error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
})
|
||||
it('emits update-user-data', () => {
|
||||
expect(wrapper.emitted('update-user-data')).toEqual([
|
||||
[
|
||||
{
|
||||
id: 0,
|
||||
email: 'bob@baumeister.de',
|
||||
},
|
||||
[0, 0, 0],
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
describe('third radio button', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
|
||||
})
|
||||
it('emits update-creation-data', () => {
|
||||
expect(wrapper.emitted('update-creation-data')).toEqual([
|
||||
[
|
||||
{
|
||||
amount: 500,
|
||||
date: expect.any(Date),
|
||||
memo: 'Test Schöpfung 2',
|
||||
moderator: 0,
|
||||
row: expect.any(Object),
|
||||
},
|
||||
],
|
||||
])
|
||||
})
|
||||
|
||||
it('sets rangeMin to 180', () => {
|
||||
expect(wrapper.vm.rangeMin).toBe(180)
|
||||
})
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_update')
|
||||
})
|
||||
})
|
||||
|
||||
it('sets rangeMax to 700', () => {
|
||||
expect(wrapper.vm.rangeMax).toBe(700)
|
||||
})
|
||||
describe('change and save memo and value with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
|
||||
await wrapper.find('input[type="number"]').setValue(500)
|
||||
await wrapper.find('textarea').setValue('Test Schöpfung 2')
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
describe('sendForm with success', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('sends ... to apollo', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
amount: 90,
|
||||
creationDate: 'YYYY-MM-DD',
|
||||
email: 'bob@baumeister.de',
|
||||
id: 0,
|
||||
memo: 'Test create coins',
|
||||
moderator: 0,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('sendForm with error', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
apolloMutateMock.mockRejectedValue({
|
||||
message: 'Ouch!',
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
await wrapper.setProps({ creation: [200, 400, 600] })
|
||||
await wrapper.setData({ text: 'Test create coins' })
|
||||
await wrapper.setData({ value: 90 })
|
||||
await wrapper.findAll('input[type="radio"]').at(2).setChecked()
|
||||
await wrapper.setData({ rangeMin: 180 })
|
||||
await wrapper.find('.test-submit').trigger('click')
|
||||
})
|
||||
|
||||
it('toast error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh no!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,68 +2,19 @@
|
||||
<div class="component-edit-creation-formular">
|
||||
<div class="shadow p-3 mb-5 bg-white rounded">
|
||||
<b-form ref="updateCreationForm">
|
||||
<b-row class="m-4">
|
||||
<div class="ml-4">
|
||||
<label>{{ $t('creation_form.select_month') }}</label>
|
||||
<b-col class="text-left">
|
||||
<b-form-radio
|
||||
id="beforeLastMonth"
|
||||
v-model="radioSelected"
|
||||
:value="beforeLastMonth"
|
||||
:disabled="selectedOpenCreationAmount[0] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(beforeLastMonth, 0, selectedOpenCreationAmount[0])"
|
||||
>
|
||||
<label for="beforeLastMonth">
|
||||
{{ beforeLastMonth.short }}
|
||||
{{
|
||||
selectedOpenCreationAmount[0] != null
|
||||
? selectedOpenCreationAmount[0] + ' GDD'
|
||||
: ''
|
||||
}}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<b-form-radio
|
||||
id="lastMonth"
|
||||
v-model="radioSelected"
|
||||
:value="lastMonth"
|
||||
:disabled="selectedOpenCreationAmount[1] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(lastMonth, 1, selectedOpenCreationAmount[1])"
|
||||
>
|
||||
<label for="lastMonth">
|
||||
{{ lastMonth.short }}
|
||||
{{
|
||||
selectedOpenCreationAmount[1] != null
|
||||
? selectedOpenCreationAmount[1] + ' GDD'
|
||||
: ''
|
||||
}}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-form-radio
|
||||
id="currentMonth"
|
||||
v-model="radioSelected"
|
||||
:value="currentMonth"
|
||||
:disabled="selectedOpenCreationAmount[2] === 0"
|
||||
size="lg"
|
||||
@change="updateRadioSelected(currentMonth, 2, selectedOpenCreationAmount[2])"
|
||||
>
|
||||
<label for="currentMonth">
|
||||
{{ currentMonth.short }}
|
||||
{{
|
||||
selectedOpenCreationAmount[2] != null
|
||||
? selectedOpenCreationAmount[2] + ' GDD'
|
||||
: ''
|
||||
}}
|
||||
</label>
|
||||
</b-form-radio>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-form-radio-group
|
||||
v-model="selected"
|
||||
:options="radioOptions"
|
||||
value-field="item"
|
||||
text-field="name"
|
||||
name="month-selection"
|
||||
></b-form-radio-group>
|
||||
</b-row>
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.select_value') }}</label>
|
||||
<div>
|
||||
<b-input-group prepend="GDD" append=".00">
|
||||
@ -74,7 +25,6 @@
|
||||
:max="rangeMax"
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
|
||||
<b-input-group prepend="0" :append="String(rangeMax)" class="mt-3">
|
||||
<b-form-input
|
||||
type="range"
|
||||
@ -85,8 +35,8 @@
|
||||
></b-form-input>
|
||||
</b-input-group>
|
||||
</div>
|
||||
</b-row>
|
||||
<b-row class="m-4">
|
||||
</div>
|
||||
<div class="m-4">
|
||||
<label>{{ $t('creation_form.enter_text') }}</label>
|
||||
<div>
|
||||
<b-form-textarea
|
||||
@ -97,9 +47,9 @@
|
||||
rows="3"
|
||||
></b-form-textarea>
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
<b-row class="m-4">
|
||||
<b-col class="text-center">
|
||||
<b-col class="text-left">
|
||||
<b-button type="reset" variant="danger" @click="$refs.updateCreationForm.reset()">
|
||||
{{ $t('creation_form.reset') }}
|
||||
</b-button>
|
||||
@ -111,7 +61,7 @@
|
||||
variant="success"
|
||||
class="test-submit"
|
||||
@click="submitCreation"
|
||||
:disabled="radioSelected === '' || value <= 0 || text.length < 10"
|
||||
:disabled="selected === '' || value <= 0 || text.length < 10"
|
||||
>
|
||||
{{ $t('creation_form.update_creation') }}
|
||||
</b-button>
|
||||
@ -124,8 +74,11 @@
|
||||
</template>
|
||||
<script>
|
||||
import { updatePendingCreation } from '../graphql/updatePendingCreation'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
|
||||
export default {
|
||||
name: 'EditCreationFormular',
|
||||
mixins: [creationMonths],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
@ -149,51 +102,26 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
radioSelected: '',
|
||||
text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
|
||||
value: !this.creationUserData.amount ? 0 : this.creationUserData.amount,
|
||||
rangeMin: 0,
|
||||
rangeMax: 1000,
|
||||
currentMonth: {
|
||||
short: this.$moment().format('MMMM'),
|
||||
long: this.$moment().format('YYYY-MM-DD'),
|
||||
},
|
||||
lastMonth: {
|
||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(1, 'month').format('YYYY-MM') + '-01',
|
||||
},
|
||||
beforeLastMonth: {
|
||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||
long: this.$moment().subtract(2, 'month').format('YYYY-MM') + '-01',
|
||||
},
|
||||
submitObj: null,
|
||||
isdisabled: true,
|
||||
createdIndex: null,
|
||||
selectedOpenCreationAmount: {},
|
||||
selected: '',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateRadioSelected(name, index, openCreation) {
|
||||
this.createdIndex = index
|
||||
this.rangeMin = 0
|
||||
this.rangeMax = this.creation[index]
|
||||
},
|
||||
submitCreation() {
|
||||
this.submitObj = {
|
||||
id: this.item.id,
|
||||
email: this.item.email,
|
||||
creationDate: this.radioSelected.long,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
moderator: Number(this.$store.state.moderator.id),
|
||||
}
|
||||
|
||||
// hinweis das eine ein einzelne Schöpfung abgesendet wird an (email)
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: updatePendingCreation,
|
||||
variables: this.submitObj,
|
||||
variables: {
|
||||
id: this.item.id,
|
||||
email: this.item.email,
|
||||
creationDate: this.selected.date,
|
||||
amount: Number(this.value),
|
||||
memo: this.text,
|
||||
moderator: Number(this.$store.state.moderator.id),
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.$emit('update-user-data', this.item, result.data.updatePendingCreation.creation)
|
||||
@ -204,22 +132,19 @@ export default {
|
||||
moderator: Number(result.data.updatePendingCreation.moderator),
|
||||
row: this.row,
|
||||
})
|
||||
this.$toasted.success(
|
||||
this.toastSuccess(
|
||||
this.$t('creation_form.toasted_update', {
|
||||
value: this.value,
|
||||
email: this.item.email,
|
||||
}),
|
||||
)
|
||||
this.submitObj = null
|
||||
this.createdIndex = null
|
||||
// das creation Formular reseten
|
||||
this.$refs.updateCreationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
this.value = 0
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.submitObj = null
|
||||
this.toastError(error.message)
|
||||
// das creation Formular reseten
|
||||
this.$refs.updateCreationForm.reset()
|
||||
// Den geschöpften Wert auf o setzen
|
||||
@ -229,25 +154,10 @@ export default {
|
||||
},
|
||||
created() {
|
||||
if (this.creationUserData.date) {
|
||||
switch (this.$moment(this.creationUserData.date).format('MMMM')) {
|
||||
case this.currentMonth.short:
|
||||
this.createdIndex = 2
|
||||
this.radioSelected = this.currentMonth
|
||||
break
|
||||
case this.lastMonth.short:
|
||||
this.createdIndex = 1
|
||||
this.radioSelected = this.lastMonth
|
||||
break
|
||||
case this.beforeLastMonth.short:
|
||||
this.createdIndex = 0
|
||||
this.radioSelected = this.beforeLastMonth
|
||||
break
|
||||
default:
|
||||
throw new Error('Something went wrong')
|
||||
}
|
||||
this.selectedOpenCreationAmount[this.createdIndex] =
|
||||
this.creation[this.createdIndex] + this.creationUserData.amount
|
||||
this.rangeMax = this.selectedOpenCreationAmount[this.createdIndex]
|
||||
const month = this.$d(new Date(this.creationUserData.date), 'month')
|
||||
const index = this.radioOptions.findIndex((obj) => obj.item.short === month)
|
||||
this.selected = this.radioOptions[index].item
|
||||
this.rangeMax = this.creation[index] + this.creationUserData.amount
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -53,13 +53,17 @@ describe('NavBar', () => {
|
||||
})
|
||||
|
||||
describe('logout', () => {
|
||||
// const assignLocationSpy = jest.fn()
|
||||
const windowLocationMock = jest.fn()
|
||||
beforeEach(async () => {
|
||||
delete window.location
|
||||
window.location = {
|
||||
assign: windowLocationMock,
|
||||
}
|
||||
await wrapper.findAll('a').at(6).trigger('click')
|
||||
})
|
||||
|
||||
it('redirects to /logout', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/logout')
|
||||
expect(windowLocationMock).toBeCalledWith('http://localhost/login')
|
||||
})
|
||||
|
||||
it('dispatches logout to store', () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<div class="component-nabvar">
|
||||
<b-navbar toggleable="md" type="dark" variant="success" class="p-3">
|
||||
<b-navbar-brand to="/">
|
||||
<img src="img/brand/green.png" class="navbar-brand-img" alt="..." />
|
||||
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img" alt="..." />
|
||||
</b-navbar-brand>
|
||||
|
||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||
@ -19,7 +19,7 @@
|
||||
>
|
||||
{{ $store.state.openCreations }} {{ $t('navbar.open_creation') }}
|
||||
</b-nav-item>
|
||||
<b-nav-item @click="wallet">{{ $t('navbar.wallet') }}</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>
|
||||
@ -33,11 +33,12 @@ export default {
|
||||
name: 'navbar',
|
||||
methods: {
|
||||
logout() {
|
||||
window.location.assign(CONFIG.WALLET_URL)
|
||||
// window.location = CONFIG.WALLET_URL
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.push('/logout')
|
||||
},
|
||||
wallet() {
|
||||
window.location = CONFIG.WALLET_AUTH_URL.replace('$1', this.$store.state.token)
|
||||
window.location = CONFIG.WALLET_AUTH_URL.replace('{token}', this.$store.state.token)
|
||||
this.$store.dispatch('logout') // logout without redirect
|
||||
},
|
||||
},
|
||||
|
||||
@ -3,11 +3,15 @@ import NotFoundPage from './NotFoundPage'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
}
|
||||
|
||||
describe('NotFoundPage', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(NotFoundPage, { localVue })
|
||||
return mount(NotFoundPage, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
@ -18,5 +22,9 @@ describe('NotFoundPage', () => {
|
||||
it('has a svg', () => {
|
||||
expect(wrapper.find('svg').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has a back button', () => {
|
||||
expect(wrapper.find('.test-back').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="header py-1 py-lg-1 pt-lg-3">
|
||||
<b-container>
|
||||
<div class="header-body text-center mb-3">
|
||||
<a href="login" to="login">
|
||||
<a href="#!" @click="goback">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 mt-5 mb-5">
|
||||
@ -1185,6 +1185,11 @@
|
||||
</div>
|
||||
</b-container>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<b-button class="test-back" variant="light" @click="goback">
|
||||
{{ $t('back') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1213,6 +1218,11 @@ export default {
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goback() {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
31
admin/src/components/Overlay.spec.js
Normal file
31
admin/src/components/Overlay.spec.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Overlay from './Overlay.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const propsData = {
|
||||
item: {},
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => String(d)),
|
||||
}
|
||||
|
||||
describe('Overlay', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(Overlay, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a DIV element with the class.component-overlay', () => {
|
||||
expect(wrapper.find('.component-overlay').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
67
admin/src/components/Overlay.vue
Normal file
67
admin/src/components/Overlay.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="component-overlay">
|
||||
<b-jumbotron class="bg-light p-4">
|
||||
<template #header>{{ $t('overlay.confirm.title') }}</template>
|
||||
|
||||
<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.date), 'month') }} {{ $d(new Date(item.date), '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>
|
||||
|
||||
<hr class="my-4" />
|
||||
<p>{{ $t('overlay.confirm.text') }}</p>
|
||||
<p>
|
||||
{{ $t('overlay.confirm.question') }}
|
||||
</p>
|
||||
<b-container>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<b-button size="md" variant="danger" class="m-3" @click="$emit('overlay-cancel')">
|
||||
{{ $t('overlay.confirm.cancel') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
<b-col class="text-right">
|
||||
<b-button
|
||||
size="md"
|
||||
variant="success"
|
||||
class="m-3 text-right"
|
||||
@click="$emit('confirm-creation', item)"
|
||||
>
|
||||
{{ $t('overlay.confirm.yes') }}
|
||||
</b-button>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-container>
|
||||
</b-jumbotron>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'overlay',
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,10 +1,7 @@
|
||||
<template>
|
||||
<b-card class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||
<b-row class="mb-2">
|
||||
<b-col></b-col>
|
||||
</b-row>
|
||||
<slot :name="slotName" />
|
||||
<b-button size="sm" @click="$emit('row-toogle-details', row, index)">
|
||||
<b-button size="sm" @click="$emit('row-toggle-details', row, index)">
|
||||
<b-icon
|
||||
:icon="type === 'PageCreationConfirm' ? 'x' : 'eye-slash-fill'"
|
||||
aria-label="Help"
|
||||
|
||||
86
admin/src/components/Tables/OpenCreationsTable.vue
Normal file
86
admin/src/components/Tables/OpenCreationsTable.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="component-open-creations-table">
|
||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
||||
<template #cell(bookmark)="row">
|
||||
<b-button
|
||||
variant="danger"
|
||||
size="md"
|
||||
@click="$emit('remove-creation', row.item)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon icon="x" variant="light"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
<template #cell(edit_creation)="row">
|
||||
<b-button variant="info" size="md" @click="rowToggleDetails(row, 0)" class="mr-2">
|
||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
<template #cell(confirm)="row">
|
||||
<b-button variant="success" size="md" @click="$emit('show-overlay', row.item)" class="mr-2">
|
||||
<b-icon icon="check" scale="2" variant=""></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
<template #row-details="row">
|
||||
<row-details
|
||||
:row="row"
|
||||
type="show-creation"
|
||||
slotName="show-creation"
|
||||
:index="0"
|
||||
@row-toggle-details="rowToggleDetails"
|
||||
>
|
||||
<template #show-creation>
|
||||
<div>
|
||||
<edit-creation-formular
|
||||
type="singleCreation"
|
||||
:creation="row.item.creation"
|
||||
:item="row.item"
|
||||
:row="row"
|
||||
:creationUserData="creationUserData"
|
||||
@update-creation-data="updateCreationData"
|
||||
@update-user-data="updateUserData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</row-details>
|
||||
</template>
|
||||
</b-table-lite>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { toggleRowDetails } from '../../mixins/toggleRowDetails'
|
||||
import RowDetails from '../RowDetails.vue'
|
||||
import EditCreationFormular from '../EditCreationFormular.vue'
|
||||
|
||||
export default {
|
||||
name: 'OpenCreationsTable',
|
||||
mixins: [toggleRowDetails],
|
||||
components: {
|
||||
EditCreationFormular,
|
||||
RowDetails,
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateCreationData(data) {
|
||||
this.creationUserData.amount = data.amount
|
||||
this.creationUserData.date = data.date
|
||||
this.creationUserData.memo = data.memo
|
||||
this.creationUserData.moderator = data.moderator
|
||||
data.row.toggleDetails()
|
||||
},
|
||||
updateUserData(rowItem, newCreation) {
|
||||
rowItem.creation = newCreation
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
125
admin/src/components/Tables/SearchUserTable.spec.js
Normal file
125
admin/src/components/Tables/SearchUserTable.spec.js
Normal file
@ -0,0 +1,125 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import SearchUserTable from './SearchUserTable.vue'
|
||||
|
||||
const date = new Date()
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloMutateMock = jest.fn().mockResolvedValue({})
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({})
|
||||
|
||||
const propsData = {
|
||||
items: [
|
||||
{
|
||||
userId: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
emailChecked: true,
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [1000, 1000, 1000],
|
||||
emailChecked: true,
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
email: 'peter@lustig.de',
|
||||
creation: [0, 0, 0],
|
||||
emailChecked: true,
|
||||
},
|
||||
{
|
||||
userId: 4,
|
||||
firstName: 'New',
|
||||
lastName: 'User',
|
||||
email: 'new@user.ch',
|
||||
creation: [1000, 1000, 1000],
|
||||
emailChecked: false,
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{ key: 'email', label: 'e_mail' },
|
||||
{ key: 'firstName', label: 'firstname' },
|
||||
{ key: 'lastName', label: 'lastname' },
|
||||
{
|
||||
key: 'creation',
|
||||
label: 'creationLabel',
|
||||
formatter: (value, key, item) => {
|
||||
return value.join(' | ')
|
||||
},
|
||||
},
|
||||
{ key: 'status', label: 'status' },
|
||||
],
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => d),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
moderator: {
|
||||
id: 0,
|
||||
name: 'test moderator',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
describe('SearchUserTable', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(SearchUserTable, { localVue, mocks, propsData })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a table with four rows', () => {
|
||||
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
|
||||
})
|
||||
|
||||
describe('show row details', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('tbody > tr').at(1).trigger('click')
|
||||
})
|
||||
|
||||
describe('deleted at', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('div.deleted-user-formular').vm.$emit('updateDeletedAt', {
|
||||
userId: 1,
|
||||
deletedAt: date,
|
||||
})
|
||||
})
|
||||
|
||||
it('emits updateDeletedAt', () => {
|
||||
expect(wrapper.emitted('updateDeletedAt')).toEqual([[1, date]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('updateUserData', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.find('div.component-creation-formular')
|
||||
.vm.$emit('update-user-data', propsData.items[1], [250, 500, 750])
|
||||
})
|
||||
|
||||
it('updates the item', () => {
|
||||
expect(wrapper.vm.items[1].creation).toEqual([250, 500, 750])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
141
admin/src/components/Tables/SearchUserTable.vue
Normal file
141
admin/src/components/Tables/SearchUserTable.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="search-user-table">
|
||||
<b-table
|
||||
tbody-tr-class="pointer"
|
||||
:items="myItems"
|
||||
:fields="fields"
|
||||
caption-top
|
||||
striped
|
||||
hover
|
||||
stacked="md"
|
||||
select-mode="single"
|
||||
selectableonRowSelected
|
||||
@row-clicked="onRowClicked"
|
||||
>
|
||||
<template #cell(creation)="data">
|
||||
<div v-html="data.value"></div>
|
||||
</template>
|
||||
|
||||
<template #cell(status)="row">
|
||||
<div class="text-right">
|
||||
<b-avatar v-if="row.item.deletedAt" class="mr-3" 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
|
||||
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>
|
||||
</span>
|
||||
<b-icon
|
||||
variant="dark"
|
||||
:icon="row.detailsShowing ? 'caret-up-fill' : 'caret-down'"
|
||||
:title="row.item.enabled ? $t('enabled') : $t('deleted')"
|
||||
></b-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #row-details="row">
|
||||
<b-card ref="rowDetails" class="shadow-lg pl-3 pr-3 mb-5 bg-white rounded">
|
||||
<creation-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
type="singleCreation"
|
||||
pagetype="singleCreation"
|
||||
:creation="row.item.creation"
|
||||
:item="row.item"
|
||||
:creationUserData="creationUserData"
|
||||
@update-user-data="updateUserData"
|
||||
/>
|
||||
<div v-else>{{ $t('userIsDeleted') }}</div>
|
||||
<confirm-register-mail-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
:checked="row.item.emailChecked"
|
||||
:email="row.item.email"
|
||||
:dateLastSend="
|
||||
row.item.emailConfirmationSend
|
||||
? $d(new Date(row.item.emailConfirmationSend), 'long')
|
||||
: ''
|
||||
"
|
||||
/>
|
||||
<creation-transaction-list-formular
|
||||
v-if="!row.item.deletedAt"
|
||||
:userId="row.item.userId"
|
||||
/>
|
||||
<deleted-user-formular :item="row.item" @updateDeletedAt="updateDeletedAt" />
|
||||
</b-card>
|
||||
</template>
|
||||
</b-table>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import CreationFormular from '../CreationFormular.vue'
|
||||
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue'
|
||||
import CreationTransactionListFormular from '../CreationTransactionListFormular.vue'
|
||||
import DeletedUserFormular from '../DeletedUserFormular.vue'
|
||||
|
||||
export default {
|
||||
name: 'SearchUserTable',
|
||||
components: {
|
||||
CreationFormular,
|
||||
ConfirmRegisterMailFormular,
|
||||
CreationTransactionListFormular,
|
||||
DeletedUserFormular,
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
creationUserData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateUserData(rowItem, newCreation) {
|
||||
rowItem.creation = newCreation
|
||||
},
|
||||
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 }
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
35
admin/src/components/Tables/SelectUsersTable.vue
Normal file
35
admin/src/components/Tables/SelectUsersTable.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div class="component-select-users-table">
|
||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
||||
<template #cell(bookmark)="row">
|
||||
<div>
|
||||
<b-button
|
||||
v-if="row.item.emailChecked"
|
||||
variant="warning"
|
||||
size="md"
|
||||
@click="$emit('push-item', row.item)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon icon="plus" variant="success"></b-icon>
|
||||
</b-button>
|
||||
<div v-else>{{ $t('e_mail') }}{{ $t('math.exclaim') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</b-table-lite>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'SelectUsersTable',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
26
admin/src/components/Tables/SelectedUsersTable.vue
Normal file
26
admin/src/components/Tables/SelectedUsersTable.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="component-selected-users-table">
|
||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
||||
<template #cell(bookmark)="row">
|
||||
<b-button variant="danger" size="md" @click="$emit('remove-item', row.item)" class="mr-2">
|
||||
<b-icon icon="x" variant="light"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
</b-table-lite>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'SelectedUsersTable',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,33 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import UserTable from './UserTable.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('UserTable', () => {
|
||||
let wrapper
|
||||
|
||||
const propsData = {
|
||||
type: 'Type',
|
||||
itemsUser: [],
|
||||
fieldsTable: [],
|
||||
creation: [],
|
||||
}
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(UserTable, { localVue, propsData, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a DIV element with the class.component-user-table', () => {
|
||||
expect(wrapper.find('.component-user-table').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,339 +0,0 @@
|
||||
<template>
|
||||
<div class="component-user-table">
|
||||
<div v-show="overlay" id="overlay" class="">
|
||||
<b-jumbotron class="bg-light p-4">
|
||||
<template #header>{{ overlayText.header }}</template>
|
||||
|
||||
<template #lead>
|
||||
{{ overlayText.text1 }}
|
||||
</template>
|
||||
|
||||
<hr class="my-4" />
|
||||
|
||||
<p>
|
||||
{{ overlayText.text2 }}
|
||||
</p>
|
||||
|
||||
<b-button size="md" variant="danger" class="m-3" @click="overlayCancel">
|
||||
{{ overlayText.button_cancel }}
|
||||
</b-button>
|
||||
<b-button
|
||||
size="md"
|
||||
variant="success"
|
||||
class="m-3 text-right"
|
||||
@click="overlayOK(overlayBookmarkType, overlayItem)"
|
||||
>
|
||||
{{ overlayText.button_ok }}
|
||||
</b-button>
|
||||
</b-jumbotron>
|
||||
</div>
|
||||
<b-table-lite
|
||||
:items="itemsUser"
|
||||
:fields="fieldsTable"
|
||||
:filter="criteria"
|
||||
caption-top
|
||||
striped
|
||||
hover
|
||||
stacked="md"
|
||||
>
|
||||
<template #cell(creation)="data">
|
||||
<div v-html="data.value"></div>
|
||||
</template>
|
||||
|
||||
<template #cell(edit_creation)="row">
|
||||
<b-button variant="info" size="md" @click="rowToogleDetails(row, 0)" class="mr-2">
|
||||
<b-icon :icon="row.detailsShowing ? 'x' : 'pencil-square'" aria-label="Help"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<template #cell(show_details)="row">
|
||||
<b-button variant="info" size="md" @click="rowToogleDetails(row, 0)" class="mr-2">
|
||||
<b-icon :icon="row.detailsShowing ? 'eye-slash-fill' : 'eye-fill'"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<template #cell(confirm_mail)="row">
|
||||
<b-button
|
||||
:variant="row.item.emailChecked ? 'success' : 'danger'"
|
||||
size="md"
|
||||
@click="rowToogleDetails(row, 1)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon
|
||||
:icon="row.item.emailChecked ? 'envelope-open' : 'envelope'"
|
||||
aria-label="Help"
|
||||
></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<template #cell(transactions_list)="row">
|
||||
<b-button variant="warning" size="md" @click="rowToogleDetails(row, 2)" class="mr-2">
|
||||
<b-icon icon="list"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<template #row-details="row">
|
||||
<row-details
|
||||
:row="row"
|
||||
:type="type"
|
||||
:slotName="slotName"
|
||||
:index="slotIndex"
|
||||
@row-toogle-details="rowToogleDetails"
|
||||
>
|
||||
<template #show-creation>
|
||||
<div>
|
||||
<creation-formular
|
||||
v-if="type === 'PageUserSearch'"
|
||||
type="singleCreation"
|
||||
:pagetype="type"
|
||||
:creation="row.item.creation"
|
||||
:item="row.item"
|
||||
:creationUserData="creationUserData"
|
||||
@update-creation-data="updateCreationData"
|
||||
@update-user-data="updateUserData"
|
||||
/>
|
||||
<edit-creation-formular
|
||||
v-else
|
||||
type="singleCreation"
|
||||
:pagetype="type"
|
||||
:creation="row.item.creation"
|
||||
:item="row.item"
|
||||
:row="row"
|
||||
:creationUserData="creationUserData"
|
||||
@update-creation-data="updateCreationData"
|
||||
@update-user-data="updateUserData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #show-register-mail>
|
||||
<confirm-register-mail-formular
|
||||
:email="row.item.email"
|
||||
:dateLastSend="$moment().subtract(1, 'month').format('dddd, DD.MMMM.YYYY HH:mm'),"
|
||||
/>
|
||||
</template>
|
||||
<template #show-transaction-list>
|
||||
<creation-transaction-list-formular :userId="row.item.userId" />
|
||||
</template>
|
||||
</row-details>
|
||||
</template>
|
||||
<template #cell(bookmark)="row">
|
||||
<b-button
|
||||
variant="warning"
|
||||
v-show="type === 'UserListSearch'"
|
||||
size="md"
|
||||
@click="bookmarkPush(row.item)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon icon="plus" variant="success"></b-icon>
|
||||
</b-button>
|
||||
<b-button
|
||||
variant="danger"
|
||||
v-show="type === 'UserListMassCreation' || type === 'PageCreationConfirm'"
|
||||
size="md"
|
||||
@click="overlayShow('remove', row.item)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon icon="x" variant="light"></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<template #cell(confirm)="row">
|
||||
<b-button
|
||||
variant="success"
|
||||
v-show="type === 'PageCreationConfirm'"
|
||||
size="md"
|
||||
@click="overlayShow('confirm', row.item)"
|
||||
class="mr-2"
|
||||
>
|
||||
<b-icon icon="check" scale="2" variant=""></b-icon>
|
||||
</b-button>
|
||||
</template>
|
||||
</b-table-lite>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CreationFormular from '../components/CreationFormular.vue'
|
||||
import EditCreationFormular from '../components/EditCreationFormular.vue'
|
||||
import ConfirmRegisterMailFormular from '../components/ConfirmRegisterMailFormular.vue'
|
||||
import CreationTransactionListFormular from '../components/CreationTransactionListFormular.vue'
|
||||
import RowDetails from '../components/RowDetails.vue'
|
||||
|
||||
import { confirmPendingCreation } from '../graphql/confirmPendingCreation'
|
||||
|
||||
const slotNames = ['show-creation', 'show-register-mail', 'show-transaction-list']
|
||||
|
||||
export default {
|
||||
name: 'UserTable',
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
itemsUser: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
fieldsTable: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
criteria: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
creation: {
|
||||
type: Array,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
CreationFormular,
|
||||
EditCreationFormular,
|
||||
ConfirmRegisterMailFormular,
|
||||
CreationTransactionListFormular,
|
||||
RowDetails,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showCreationFormular: null,
|
||||
showConfirmRegisterMailFormular: null,
|
||||
showCreationTransactionListFormular: null,
|
||||
creationUserData: {},
|
||||
overlay: false,
|
||||
overlayBookmarkType: '',
|
||||
overlayItem: [],
|
||||
overlayText: [
|
||||
{
|
||||
header: '-',
|
||||
text1: '--',
|
||||
text2: '---',
|
||||
button_ok: 'OK',
|
||||
button_cancel: 'Cancel',
|
||||
},
|
||||
],
|
||||
slotIndex: 0,
|
||||
openRow: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowToogleDetails(row, index) {
|
||||
if (this.openRow) {
|
||||
if (this.openRow.index === row.index) {
|
||||
if (index === this.slotIndex) {
|
||||
row.toggleDetails()
|
||||
this.openRow = null
|
||||
} else {
|
||||
this.slotIndex = index
|
||||
}
|
||||
} else {
|
||||
this.openRow.toggleDetails()
|
||||
row.toggleDetails()
|
||||
this.slotIndex = index
|
||||
this.openRow = row
|
||||
}
|
||||
} else {
|
||||
row.toggleDetails()
|
||||
this.slotIndex = index
|
||||
this.openRow = row
|
||||
if (this.type === 'PageCreationConfirm') {
|
||||
this.creationUserData = row.item
|
||||
}
|
||||
}
|
||||
},
|
||||
overlayShow(bookmarkType, item) {
|
||||
this.overlay = true
|
||||
this.overlayBookmarkType = bookmarkType
|
||||
this.overlayItem = item
|
||||
|
||||
if (bookmarkType === 'remove') {
|
||||
this.overlayText.header = this.$t('overlay.remove.title')
|
||||
this.overlayText.text1 = this.$t('overlay.remove.text')
|
||||
this.overlayText.text2 = this.$t('overlay.remove.question')
|
||||
this.overlayText.button_ok = this.$t('overlay.remove.yes')
|
||||
this.overlayText.button_cancel = this.$t('overlay.remove.no')
|
||||
}
|
||||
if (bookmarkType === 'confirm') {
|
||||
this.overlayText.header = this.$t('overlay.confirm.title')
|
||||
this.overlayText.text1 = this.$t('overlay.confirm.text')
|
||||
this.overlayText.text2 = this.$t('overlay.confirm.question')
|
||||
this.overlayText.button_ok = this.$t('overlay.confirm.yes')
|
||||
this.overlayText.button_cancel = this.$t('overlay.confirm.no')
|
||||
}
|
||||
},
|
||||
overlayOK(bookmarkType, item) {
|
||||
if (bookmarkType === 'remove') {
|
||||
this.bookmarkRemove(item)
|
||||
}
|
||||
if (bookmarkType === 'confirm') {
|
||||
this.bookmarkConfirm(item)
|
||||
}
|
||||
this.overlay = false
|
||||
},
|
||||
overlayCancel() {
|
||||
this.overlay = false
|
||||
},
|
||||
bookmarkPush(item) {
|
||||
this.$emit('update-item', item, 'push')
|
||||
},
|
||||
bookmarkRemove(item) {
|
||||
if (this.type === 'UserListMassCreation') {
|
||||
this.$emit('update-item', item, 'remove')
|
||||
}
|
||||
|
||||
if (this.type === 'PageCreationConfirm') {
|
||||
this.$emit('remove-confirm-result', item, 'remove')
|
||||
}
|
||||
},
|
||||
bookmarkConfirm(item) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: confirmPendingCreation,
|
||||
variables: {
|
||||
id: item.id,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$emit('remove-confirm-result', item, 'confirmed')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
},
|
||||
updateCreationData(data) {
|
||||
this.creationUserData.amount = data.amount
|
||||
this.creationUserData.date = data.date
|
||||
this.creationUserData.memo = data.memo
|
||||
this.creationUserData.moderator = data.moderator
|
||||
|
||||
data.row.toggleDetails()
|
||||
},
|
||||
updateUserData(rowItem, newCreation) {
|
||||
rowItem.creation = newCreation
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
slotName() {
|
||||
return slotNames[this.slotIndex]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#overlay {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-left: 5%;
|
||||
background-color: rgba(12, 11, 11, 0.781);
|
||||
z-index: 1000000;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@ -4,11 +4,20 @@
|
||||
// Load Package Details for some default values
|
||||
const pkg = require('../../package')
|
||||
|
||||
const constants = {
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v1.2022-03-18',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const version = {
|
||||
APP_VERSION: pkg.version,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT || null,
|
||||
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').substr(0, 7),
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').slice(0, 7),
|
||||
PORT: process.env.PORT || 8080,
|
||||
}
|
||||
|
||||
const environment = {
|
||||
@ -19,21 +28,32 @@ const environment = {
|
||||
|
||||
const endpoints = {
|
||||
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
|
||||
WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/vue/authenticate?token=$1',
|
||||
WALLET_AUTH_URL: process.env.WALLET_AUTH_URL || 'http://localhost/authenticate?token={token}',
|
||||
WALLET_URL: process.env.WALLET_URL || 'http://localhost/login',
|
||||
}
|
||||
|
||||
const debug = {
|
||||
DEBUG_DISABLE_AUTH: process.env.DEBUG_DISABLE_AUTH === 'true' || false,
|
||||
}
|
||||
|
||||
const options = {}
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
if (
|
||||
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
|
||||
constants.CONFIG_VERSION.CURRENT,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
|
||||
)
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
...constants,
|
||||
...version,
|
||||
...environment,
|
||||
...endpoints,
|
||||
...options,
|
||||
...debug,
|
||||
}
|
||||
|
||||
export default CONFIG
|
||||
module.exports = CONFIG
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const confirmPendingCreation = gql`
|
||||
mutation ($id: Float!) {
|
||||
mutation ($id: Int!) {
|
||||
confirmPendingCreation(id: $id)
|
||||
}
|
||||
`
|
||||
|
||||
22
admin/src/graphql/creationTransactionList.js
Normal file
22
admin/src/graphql/creationTransactionList.js
Normal file
@ -0,0 +1,22 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const creationTransactionList = gql`
|
||||
query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC, $userId: Int!) {
|
||||
creationTransactionList(
|
||||
currentPage: $currentPage
|
||||
pageSize: $pageSize
|
||||
order: $order
|
||||
userId: $userId
|
||||
) {
|
||||
id
|
||||
amount
|
||||
balanceDate
|
||||
creationDate
|
||||
memo
|
||||
linkedUser {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -1,7 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const deletePendingCreation = gql`
|
||||
mutation ($id: Float!) {
|
||||
mutation ($id: Int!) {
|
||||
deletePendingCreation(id: $id)
|
||||
}
|
||||
`
|
||||
|
||||
7
admin/src/graphql/deleteUser.js
Normal file
7
admin/src/graphql/deleteUser.js
Normal file
@ -0,0 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const deleteUser = gql`
|
||||
mutation ($userId: Int!) {
|
||||
deleteUser(userId: $userId)
|
||||
}
|
||||
`
|
||||
@ -1,14 +1,32 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const searchUsers = gql`
|
||||
query ($searchText: String!) {
|
||||
searchUsers(searchText: $searchText) {
|
||||
userId
|
||||
firstName
|
||||
lastName
|
||||
email
|
||||
creation
|
||||
emailChecked
|
||||
query (
|
||||
$searchText: String!
|
||||
$currentPage: Int
|
||||
$pageSize: Int
|
||||
$notActivated: Boolean
|
||||
$isDeleted: Boolean
|
||||
) {
|
||||
searchUsers(
|
||||
searchText: $searchText
|
||||
currentPage: $currentPage
|
||||
pageSize: $pageSize
|
||||
notActivated: $notActivated
|
||||
isDeleted: $isDeleted
|
||||
) {
|
||||
userCount
|
||||
userList {
|
||||
userId
|
||||
firstName
|
||||
lastName
|
||||
email
|
||||
creation
|
||||
emailChecked
|
||||
hasElopage
|
||||
emailConfirmationSend
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const transactionList = gql`
|
||||
query (
|
||||
$currentPage: Int = 1
|
||||
$pageSize: Int = 25
|
||||
$order: Order = DESC
|
||||
$onlyCreations: Boolean = false
|
||||
$userId: Int = null
|
||||
) {
|
||||
transactionList(
|
||||
currentPage: $currentPage
|
||||
pageSize: $pageSize
|
||||
order: $order
|
||||
onlyCreations: $onlyCreations
|
||||
userId: $userId
|
||||
) {
|
||||
gdtSum
|
||||
count
|
||||
balance
|
||||
decay
|
||||
decayDate
|
||||
transactions {
|
||||
type
|
||||
balance
|
||||
decayStart
|
||||
decayEnd
|
||||
decayDuration
|
||||
memo
|
||||
transactionId
|
||||
name
|
||||
email
|
||||
date
|
||||
decay {
|
||||
balance
|
||||
decayStart
|
||||
decayEnd
|
||||
decayDuration
|
||||
decayStartBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
7
admin/src/graphql/unDeleteUser.js
Normal file
7
admin/src/graphql/unDeleteUser.js
Normal file
@ -0,0 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const unDeleteUser = gql`
|
||||
mutation ($userId: Int!) {
|
||||
unDeleteUser(userId: $userId)
|
||||
}
|
||||
`
|
||||
@ -7,6 +7,7 @@ export const verifyLogin = gql`
|
||||
lastName
|
||||
isAdmin
|
||||
id
|
||||
language
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -54,12 +54,21 @@ const dateTimeFormats = {
|
||||
},
|
||||
long: {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'short',
|
||||
weekday: 'long',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
},
|
||||
monthShort: {
|
||||
month: 'short',
|
||||
},
|
||||
month: {
|
||||
month: 'long',
|
||||
},
|
||||
year: {
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
de: {
|
||||
short: {
|
||||
@ -69,12 +78,21 @@ const dateTimeFormats = {
|
||||
},
|
||||
long: {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
weekday: 'short',
|
||||
weekday: 'long',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
},
|
||||
monthShort: {
|
||||
month: 'short',
|
||||
},
|
||||
month: {
|
||||
month: 'long',
|
||||
},
|
||||
year: {
|
||||
year: 'numeric',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
{
|
||||
"bookmark": "bookmark",
|
||||
"confirmed": "bestätigt",
|
||||
"all_emails": "Alle Nutzer",
|
||||
"back": "zurück",
|
||||
"creation": "Schöpfung",
|
||||
"creation_form": {
|
||||
"creation_for": "Schöpfung für ",
|
||||
"creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.",
|
||||
"creation_for": "Aktives Grundeinkommen für",
|
||||
"enter_text": "Text eintragen",
|
||||
"form": "Schöpfungsformular",
|
||||
"min_characters": "Mindestens 10 Zeichen eingeben",
|
||||
@ -11,55 +13,87 @@
|
||||
"select_value": "Betrag auswählen",
|
||||
"submit_creation": "Schöpfung einreichen",
|
||||
"toasted": "Offene Schöpfung ({value} GDD) für {email} wurde gespeichert und liegt zur Bestätigung bereit",
|
||||
"toasted_created": "Schöpfung wurde erfolgreich gespeichert",
|
||||
"toasted_delete": "Offene Schöpfung wurde gelöscht",
|
||||
"toasted_update": "`Offene Schöpfung {value} GDD) für {email} wurde geändert und liegt zur Bestätigung bereit",
|
||||
"update_creation": "Schöpfung aktualisieren"
|
||||
},
|
||||
"details": "Details",
|
||||
"creation_for_month": "Schöpfung für Monat",
|
||||
"date": "Datum",
|
||||
"delete": "Löschen",
|
||||
"deleted": "gelöscht",
|
||||
"deleted_user": "Alle gelöschten Nutzer",
|
||||
"delete_user": "Nutzer löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"enabled": "aktiviert",
|
||||
"error": "Fehler",
|
||||
"e_mail": "E-Mail",
|
||||
"firstname": "Vorname",
|
||||
"gradido_admin_footer": "Gradido Akademie Adminkonsole",
|
||||
"hide_details": "Details verbergen von",
|
||||
"footer": {
|
||||
"app_version": "App version {version}",
|
||||
"copyright": {
|
||||
"link": "Gradido Akademie Adminkonsole",
|
||||
"year": "© {year}"
|
||||
},
|
||||
"short_hash": "({shortHash})"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"hide_details": "Details verbergen",
|
||||
"lastname": "Nachname",
|
||||
"math": {
|
||||
"exclaim": "!",
|
||||
"pipe": "|"
|
||||
},
|
||||
"moderator": "Moderator",
|
||||
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
||||
"name": "Name",
|
||||
"navbar": {
|
||||
"logout": "Abmelden",
|
||||
"multi_creation": "Mehrfachschöpfung",
|
||||
"my-account": "Mein Konto",
|
||||
"open_creation": "Offene Schöpfungen",
|
||||
"overview": "Übersicht",
|
||||
"user_search": "Nutzersuche",
|
||||
"wallet": "Wallet"
|
||||
"user_search": "Nutzersuche"
|
||||
},
|
||||
"not_open_creations": "Keine offenen Schöpfungen",
|
||||
"open_creation": "Offene Schöpfung",
|
||||
"open_creations": "Offene Schöpfungen",
|
||||
"overlay": {
|
||||
"confirm": {
|
||||
"no": "Nein, nicht speichern.",
|
||||
"question": "Willst du diese vorgespeicherte Schöpfung wirklich vollziehen und entgültig speichern?",
|
||||
"cancel": "Abbrechen",
|
||||
"question": "Willst du diese vorgespeicherte Schöpfung wirklich vollziehen und endgültig speichern?",
|
||||
"text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.",
|
||||
"title": "Schöpfung bestätigen!",
|
||||
"yes": "Ja, Schöpfung bestätigen und speichern!"
|
||||
},
|
||||
"remove": {
|
||||
"no": "Nein, nicht löschen.",
|
||||
"question": "Willst du die vorgespeicherte Schöpfung wirklich löschen?",
|
||||
"text": "Nach dem Löschen gibt es keine Möglichkeit mehr diesen Datensatz wiederherzustellen. Es wird aber der gesamte Vorgang in der Logdatei als Übersicht gespeichert.",
|
||||
"title": "Achtung! Schöpfung löschen!",
|
||||
"yes": "Ja, Schöpfung löschen!"
|
||||
}
|
||||
},
|
||||
"remove": "Entfernen",
|
||||
"transaction": "Transaktion",
|
||||
"removeNotSelf": "Als Admin / Moderator kannst du dich nicht selber löschen.",
|
||||
"remove_all": "alle Nutzer entfernen",
|
||||
"save": "Speichern",
|
||||
"status": "Status",
|
||||
"success": "Erfolg",
|
||||
"text": "Text",
|
||||
"transactionlist": {
|
||||
"amount": "Betrag",
|
||||
"balanceDate": "Schöpfungsdatum",
|
||||
"community": "Gemeinschaft",
|
||||
"date": "Datum",
|
||||
"memo": "Nachricht",
|
||||
"title": "Alle geschöpften Transaktionen für den Nutzer"
|
||||
},
|
||||
"unregistered_emails": "Unregistrierte E-Mails",
|
||||
"undelete_user": "Nutzer wiederherstellen",
|
||||
"unregistered_emails": "Nur unregistrierte Nutzer",
|
||||
"unregister_mail": {
|
||||
"button": "Registrierungs-Email bestätigen, jetzt senden",
|
||||
"error": "Fehler beim Senden des Bestätigungs-Links an den Benutzer: {message}",
|
||||
"info": "Email bestätigen, wiederholt senden an:",
|
||||
"never_sent": "Es scheint so, als ob wir nie eine E-Mail an {email} geschickt haben",
|
||||
"success": "Erfolgreiches Senden des Bestätigungs-Links an die E-Mail des Nutzers! ({email})",
|
||||
"text": " Die letzte Email wurde am <b>{date} Uhr</b> an das Mitglied ({mail}) gesendet."
|
||||
"text_false": " Die letzte Email wurde am {date} Uhr an das Mitglied ({email}) gesendet.",
|
||||
"text_true": " Die Email wurde bestätigt."
|
||||
},
|
||||
"userIsDeleted": "Der Nutzer ist gelöscht. Es können keine GDD mehr geschöpft werden.",
|
||||
"user_deleted": "Nutzer ist gelöscht.",
|
||||
"user_recovered": "Nutzer ist wiederhergestellt.",
|
||||
"user_search": "Nutzer-Suche"
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
{
|
||||
"bookmark": "Remember",
|
||||
"confirmed": "confirmed",
|
||||
"all_emails": "All users",
|
||||
"back": "back",
|
||||
"creation": "Creation",
|
||||
"creation_form": {
|
||||
"creation_for": "Creation for ",
|
||||
"creation_failed": "Could not create pending creation for {email}",
|
||||
"creation_for": "Active Basic Income for",
|
||||
"enter_text": "Enter text",
|
||||
"form": "Creation form",
|
||||
"min_characters": "Enter at least 10 characters",
|
||||
@ -11,55 +13,87 @@
|
||||
"select_value": "Select amount",
|
||||
"submit_creation": "Submit creation",
|
||||
"toasted": "Open creation ({value} GDD) for {email} has been saved and is ready for confirmation.",
|
||||
"toasted_created": "Creation has been successfully saved",
|
||||
"toasted_delete": "Open creation has been deleted",
|
||||
"toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.",
|
||||
"update_creation": "Creation update"
|
||||
},
|
||||
"details": "Details",
|
||||
"e_mail": "E-Mail",
|
||||
"creation_for_month": "Creation for month",
|
||||
"date": "Date",
|
||||
"delete": "Delete",
|
||||
"deleted": "deleted",
|
||||
"deleted_user": "All deleted user",
|
||||
"delete_user": "Delete user",
|
||||
"edit": "Edit",
|
||||
"enabled": "enabled",
|
||||
"error": "Error",
|
||||
"e_mail": "E-mail",
|
||||
"firstname": "Firstname",
|
||||
"gradido_admin_footer": "Gradido Academy Admin Console",
|
||||
"hide_details": "Hide details from",
|
||||
"footer": {
|
||||
"app_version": "App version {version}",
|
||||
"copyright": {
|
||||
"link": "Gradido Academy Admin Console",
|
||||
"year": "© {year}"
|
||||
},
|
||||
"short_hash": "({shortHash})"
|
||||
},
|
||||
"GDD": "GDD",
|
||||
"hide_details": "Hide details",
|
||||
"lastname": "Lastname",
|
||||
"math": {
|
||||
"exclaim": "!",
|
||||
"pipe": "|"
|
||||
},
|
||||
"moderator": "Moderator",
|
||||
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
||||
"name": "Name",
|
||||
"navbar": {
|
||||
"logout": "Logout",
|
||||
"multi_creation": "Multiple creation",
|
||||
"my-account": "My Account",
|
||||
"open_creation": "Open creations",
|
||||
"overview": "Overview",
|
||||
"user_search": "User search",
|
||||
"wallet": "Wallet"
|
||||
"user_search": "User search"
|
||||
},
|
||||
"not_open_creations": "No open creations",
|
||||
"open_creation": "Open creation",
|
||||
"open_creations": "Open creations",
|
||||
"overlay": {
|
||||
"confirm": {
|
||||
"no": "No, do not save.",
|
||||
"cancel": "Cancel",
|
||||
"question": "Do you really want to carry out and finally save this pre-stored creation?",
|
||||
"text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.",
|
||||
"title": "Confirm creation!",
|
||||
"yes": "Yes, confirm and save creation!"
|
||||
},
|
||||
"remove": {
|
||||
"no": "No, do not delete.",
|
||||
"question": "Do you really want to delete the pre-stored creation?",
|
||||
"text": "After deletion, there is no possibility to restore this data record. However, the entire process is saved in the log file as an overview.",
|
||||
"title": "Attention! Delete creation!",
|
||||
"yes": "Yes, delete creation!"
|
||||
}
|
||||
},
|
||||
"remove": "Remove",
|
||||
"transaction": "Transaction",
|
||||
"removeNotSelf": "As admin / moderator you cannot delete yourself.",
|
||||
"remove_all": "Remove all users",
|
||||
"save": "Speichern",
|
||||
"status": "Status",
|
||||
"success": "Success",
|
||||
"text": "Text",
|
||||
"transactionlist": {
|
||||
"amount": "Amount",
|
||||
"balanceDate": "Creation date",
|
||||
"community": "Community",
|
||||
"date": "Date",
|
||||
"memo": "Message",
|
||||
"title": "All creation-transactions for the user"
|
||||
},
|
||||
"unregistered_emails": "Unregistered e-mails",
|
||||
"undelete_user": "Undelete User",
|
||||
"unregistered_emails": "Only unregistered users",
|
||||
"unregister_mail": {
|
||||
"button": "Confirm registration email, send now",
|
||||
"error": "Error sending the confirmation link to the user: {message}",
|
||||
"info": "Confirm email, send repeatedly to:",
|
||||
"never_sent": "It seems we did never send an email to the member {email}",
|
||||
"success": "Successfully send the confirmation link to the user's email! ({email})",
|
||||
"text": " The last email was sent to the member ({mail}) on <b>{date} clock</b>."
|
||||
"text_false": "The last email was sent to the member ({email}) on {date}.",
|
||||
"text_true": "The email was confirmed."
|
||||
},
|
||||
"userIsDeleted": "The user is deleted. No more GDD can be created.",
|
||||
"user_deleted": "User is deleted.",
|
||||
"user_recovered": "User is recovered.",
|
||||
"user_search": "User search"
|
||||
}
|
||||
|
||||
@ -13,39 +13,31 @@ import i18n from './i18n'
|
||||
|
||||
import VueApollo from 'vue-apollo'
|
||||
|
||||
import PortalVue from 'portal-vue'
|
||||
|
||||
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||
import 'bootstrap/dist/css/bootstrap.css'
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
import moment from 'vue-moment'
|
||||
import Toasted from 'vue-toasted'
|
||||
import { toasters } from './mixins/toaster'
|
||||
|
||||
import { apolloProvider } from './plugins/apolloProvider'
|
||||
|
||||
Vue.use(PortalVue)
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
Vue.use(IconsPlugin)
|
||||
|
||||
Vue.use(moment)
|
||||
|
||||
Vue.use(VueApollo)
|
||||
|
||||
Vue.use(Toasted, {
|
||||
position: 'top-center',
|
||||
duration: 5000,
|
||||
fullWidth: true,
|
||||
action: {
|
||||
text: 'x',
|
||||
onClick: (e, toastObject) => {
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
})
|
||||
Vue.mixin(toasters)
|
||||
|
||||
addNavigationGuards(router, store, apolloProvider.defaultClient)
|
||||
addNavigationGuards(router, store, apolloProvider.defaultClient, i18n)
|
||||
|
||||
i18n.locale =
|
||||
store.state.moderator && store.state.moderator.language ? store.state.moderator.language : 'en'
|
||||
|
||||
new Vue({
|
||||
moment,
|
||||
router,
|
||||
store,
|
||||
i18n,
|
||||
|
||||
@ -6,7 +6,6 @@ import Vue from 'vue'
|
||||
import VueApollo from 'vue-apollo'
|
||||
import i18n from './i18n'
|
||||
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||
import moment from 'vue-moment'
|
||||
import store from './store/store'
|
||||
import router from './router/router'
|
||||
|
||||
@ -14,8 +13,15 @@ jest.mock('vue')
|
||||
jest.mock('vue-apollo')
|
||||
jest.mock('vuex')
|
||||
jest.mock('vue-i18n')
|
||||
jest.mock('vue-moment')
|
||||
jest.mock('./store/store')
|
||||
jest.mock('./store/store', () => {
|
||||
return {
|
||||
state: {
|
||||
moderator: {
|
||||
language: 'es',
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
jest.mock('./i18n')
|
||||
jest.mock('./router/router')
|
||||
|
||||
@ -82,10 +88,6 @@ describe('main', () => {
|
||||
expect(Vue.use).toBeCalledWith(IconsPlugin)
|
||||
})
|
||||
|
||||
it('calls Moment', () => {
|
||||
expect(Vue.use).toBeCalledWith(moment)
|
||||
})
|
||||
|
||||
it('creates a store', () => {
|
||||
expect(Vue).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -101,4 +103,8 @@ describe('main', () => {
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('sets the locale from store', () => {
|
||||
expect(i18n.locale).toBe('es')
|
||||
})
|
||||
})
|
||||
|
||||
41
admin/src/mixins/creationMonths.js
Normal file
41
admin/src/mixins/creationMonths.js
Normal file
@ -0,0 +1,41 @@
|
||||
export const creationMonths = {
|
||||
props: {
|
||||
creation: {
|
||||
type: Array,
|
||||
default: () => [1000, 1000, 1000],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
creationDates() {
|
||||
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()
|
||||
},
|
||||
creationDateObjects() {
|
||||
const result = []
|
||||
this.creationDates.forEach((date) => {
|
||||
result.push({
|
||||
short: this.$d(date, 'month'),
|
||||
long: this.$d(date, 'short'),
|
||||
year: this.$d(date, 'year'),
|
||||
date: this.$d(date, 'short', 'en'),
|
||||
})
|
||||
})
|
||||
return result
|
||||
},
|
||||
radioOptions() {
|
||||
return this.creationDateObjects.map((obj, idx) => {
|
||||
return {
|
||||
item: { ...obj, creation: this.creation[idx] },
|
||||
name: obj.short + (this.creation[idx] ? ' ' + this.creation[idx] + ' GDD' : ''),
|
||||
}
|
||||
})
|
||||
},
|
||||
creationLabel() {
|
||||
return this.creationDates.map((date) => this.$d(date, 'monthShort')).join(' | ')
|
||||
},
|
||||
},
|
||||
}
|
||||
30
admin/src/mixins/toaster.js
Normal file
30
admin/src/mixins/toaster.js
Normal file
@ -0,0 +1,30 @@
|
||||
export const toasters = {
|
||||
methods: {
|
||||
toastSuccess(message) {
|
||||
this.toast(message, {
|
||||
title: this.$t('success'),
|
||||
variant: 'success',
|
||||
})
|
||||
},
|
||||
toastError(message) {
|
||||
this.toast(message, {
|
||||
title: this.$t('error'),
|
||||
variant: 'danger',
|
||||
})
|
||||
},
|
||||
toast(message, options) {
|
||||
// for unit tests, check that replace is present
|
||||
if (message.replace) message = message.replace(/^GraphQL error: /, '')
|
||||
this.$root.$bvToast.toast(message, {
|
||||
autoHideDelay: 5000,
|
||||
appendToast: true,
|
||||
solid: true,
|
||||
toaster: 'b-toaster-top-right',
|
||||
headerClass: 'gdd-toaster-title',
|
||||
bodyClass: 'gdd-toaster-body',
|
||||
toastClass: 'gdd-toaster',
|
||||
...options,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
34
admin/src/mixins/toggleRowDetails.js
Normal file
34
admin/src/mixins/toggleRowDetails.js
Normal file
@ -0,0 +1,34 @@
|
||||
export const toggleRowDetails = {
|
||||
data() {
|
||||
return {
|
||||
slotIndex: 0,
|
||||
openRow: null,
|
||||
creationUserData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowToggleDetails(row, index) {
|
||||
if (this.openRow) {
|
||||
if (this.openRow.index === row.index) {
|
||||
if (index === this.slotIndex) {
|
||||
row.toggleDetails()
|
||||
this.openRow = null
|
||||
} else {
|
||||
this.slotIndex = index
|
||||
}
|
||||
} else {
|
||||
this.openRow.toggleDetails()
|
||||
row.toggleDetails()
|
||||
this.slotIndex = index
|
||||
this.openRow = row
|
||||
this.creationUserData = row.item
|
||||
}
|
||||
} else {
|
||||
row.toggleDetails()
|
||||
this.slotIndex = index
|
||||
this.openRow = row
|
||||
this.creationUserData = row.item
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
141
admin/src/mixins/toggleRowDetails.test.js
Normal file
141
admin/src/mixins/toggleRowDetails.test.js
Normal file
@ -0,0 +1,141 @@
|
||||
import { toggleRowDetails } from './toggleRowDetails'
|
||||
import { mount } from '@vue/test-utils'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const Component = {
|
||||
render() {},
|
||||
mixins: [toggleRowDetails],
|
||||
}
|
||||
|
||||
const toggleDetailsMock = jest.fn()
|
||||
const secondToggleDetailsMock = jest.fn()
|
||||
|
||||
const row = {
|
||||
toggleDetails: toggleDetailsMock,
|
||||
index: 0,
|
||||
item: {
|
||||
data: 'item-data',
|
||||
},
|
||||
}
|
||||
|
||||
let wrapper
|
||||
|
||||
describe('toggleRowDetails', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
wrapper = mount(Component, { localVue })
|
||||
})
|
||||
|
||||
it('sets default data', () => {
|
||||
expect(wrapper.vm.slotIndex).toBe(0)
|
||||
expect(wrapper.vm.openRow).toBe(null)
|
||||
expect(wrapper.vm.creationUserData).toEqual({})
|
||||
})
|
||||
|
||||
describe('no open row', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.rowToggleDetails(row, 2)
|
||||
})
|
||||
|
||||
it('calls toggleDetails', () => {
|
||||
expect(toggleDetailsMock).toBeCalled()
|
||||
})
|
||||
|
||||
it('updates slot index', () => {
|
||||
expect(wrapper.vm.slotIndex).toBe(2)
|
||||
})
|
||||
|
||||
it('updates open row', () => {
|
||||
expect(wrapper.vm.openRow).toEqual(
|
||||
expect.objectContaining({
|
||||
index: 0,
|
||||
item: {
|
||||
data: 'item-data',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('updates creation user data', () => {
|
||||
expect(wrapper.vm.creationUserData).toEqual({ data: 'item-data' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('with open row', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.setData({ openRow: row })
|
||||
})
|
||||
|
||||
describe('row index is open row index', () => {
|
||||
describe('index is slot index', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.rowToggleDetails(row, 0)
|
||||
})
|
||||
|
||||
it('calls toggleDetails', () => {
|
||||
expect(toggleDetailsMock).toBeCalled()
|
||||
})
|
||||
|
||||
it('sets open row to null', () => {
|
||||
expect(wrapper.vm.openRow).toBe(null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('index is not slot index', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.rowToggleDetails(row, 2)
|
||||
})
|
||||
|
||||
it('does not call toggleDetails', () => {
|
||||
expect(toggleDetailsMock).not.toBeCalled()
|
||||
})
|
||||
|
||||
it('updates slot index', () => {
|
||||
expect(wrapper.vm.slotIndex).toBe(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('row index is not open row index', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.rowToggleDetails(
|
||||
{
|
||||
toggleDetails: secondToggleDetailsMock,
|
||||
index: 2,
|
||||
item: {
|
||||
data: 'new-item-data',
|
||||
},
|
||||
},
|
||||
2,
|
||||
)
|
||||
})
|
||||
|
||||
it('closes the open row', () => {
|
||||
expect(toggleDetailsMock).toBeCalled()
|
||||
})
|
||||
|
||||
it('opens the new row', () => {
|
||||
expect(secondToggleDetailsMock).toBeCalled()
|
||||
})
|
||||
|
||||
it('updates slot index', () => {
|
||||
expect(wrapper.vm.slotIndex).toBe(2)
|
||||
})
|
||||
|
||||
it('updates open row', () => {
|
||||
expect(wrapper.vm.openRow).toEqual({
|
||||
toggleDetails: secondToggleDetailsMock,
|
||||
index: 2,
|
||||
item: {
|
||||
data: 'new-item-data',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('updates creation user data', () => {
|
||||
expect(wrapper.vm.creationUserData).toEqual({ data: 'new-item-data' })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,39 +1,48 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Creation from './Creation.vue'
|
||||
import Vue from 'vue'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
searchUsers: [
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
},
|
||||
],
|
||||
searchUsers: {
|
||||
userCount: 2,
|
||||
userList: [
|
||||
{
|
||||
userId: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
emailChecked: true,
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
emailChecked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const toastErrorMock = jest.fn()
|
||||
const storeCommitMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$t: jest.fn((t, options) => (options ? [t, options] : t)),
|
||||
$d: jest.fn((d) => d),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
$toasted: {
|
||||
error: toastErrorMock,
|
||||
$store: {
|
||||
commit: storeCommitMock,
|
||||
state: {
|
||||
userSelectedInMassCreation: [],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -41,11 +50,12 @@ describe('Creation', () => {
|
||||
let wrapper
|
||||
|
||||
const Wrapper = () => {
|
||||
return shallowMount(Creation, { localVue, mocks })
|
||||
return mount(Creation, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('shallowMount', () => {
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
@ -55,165 +65,243 @@ describe('Creation', () => {
|
||||
|
||||
describe('apollo returns user array', () => {
|
||||
it('calls the searchUser query', () => {
|
||||
expect(apolloQueryMock).toBeCalled()
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('sets the data of itemsList', () => {
|
||||
expect(wrapper.vm.itemsList).toEqual([
|
||||
it('has two rows in the left table', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('has nwo rows in the right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('has correct data in first row ', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi')
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'Bloxberg',
|
||||
)
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'200 | 400 | 600',
|
||||
)
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'bibi@bloxberg.de',
|
||||
)
|
||||
})
|
||||
|
||||
it('has correct data in second row ', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||
'Benjamin',
|
||||
)
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||
'Blümchen',
|
||||
)
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||
'800 | 600 | 400',
|
||||
)
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
||||
'benjamin@bluemchen.de',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('push item', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('has one item in left table', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('has one item in right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('has the correct user in left table', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'bibi@bloxberg.de',
|
||||
)
|
||||
})
|
||||
|
||||
it('has the correct user in right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'benjamin@bluemchen.de',
|
||||
)
|
||||
})
|
||||
|
||||
it('updates userSelectedInMassCreation in store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
showDetails: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
userId: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
emailChecked: true,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
describe('remove item', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.findAll('table')
|
||||
.at(1)
|
||||
.findAll('tbody > tr')
|
||||
.at(0)
|
||||
.find('button')
|
||||
.trigger('click')
|
||||
})
|
||||
|
||||
it('has two items in left table', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
||||
})
|
||||
|
||||
it('has the removed user in first row', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'benjamin@bluemchen.de',
|
||||
)
|
||||
})
|
||||
|
||||
it('has no items in right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('commits empty array as userSelectedInMassCreation', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
||||
})
|
||||
})
|
||||
|
||||
describe('remove all bookmarks', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.find('button.btn-light').trigger('click')
|
||||
})
|
||||
|
||||
it('has no items in right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('commits empty array to userSelectedInMassCreation', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
||||
})
|
||||
|
||||
it('calls searchUsers', () => {
|
||||
expect(apolloQueryMock).toBeCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('store has items in userSelectedInMassCreation', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$store.state.userSelectedInMassCreation = [
|
||||
{
|
||||
userId: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
emailChecked: true,
|
||||
},
|
||||
]
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has one item in left table', () => {
|
||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('has one item in right table', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('has the stored user in second row', () => {
|
||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
||||
'benjamin@bluemchen.de',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('failed creations', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.findComponent({ name: 'CreationFormular' })
|
||||
.vm.$emit('toast-failed-creations', ['bibi@bloxberg.de', 'benjamin@bluemchen.de'])
|
||||
})
|
||||
|
||||
it('toasts two error messages', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith([
|
||||
'creation_form.creation_failed',
|
||||
{ email: 'bibi@bloxberg.de' },
|
||||
])
|
||||
expect(toastErrorSpy).toBeCalledWith([
|
||||
'creation_form.creation_failed',
|
||||
{ email: 'benjamin@bluemchen.de' },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('update item', () => {
|
||||
describe('watchers', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('push', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
|
||||
'update-item',
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
},
|
||||
'push',
|
||||
describe('search criteria', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({ criteria: 'XX' })
|
||||
})
|
||||
|
||||
it('calls API when criteria changes', async () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: 'XX',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('removes the pushed item from itemsList', () => {
|
||||
expect(wrapper.vm.itemsList).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
showDetails: false,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('adds the pushed item to itemsMassCreation', () => {
|
||||
expect(wrapper.vm.itemsMassCreation).toEqual([
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
describe('remove', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
|
||||
'update-item',
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
},
|
||||
'remove',
|
||||
describe('reset search criteria', () => {
|
||||
it('calls the API', async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('removes the item from itemsMassCreation', () => {
|
||||
expect(wrapper.vm.itemsMassCreation).toEqual([])
|
||||
})
|
||||
|
||||
it('adds the item to itemsList', () => {
|
||||
expect(wrapper.vm.itemsList).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
showDetails: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('error', () => {
|
||||
const consoleErrorMock = jest.fn()
|
||||
const warnHandler = Vue.config.warnHandler
|
||||
|
||||
beforeEach(() => {
|
||||
Vue.config.warnHandler = (w) => {}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error = consoleErrorMock
|
||||
wrapper.findComponent({ name: 'UserTable' }).vm.$emit('update-item', {}, 'no-rule')
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Vue.config.warnHandler = warnHandler
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
expect(consoleErrorMock).toBeCalledWith(expect.objectContaining({ message: 'no-rule' }))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('remove all bookmarks', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit(
|
||||
'update-item',
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [800, 600, 400],
|
||||
showDetails: false,
|
||||
},
|
||||
'push',
|
||||
it('calls API when currentPage changes', async () => {
|
||||
await wrapper.setData({ currentPage: 2 })
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 2,
|
||||
pageSize: 25,
|
||||
},
|
||||
}),
|
||||
)
|
||||
wrapper.findComponent({ name: 'CreationFormular' }).vm.$emit('remove-all-bookmark')
|
||||
})
|
||||
|
||||
it('removes all items from itemsMassCreation', () => {
|
||||
expect(wrapper.vm.itemsMassCreation).toEqual([])
|
||||
})
|
||||
|
||||
it('adds all items to itemsList', () => {
|
||||
expect(wrapper.vm.itemsList).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
@ -226,7 +314,7 @@ describe('Creation', () => {
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorMock).toBeCalledWith('Ouch')
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouch')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,35 +1,52 @@
|
||||
<template>
|
||||
<div class="creation">
|
||||
<b-row>
|
||||
<b-col cols="12" lg="5">
|
||||
<label>Usersuche</label>
|
||||
<b-input
|
||||
type="text"
|
||||
v-model="criteria"
|
||||
class="shadow p-3 mb-5 bg-white rounded"
|
||||
placeholder="User suche"
|
||||
></b-input>
|
||||
<user-table
|
||||
<b-col cols="12" lg="6">
|
||||
<label>{{ $t('user_search') }}</label>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
type="text"
|
||||
class="test-input-criteria"
|
||||
v-model="criteria"
|
||||
:placeholder="$t('user_search')"
|
||||
></b-form-input>
|
||||
|
||||
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
||||
<b-input-group-text class="pointer">
|
||||
<b-icon icon="x" />
|
||||
</b-input-group-text>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
<select-users-table
|
||||
v-if="itemsList.length > 0"
|
||||
type="UserListSearch"
|
||||
:itemsUser="itemsList"
|
||||
:fieldsTable="Searchfields"
|
||||
:criteria="criteria"
|
||||
:creation="creation"
|
||||
@update-item="updateItem"
|
||||
:items="itemsList"
|
||||
:fields="Searchfields"
|
||||
@push-item="pushItem"
|
||||
/>
|
||||
<b-pagination
|
||||
pills
|
||||
v-model="currentPage"
|
||||
per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
></b-pagination>
|
||||
</b-col>
|
||||
<b-col cols="12" lg="7" class="shadow p-3 mb-5 rounded bg-info">
|
||||
<user-table
|
||||
v-show="itemsMassCreation.length > 0"
|
||||
class="shadow p-3 mb-5 bg-white rounded"
|
||||
type="UserListMassCreation"
|
||||
:itemsUser="itemsMassCreation"
|
||||
:fieldsTable="fields"
|
||||
:criteria="null"
|
||||
:creation="creation"
|
||||
@update-item="updateItem"
|
||||
/>
|
||||
<b-col cols="12" lg="6" class="shadow p-3 mb-5 rounded bg-info">
|
||||
<div v-show="itemsMassCreation.length > 0">
|
||||
<div class="text-right pr-4 mb-1">
|
||||
<b-button @click="removeAllBookmarks()" variant="light">
|
||||
<b-icon icon="x" scale="2" variant="danger"></b-icon>
|
||||
|
||||
{{ $t('remove_all') }}
|
||||
</b-button>
|
||||
</div>
|
||||
<selected-users-table
|
||||
class="shadow p-3 mb-5 bg-white rounded"
|
||||
:items="itemsMassCreation"
|
||||
:fields="fields"
|
||||
@remove-item="removeItem"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="itemsMassCreation.length === 0">
|
||||
{{ $t('multiple_creation_text') }}
|
||||
</div>
|
||||
@ -38,7 +55,8 @@
|
||||
type="massCreation"
|
||||
:creation="creation"
|
||||
:items="itemsMassCreation"
|
||||
@remove-all-bookmark="removeAllBookmark"
|
||||
@remove-all-bookmark="removeAllBookmarks"
|
||||
@toast-failed-creations="toastFailedCreations"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@ -46,37 +64,30 @@
|
||||
</template>
|
||||
<script>
|
||||
import CreationFormular from '../components/CreationFormular.vue'
|
||||
import UserTable from '../components/UserTable.vue'
|
||||
import SelectUsersTable from '../components/Tables/SelectUsersTable.vue'
|
||||
import SelectedUsersTable from '../components/Tables/SelectedUsersTable.vue'
|
||||
import { searchUsers } from '../graphql/searchUsers'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
|
||||
export default {
|
||||
name: 'Creation',
|
||||
mixins: [creationMonths],
|
||||
components: {
|
||||
CreationFormular,
|
||||
UserTable,
|
||||
SelectUsersTable,
|
||||
SelectedUsersTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showArrays: false,
|
||||
Searchfields: [
|
||||
{ key: 'bookmark', label: 'bookmark' },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{ key: 'creation', label: this.$t('open_creations') },
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
],
|
||||
fields: [
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{ key: 'creation', label: this.$t('open_creations') },
|
||||
{ key: 'bookmark', label: this.$t('remove') },
|
||||
],
|
||||
itemsList: [],
|
||||
itemsMassCreation: [],
|
||||
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
|
||||
radioSelectedMass: '',
|
||||
criteria: '',
|
||||
creation: [null, null, null],
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 25,
|
||||
now: Date.now(),
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
@ -89,44 +100,95 @@ export default {
|
||||
query: searchUsers,
|
||||
variables: {
|
||||
searchText: this.criteria,
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then((result) => {
|
||||
this.itemsList = result.data.searchUsers.map((user) => {
|
||||
this.rows = result.data.searchUsers.userCount
|
||||
this.itemsList = result.data.searchUsers.userList.map((user) => {
|
||||
return {
|
||||
...user,
|
||||
showDetails: false,
|
||||
}
|
||||
})
|
||||
if (this.itemsMassCreation.length !== 0) {
|
||||
const selectedIndices = this.itemsMassCreation.map((item) => item.userId)
|
||||
this.itemsList = this.itemsList.filter((item) => !selectedIndices.includes(item.userId))
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
updateItem(e, event) {
|
||||
let index = 0
|
||||
let findArr = {}
|
||||
|
||||
switch (event) {
|
||||
case 'push':
|
||||
findArr = this.itemsList.find((arr) => arr.id === e.id)
|
||||
index = this.itemsList.indexOf(findArr)
|
||||
this.itemsList.splice(index, 1)
|
||||
this.itemsMassCreation.push(e)
|
||||
break
|
||||
case 'remove':
|
||||
findArr = this.itemsMassCreation.find((arr) => arr.id === e.id)
|
||||
index = this.itemsMassCreation.indexOf(findArr)
|
||||
this.itemsMassCreation.splice(index, 1)
|
||||
this.itemsList.push(e)
|
||||
break
|
||||
default:
|
||||
throw new Error(event)
|
||||
}
|
||||
pushItem(selectedItem) {
|
||||
this.itemsMassCreation = [
|
||||
this.itemsList.find((item) => selectedItem.userId === item.userId),
|
||||
...this.itemsMassCreation,
|
||||
]
|
||||
this.itemsList = this.itemsList.filter((item) => selectedItem.userId !== item.userId)
|
||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
||||
},
|
||||
removeAllBookmark() {
|
||||
this.itemsMassCreation.forEach((item) => this.itemsList.push(item))
|
||||
removeItem(selectedItem) {
|
||||
this.itemsList = [
|
||||
this.itemsMassCreation.find((item) => selectedItem.userId === item.userId),
|
||||
...this.itemsList,
|
||||
]
|
||||
this.itemsMassCreation = this.itemsMassCreation.filter(
|
||||
(item) => selectedItem.userId !== item.userId,
|
||||
)
|
||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
||||
},
|
||||
removeAllBookmarks() {
|
||||
this.itemsMassCreation = []
|
||||
this.$store.commit('setUserSelectedInMassCreation', [])
|
||||
this.getUsers()
|
||||
},
|
||||
toastFailedCreations(failedCreations) {
|
||||
failedCreations.forEach((email) =>
|
||||
this.toastError(this.$t('creation_form.creation_failed', { email })),
|
||||
)
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
Searchfields() {
|
||||
return [
|
||||
{ key: 'bookmark', label: 'bookmark' },
|
||||
{ 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: 'email', label: this.$t('e_mail') },
|
||||
]
|
||||
},
|
||||
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: 'bookmark', label: this.$t('remove') },
|
||||
]
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentPage() {
|
||||
this.getUsers()
|
||||
},
|
||||
criteria() {
|
||||
this.getUsers()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import CreationConfirm from './CreationConfirm.vue'
|
||||
import { deletePendingCreation } from '../graphql/deletePendingCreation'
|
||||
import { confirmPendingCreation } from '../graphql/confirmPendingCreation'
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const storeCommitMock = jest.fn()
|
||||
const toastedErrorMock = jest.fn()
|
||||
const toastedSuccessMock = jest.fn()
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
getPendingCreations: [
|
||||
@ -26,7 +26,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
lastName: 'Hotzenplotz',
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
amount: 1000000,
|
||||
memo: 'Gut Ergatert',
|
||||
memo: 'Gut Ergattert',
|
||||
date: new Date(),
|
||||
moderator: 0,
|
||||
},
|
||||
@ -38,6 +38,7 @@ const apolloMutateMock = jest.fn().mockResolvedValue({})
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => d),
|
||||
$store: {
|
||||
commit: storeCommitMock,
|
||||
},
|
||||
@ -45,15 +46,6 @@ const mocks = {
|
||||
query: apolloQueryMock,
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
$toasted: {
|
||||
error: toastedErrorMock,
|
||||
success: toastedSuccessMock,
|
||||
},
|
||||
$moment: jest.fn((value) => {
|
||||
return {
|
||||
format: jest.fn((format) => value),
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
describe('CreationConfirm', () => {
|
||||
@ -73,46 +65,23 @@ describe('CreationConfirm', () => {
|
||||
expect(wrapper.find('div.creation-confirm').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has two pending creations', () => {
|
||||
expect(wrapper.vm.pendingCreations).toHaveLength(2)
|
||||
})
|
||||
|
||||
describe('store', () => {
|
||||
it('commits resetOpenCreations to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('resetOpenCreations')
|
||||
})
|
||||
|
||||
it('commits setOpenCreations to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('setOpenCreations', 2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete creation delete with success', () => {
|
||||
describe('remove creation with success', () => {
|
||||
beforeEach(async () => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
getPendingCreations: [
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
amount: 500,
|
||||
memo: 'Danke für alles',
|
||||
date: new Date(),
|
||||
moderator: 0,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
amount: 1000000,
|
||||
memo: 'Gut Ergatert',
|
||||
date: new Date(),
|
||||
moderator: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await wrapper
|
||||
.findComponent({ name: 'UserTable' })
|
||||
.vm.$emit('remove-confirm-result', { id: 1 }, 'remove')
|
||||
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
|
||||
})
|
||||
|
||||
it('calls the deletePendingCreation mutation', () => {
|
||||
@ -127,85 +96,85 @@ describe('CreationConfirm', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastedSuccessMock).toBeCalledWith('Pending Creation has been deleted')
|
||||
expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_delete')
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete creation delete with error', () => {
|
||||
describe('remove creation with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' })
|
||||
await wrapper
|
||||
.findComponent({ name: 'UserTable' })
|
||||
.vm.$emit('remove-confirm-result', { id: 1 }, 'remove')
|
||||
await wrapper.findAll('tr').at(1).findAll('button').at(0).trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouchhh!')
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouchhh!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm creation delete with success', () => {
|
||||
describe('confirm creation with success', () => {
|
||||
beforeEach(async () => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
getPendingCreations: [
|
||||
{
|
||||
id: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
amount: 500,
|
||||
memo: 'Danke für alles',
|
||||
date: new Date(),
|
||||
moderator: 0,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
firstName: 'Räuber',
|
||||
lastName: 'Hotzenplotz',
|
||||
email: 'raeuber@hotzenplotz.de',
|
||||
amount: 1000000,
|
||||
memo: 'Gut Ergatert',
|
||||
date: new Date(),
|
||||
moderator: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
await wrapper
|
||||
.findComponent({ name: 'UserTable' })
|
||||
.vm.$emit('remove-confirm-result', { id: 1 }, 'confirmed')
|
||||
apolloMutateMock.mockResolvedValue({})
|
||||
await wrapper.findAll('tr').at(2).findAll('button').at(2).trigger('click')
|
||||
})
|
||||
|
||||
it('calls the deletePendingCreation mutation', () => {
|
||||
expect(apolloMutateMock).not.toBeCalledWith({
|
||||
mutation: deletePendingCreation,
|
||||
variables: { id: 1 },
|
||||
describe('overlay', () => {
|
||||
it('opens the overlay', () => {
|
||||
expect(wrapper.find('#overlay').isVisible()).toBeTruthy()
|
||||
})
|
||||
|
||||
describe('cancel confirmation', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('#overlay').findAll('button').at(0).trigger('click')
|
||||
})
|
||||
|
||||
it('closes the overlay', async () => {
|
||||
expect(wrapper.find('#overlay').exists()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('still has 2 items in the table', () => {
|
||||
expect(wrapper.findAll('tbody > tr')).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm creation', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('#overlay').findAll('button').at(1).trigger('click')
|
||||
})
|
||||
|
||||
it('calls the confirmPendingCreation mutation', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith({
|
||||
mutation: confirmPendingCreation,
|
||||
variables: { id: 2 },
|
||||
})
|
||||
})
|
||||
|
||||
it('commits openCreationsMinus to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('openCreationsMinus', 1)
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('creation_form.toasted_created')
|
||||
})
|
||||
|
||||
it('has 1 item left in the table', () => {
|
||||
expect(wrapper.findAll('tbody > tr')).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('confirm creation with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Ouchhh!' })
|
||||
await wrapper.find('#overlay').findAll('button').at(1).trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouchhh!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('commits openCreationsMinus to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('openCreationsMinus', 1)
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastedSuccessMock).toBeCalledWith('Pending Creation has been deleted')
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete creation delete with error', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper
|
||||
.findComponent({ name: 'UserTable' })
|
||||
.vm.$emit('remove-confirm-result', { id: 1 }, 'confirm')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Case confirm is not supported')
|
||||
})
|
||||
})
|
||||
|
||||
describe('server response is error', () => {
|
||||
describe('server response for get pending creations is error', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
apolloQueryMock.mockRejectedValue({
|
||||
@ -215,7 +184,7 @@ describe('CreationConfirm', () => {
|
||||
})
|
||||
|
||||
it('toast an error message', () => {
|
||||
expect(toastedErrorMock).toBeCalledWith('Ouch!')
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouch!')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,85 +1,71 @@
|
||||
<template>
|
||||
<div class="creation-confirm">
|
||||
<user-table
|
||||
<div v-if="overlay" id="overlay" @dblclick="overlay = false">
|
||||
<overlay :item="item" @overlay-cancel="overlay = false" @confirm-creation="confirmCreation" />
|
||||
</div>
|
||||
<open-creations-table
|
||||
class="mt-4"
|
||||
type="PageCreationConfirm"
|
||||
:itemsUser="confirmResult"
|
||||
:fieldsTable="fields"
|
||||
@remove-confirm-result="removeConfirmResult"
|
||||
:items="pendingCreations"
|
||||
:fields="fields"
|
||||
@remove-creation="removeCreation"
|
||||
@show-overlay="showOverlay"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserTable from '../components/UserTable.vue'
|
||||
import Overlay from '../components/Overlay.vue'
|
||||
import OpenCreationsTable from '../components/Tables/OpenCreationsTable.vue'
|
||||
import { getPendingCreations } from '../graphql/getPendingCreations'
|
||||
import { deletePendingCreation } from '../graphql/deletePendingCreation'
|
||||
import { confirmPendingCreation } from '../graphql/confirmPendingCreation'
|
||||
|
||||
export default {
|
||||
name: 'CreationConfirm',
|
||||
components: {
|
||||
UserTable,
|
||||
OpenCreationsTable,
|
||||
Overlay,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showArrays: false,
|
||||
fields: [
|
||||
{ key: 'bookmark', label: 'löschen' },
|
||||
{ key: 'email', label: 'Email' },
|
||||
{ key: 'firstName', label: 'Vorname' },
|
||||
{ key: 'lastName', label: 'Nachname' },
|
||||
{
|
||||
key: 'amount',
|
||||
label: 'Schöpfung',
|
||||
formatter: (value) => {
|
||||
return value + ' GDD'
|
||||
},
|
||||
},
|
||||
{ key: 'memo', label: 'Text' },
|
||||
{
|
||||
key: 'date',
|
||||
label: 'Datum',
|
||||
formatter: (value) => {
|
||||
return this.$moment(value).format('ll')
|
||||
},
|
||||
},
|
||||
{ key: 'moderator', label: 'Moderator' },
|
||||
{ key: 'edit_creation', label: 'ändern' },
|
||||
{ key: 'confirm', label: 'speichern' },
|
||||
],
|
||||
confirmResult: [],
|
||||
pendingCreations: [],
|
||||
overlay: false,
|
||||
item: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeConfirmResult(e, event) {
|
||||
let index = 0
|
||||
const findArr = this.confirmResult.find((arr) => arr.id === e.id)
|
||||
switch (event) {
|
||||
case 'remove':
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: deletePendingCreation,
|
||||
variables: {
|
||||
id: findArr.id,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
index = this.confirmResult.indexOf(findArr)
|
||||
this.confirmResult.splice(index, 1)
|
||||
this.$store.commit('openCreationsMinus', 1)
|
||||
this.$toasted.success('Pending Creation has been deleted')
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
break
|
||||
case 'confirmed':
|
||||
this.confirmResult.splice(index, 1)
|
||||
this.$store.commit('openCreationsMinus', 1)
|
||||
this.$toasted.success('Pending Creation has been deleted')
|
||||
break
|
||||
default:
|
||||
this.$toasted.error('Case ' + event + ' is not supported')
|
||||
}
|
||||
removeCreation(item) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: deletePendingCreation,
|
||||
variables: {
|
||||
id: item.id,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.updatePendingCreations(item.id)
|
||||
this.toastSuccess(this.$t('creation_form.toasted_delete'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
confirmCreation() {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: confirmPendingCreation,
|
||||
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)
|
||||
})
|
||||
},
|
||||
getPendingCreations() {
|
||||
this.$apollo
|
||||
@ -89,16 +75,69 @@ export default {
|
||||
})
|
||||
.then((result) => {
|
||||
this.$store.commit('resetOpenCreations')
|
||||
this.confirmResult = result.data.getPendingCreations
|
||||
this.pendingCreations = result.data.getPendingCreations
|
||||
this.$store.commit('setOpenCreations', result.data.getPendingCreations.length)
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
updatePendingCreations(id) {
|
||||
this.pendingCreations = this.pendingCreations.filter((obj) => obj.id !== id)
|
||||
this.$store.commit('openCreationsMinus', 1)
|
||||
},
|
||||
showOverlay(item) {
|
||||
this.overlay = true
|
||||
this.item = item
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
{ key: 'bookmark', label: this.$t('delete') },
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
{ 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') },
|
||||
{
|
||||
key: 'date',
|
||||
label: this.$t('date'),
|
||||
formatter: (value) => {
|
||||
return this.$d(new Date(value), 'short')
|
||||
},
|
||||
},
|
||||
{ key: 'moderator', label: this.$t('moderator') },
|
||||
{ key: 'edit_creation', label: this.$t('edit') },
|
||||
{ key: 'confirm', label: this.$t('save') },
|
||||
]
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
await this.getPendingCreations()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
#overlay {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-left: 5%;
|
||||
background-color: rgba(12, 11, 11, 0.781);
|
||||
z-index: 1000000;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,42 +1,61 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import UserSearch from './UserSearch.vue'
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
searchUsers: [
|
||||
{
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
emailChecked: false,
|
||||
},
|
||||
],
|
||||
searchUsers: {
|
||||
userCount: 1,
|
||||
userList: [
|
||||
{
|
||||
userId: 1,
|
||||
firstName: 'Bibi',
|
||||
lastName: 'Bloxberg',
|
||||
email: 'bibi@bloxberg.de',
|
||||
creation: [200, 400, 600],
|
||||
emailChecked: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
firstName: 'Benjamin',
|
||||
lastName: 'Blümchen',
|
||||
email: 'benjamin@bluemchen.de',
|
||||
creation: [1000, 1000, 1000],
|
||||
emailChecked: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
email: 'peter@lustig.de',
|
||||
creation: [0, 0, 0],
|
||||
emailChecked: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
{
|
||||
userId: 4,
|
||||
firstName: 'New',
|
||||
lastName: 'User',
|
||||
email: 'new@user.ch',
|
||||
creation: [1000, 1000, 1000],
|
||||
emailChecked: false,
|
||||
deletedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const toastErrorMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => String(d)),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
$toasted: {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$moment: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
subtract: jest.fn(() => {
|
||||
return {
|
||||
format: jest.fn((m) => m),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
describe('UserSearch', () => {
|
||||
@ -48,6 +67,7 @@ describe('UserSearch', () => {
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
@ -55,13 +75,130 @@ describe('UserSearch', () => {
|
||||
expect(wrapper.find('div.user-search').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
notActivated: false,
|
||||
isDeleted: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('unconfirmed emails', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button.btn-block').trigger('click')
|
||||
await wrapper.find('button.unconfirmedRegisterMails').trigger('click')
|
||||
})
|
||||
|
||||
it('filters the users by unconfirmed emails', () => {
|
||||
expect(wrapper.vm.searchResult).toHaveLength(0)
|
||||
it('calls API with filter', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
notActivated: true,
|
||||
isDeleted: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleted Users', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('button.deletedUserSearch').trigger('click')
|
||||
})
|
||||
|
||||
it('calls API with filter', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
notActivated: false,
|
||||
isDeleted: true,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pagination', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper.setData({ currentPage: 2 })
|
||||
})
|
||||
|
||||
it('calls the API with new page', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 2,
|
||||
pageSize: 25,
|
||||
notActivated: false,
|
||||
isDeleted: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('user search', () => {
|
||||
beforeEach(async () => {
|
||||
wrapper.setData({ criteria: 'search string' })
|
||||
})
|
||||
|
||||
it('calls the API with search string', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: 'search string',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
notActivated: false,
|
||||
isDeleted: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('reset the search field', () => {
|
||||
it('calls the API with empty criteria', async () => {
|
||||
jest.clearAllMocks()
|
||||
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
searchText: '',
|
||||
currentPage: 1,
|
||||
pageSize: 25,
|
||||
notActivated: false,
|
||||
isDeleted: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('delete user', () => {
|
||||
const now = new Date()
|
||||
beforeEach(async () => {
|
||||
wrapper.findComponent({ name: 'SearchUserTable' }).vm.$emit('updateDeletedAt', 4, now)
|
||||
})
|
||||
|
||||
it('marks the user as deleted', () => {
|
||||
expect(wrapper.vm.searchResult.find((obj) => obj.userId === 4).deletedAt).toEqual(now)
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('user_deleted')
|
||||
})
|
||||
})
|
||||
|
||||
@ -74,7 +211,7 @@ describe('UserSearch', () => {
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorMock).toBeCalledWith('Ouch')
|
||||
expect(toastErrorSpy).toBeCalledWith('Ouch')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,94 +1,81 @@
|
||||
<template>
|
||||
<div class="user-search">
|
||||
<div style="text-align: right">
|
||||
<b-button block variant="danger" @click="unconfirmedRegisterMails">
|
||||
<b-icon icon="envelope" variant="light"></b-icon>
|
||||
{{ $t('unregistered_emails') }}
|
||||
<div class="user-search-first-div">
|
||||
<b-button class="unconfirmedRegisterMails" variant="light" @click="unconfirmedRegisterMails">
|
||||
<b-icon icon="envelope" variant="danger"></b-icon>
|
||||
{{ filterCheckedEmails ? $t('all_emails') : $t('unregistered_emails') }}
|
||||
</b-button>
|
||||
<b-button class="deletedUserSearch" variant="light" @click="deletedUserSearch">
|
||||
<b-icon icon="x-circle" variant="danger"></b-icon>
|
||||
{{ filterDeletedUser ? $t('all_emails') : $t('deleted_user') }}
|
||||
</b-button>
|
||||
</div>
|
||||
<label>{{ $t('user_search') }}</label>
|
||||
<b-input
|
||||
type="text"
|
||||
v-model="criteria"
|
||||
class="shadow p-3 mb-3 bg-white rounded"
|
||||
:placeholder="$t('user_search')"
|
||||
@input="getUsers"
|
||||
></b-input>
|
||||
|
||||
<user-table
|
||||
<div>
|
||||
<b-input-group>
|
||||
<b-form-input
|
||||
type="text"
|
||||
class="test-input-criteria"
|
||||
v-model="criteria"
|
||||
:placeholder="$t('user_search')"
|
||||
></b-form-input>
|
||||
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
||||
<b-input-group-text class="pointer">
|
||||
<b-icon icon="x" />
|
||||
</b-input-group-text>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</div>
|
||||
<search-user-table
|
||||
type="PageUserSearch"
|
||||
:itemsUser="searchResult"
|
||||
:fieldsTable="fields"
|
||||
:criteria="criteria"
|
||||
:items="searchResult"
|
||||
:fields="fields"
|
||||
@updateDeletedAt="updateDeletedAt"
|
||||
/>
|
||||
<b-pagination
|
||||
pills
|
||||
size="lg"
|
||||
v-model="currentPage"
|
||||
per-page="perPage"
|
||||
:total-rows="rows"
|
||||
align="center"
|
||||
></b-pagination>
|
||||
<div></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import UserTable from '../components/UserTable.vue'
|
||||
import SearchUserTable from '../components/Tables/SearchUserTable.vue'
|
||||
import { searchUsers } from '../graphql/searchUsers'
|
||||
import { creationMonths } from '../mixins/creationMonths'
|
||||
|
||||
export default {
|
||||
name: 'UserSearch',
|
||||
mixins: [creationMonths],
|
||||
components: {
|
||||
UserTable,
|
||||
SearchUserTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showArrays: false,
|
||||
fields: [
|
||||
{ key: 'email', label: this.$t('e_mail') },
|
||||
{ key: 'firstName', label: this.$t('firstname') },
|
||||
{ key: 'lastName', label: this.$t('lastname') },
|
||||
{
|
||||
key: 'creation',
|
||||
label: this.$t('open_creation'),
|
||||
formatter: (value, key, item) => {
|
||||
return (
|
||||
`
|
||||
<div>` +
|
||||
this.$moment().subtract(2, 'month').format('MMMM') +
|
||||
` - ` +
|
||||
String(value[0]) +
|
||||
` GDD</div>
|
||||
<div>` +
|
||||
this.$moment().subtract(1, 'month').format('MMMM') +
|
||||
` - ` +
|
||||
String(value[1]) +
|
||||
` GDD</div>
|
||||
<div>` +
|
||||
this.$moment().format('MMMM') +
|
||||
` - ` +
|
||||
String(value[2]) +
|
||||
` GDD</div>
|
||||
`
|
||||
)
|
||||
},
|
||||
},
|
||||
{ key: 'show_details', label: this.$t('details') },
|
||||
{ key: 'confirm_mail', label: this.$t('confirmed') },
|
||||
{ key: 'transactions_list', label: this.$t('transaction') },
|
||||
],
|
||||
searchResult: [],
|
||||
massCreation: [],
|
||||
criteria: '',
|
||||
currentMonth: {
|
||||
short: this.$moment().format('MMMM'),
|
||||
},
|
||||
lastMonth: {
|
||||
short: this.$moment().subtract(1, 'month').format('MMMM'),
|
||||
},
|
||||
beforeLastMonth: {
|
||||
short: this.$moment().subtract(2, 'month').format('MMMM'),
|
||||
},
|
||||
filterCheckedEmails: false,
|
||||
filterDeletedUser: false,
|
||||
rows: 0,
|
||||
currentPage: 1,
|
||||
perPage: 25,
|
||||
now: Date.now(),
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
unconfirmedRegisterMails() {
|
||||
this.searchResult = this.searchResult.filter((user) => {
|
||||
return user.emailChecked
|
||||
})
|
||||
this.filterCheckedEmails = !this.filterCheckedEmails
|
||||
this.getUsers()
|
||||
},
|
||||
deletedUserSearch() {
|
||||
this.filterDeletedUser = !this.filterDeletedUser
|
||||
this.getUsers()
|
||||
},
|
||||
getUsers() {
|
||||
this.$apollo
|
||||
@ -96,18 +83,61 @@ export default {
|
||||
query: searchUsers,
|
||||
variables: {
|
||||
searchText: this.criteria,
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.perPage,
|
||||
notActivated: this.filterCheckedEmails,
|
||||
isDeleted: this.filterDeletedUser,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
this.searchResult = result.data.searchUsers
|
||||
this.rows = result.data.searchUsers.userCount
|
||||
this.searchResult = result.data.searchUsers.userList
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
updateDeletedAt(userId, deletedAt) {
|
||||
this.searchResult.find((obj) => obj.userId === userId).deletedAt = deletedAt
|
||||
this.toastSuccess(deletedAt ? this.$t('user_deleted') : this.$t('user_recovered'))
|
||||
},
|
||||
},
|
||||
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>
|
||||
.user-search-first-div {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,8 +2,6 @@ import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
|
||||
import VueApollo from 'vue-apollo'
|
||||
import CONFIG from '../config'
|
||||
import store from '../store/store'
|
||||
import router from '../router/router'
|
||||
import i18n from '../i18n'
|
||||
|
||||
const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI })
|
||||
|
||||
@ -16,9 +14,8 @@ const authLink = new ApolloLink((operation, forward) => {
|
||||
})
|
||||
return forward(operation).map((response) => {
|
||||
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
|
||||
response.errors[0].message = i18n.t('error.session-expired')
|
||||
store.dispatch('logout', null)
|
||||
if (router.currentRoute.path !== '/logout') router.push('/logout')
|
||||
window.location.assign(CONFIG.WALLET_URL)
|
||||
return response
|
||||
}
|
||||
const newToken = operation.getContext().response.headers.get('token')
|
||||
|
||||
@ -4,12 +4,10 @@ import CONFIG from '../config'
|
||||
|
||||
import VueApollo from 'vue-apollo'
|
||||
import store from '../store/store'
|
||||
import router from '../router/router'
|
||||
import i18n from '../i18n'
|
||||
|
||||
jest.mock('vue-apollo')
|
||||
jest.mock('../store/store')
|
||||
jest.mock('../router/router')
|
||||
jest.mock('../i18n')
|
||||
|
||||
jest.mock('apollo-boost', () => {
|
||||
@ -59,13 +57,11 @@ describe('apolloProvider', () => {
|
||||
errors: [{ message: '403.13 - Client certificate revoked' }],
|
||||
}
|
||||
|
||||
// mock router
|
||||
const routerPushMock = jest.fn()
|
||||
router.push = routerPushMock
|
||||
router.currentRoute = {
|
||||
path: '/overview',
|
||||
const windowLocationMock = jest.fn()
|
||||
delete window.location
|
||||
window.location = {
|
||||
assign: windowLocationMock,
|
||||
}
|
||||
|
||||
// mock context
|
||||
const setContextMock = jest.fn()
|
||||
const getContextMock = jest.fn(() => {
|
||||
@ -128,21 +124,8 @@ describe('apolloProvider', () => {
|
||||
expect(storeDispatchMock).toBeCalledWith('logout', null)
|
||||
})
|
||||
|
||||
describe('current route is not logout', () => {
|
||||
it('redirects to logout', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/logout')
|
||||
})
|
||||
})
|
||||
|
||||
describe('current route is logout', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
router.currentRoute.path = '/logout'
|
||||
})
|
||||
|
||||
it('does not redirect to logout', () => {
|
||||
expect(routerPushMock).not.toBeCalled()
|
||||
})
|
||||
it('redirects to logout', () => {
|
||||
expect(windowLocationMock).toBeCalledWith('http://localhost/login')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { verifyLogin } from '../graphql/verifyLogin'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const addNavigationGuards = (router, store, apollo) => {
|
||||
const addNavigationGuards = (router, store, apollo, i18n) => {
|
||||
// store token on `authenticate`
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
if (to.path === '/authenticate' && to.query && to.query.token) {
|
||||
@ -14,6 +14,7 @@ const addNavigationGuards = (router, store, apollo) => {
|
||||
.then((result) => {
|
||||
const moderator = result.data.verifyLogin
|
||||
if (moderator.isAdmin) {
|
||||
i18n.locale = moderator.language
|
||||
store.commit('moderator', moderator)
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
|
||||
@ -6,9 +6,11 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
verifyLogin: {
|
||||
isAdmin: true,
|
||||
language: 'de',
|
||||
},
|
||||
},
|
||||
})
|
||||
const i18nLocaleMock = jest.fn()
|
||||
|
||||
const store = {
|
||||
commit: storeCommitMock,
|
||||
@ -21,7 +23,11 @@ const apollo = {
|
||||
query: apolloQueryMock,
|
||||
}
|
||||
|
||||
addNavigationGuards(router, store, apollo)
|
||||
const i18n = {
|
||||
locale: i18nLocaleMock,
|
||||
}
|
||||
|
||||
addNavigationGuards(router, store, apollo, i18n)
|
||||
|
||||
describe('navigation guards', () => {
|
||||
beforeEach(() => {
|
||||
@ -33,19 +39,23 @@ describe('navigation guards', () => {
|
||||
const next = jest.fn()
|
||||
|
||||
describe('with valid token and as admin', () => {
|
||||
beforeEach(() => {
|
||||
navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
|
||||
beforeEach(async () => {
|
||||
await navGuard({ path: '/authenticate', query: { token: 'valid-token' } }, {}, next)
|
||||
})
|
||||
|
||||
it('commits the token to the store', async () => {
|
||||
it('commits the token to the store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('token', 'valid-token')
|
||||
})
|
||||
|
||||
it('commits the moderator to the store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('moderator', { isAdmin: true })
|
||||
it.skip('sets the locale', () => {
|
||||
expect(i18nLocaleMock).toBeCalledWith('de')
|
||||
})
|
||||
|
||||
it('redirects to /', async () => {
|
||||
it('commits the moderator to the store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('moderator', { isAdmin: true, language: 'de' })
|
||||
})
|
||||
|
||||
it('redirects to /', () => {
|
||||
expect(next).toBeCalledWith({ path: '/' })
|
||||
})
|
||||
})
|
||||
|
||||
@ -24,6 +24,9 @@ export const mutations = {
|
||||
moderator: (state, moderator) => {
|
||||
state.moderator = moderator
|
||||
},
|
||||
setUserSelectedInMassCreation: (state, userSelectedInMassCreation) => {
|
||||
state.userSelectedInMassCreation = userSelectedInMassCreation
|
||||
},
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
@ -44,6 +47,7 @@ const store = new Vuex.Store({
|
||||
token: CONFIG.DEBUG_DISABLE_AUTH ? 'validToken' : null,
|
||||
moderator: null,
|
||||
openCreations: 0,
|
||||
userSelectedInMassCreation: [],
|
||||
},
|
||||
// Syncronous mutation of the state
|
||||
mutations,
|
||||
|
||||
@ -10,6 +10,7 @@ const {
|
||||
resetOpenCreations,
|
||||
setOpenCreations,
|
||||
moderator,
|
||||
setUserSelectedInMassCreation,
|
||||
} = mutations
|
||||
const { logout } = actions
|
||||
|
||||
@ -64,6 +65,14 @@ describe('Vuex store', () => {
|
||||
expect(state.openCreations).toEqual(12)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setUserSelectedInMassCreation', () => {
|
||||
it('sets userSelectedInMassCreation to given value', () => {
|
||||
const state = { userSelectedInMassCreation: [] }
|
||||
setUserSelectedInMassCreation(state, [0, 1, 2])
|
||||
expect(state.userSelectedInMassCreation).toEqual([0, 1, 2])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('actions', () => {
|
||||
|
||||
@ -5,11 +5,18 @@ import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||
// without this async calls are not working
|
||||
import 'regenerator-runtime'
|
||||
|
||||
import { toasters } from '../src/mixins/toaster'
|
||||
|
||||
export const toastErrorSpy = jest.spyOn(toasters.methods, 'toastError')
|
||||
export const toastSuccessSpy = jest.spyOn(toasters.methods, 'toastSuccess')
|
||||
|
||||
global.localVue = createLocalVue()
|
||||
|
||||
global.localVue.use(BootstrapVue)
|
||||
global.localVue.use(IconsPlugin)
|
||||
|
||||
global.localVue.mixin(toasters)
|
||||
|
||||
// throw errors for vue warnings to force the programmers to take care about warnings
|
||||
Vue.config.warnHandler = (w) => {
|
||||
throw new Error(w)
|
||||
|
||||
@ -2,11 +2,12 @@ const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const Dotenv = require('dotenv-webpack')
|
||||
const StatsPlugin = require('stats-webpack-plugin')
|
||||
const CONFIG = require('./src/config')
|
||||
|
||||
// vue.config.js
|
||||
module.exports = {
|
||||
devServer: {
|
||||
port: process.env.PORT || 8080,
|
||||
port: CONFIG.PORT,
|
||||
},
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
@ -14,6 +15,7 @@ module.exports = {
|
||||
fallbackLocale: 'de',
|
||||
localeDir: 'locales',
|
||||
enableInSFC: false,
|
||||
enableLegacy: false,
|
||||
},
|
||||
},
|
||||
lintOnSave: true,
|
||||
@ -33,7 +35,7 @@ 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(process.env.BUILD_COMMIT),
|
||||
'process.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
|
||||
@ -45,7 +47,7 @@ module.exports = {
|
||||
},
|
||||
css: {
|
||||
// Enable CSS source maps.
|
||||
sourceMap: process.env.NODE_ENV !== 'production',
|
||||
sourceMap: CONFIG.NODE_ENV !== 'production',
|
||||
},
|
||||
outputDir: path.resolve(__dirname, './dist'),
|
||||
}
|
||||
|
||||
812
admin/yarn.lock
812
admin/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -1,36 +1,48 @@
|
||||
CONFIG_VERSION=v1.2022-03-18
|
||||
|
||||
# Server
|
||||
PORT=4000
|
||||
JWT_SECRET=secret123
|
||||
JWT_EXPIRES_IN=10m
|
||||
GRAPHIQL=false
|
||||
GDT_API_URL=https://gdt.gradido.net
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_community
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
|
||||
#EMAIL=true
|
||||
#EMAIL_USERNAME=
|
||||
#EMAIL_SENDER=
|
||||
#EMAIL_PASSWORD=
|
||||
#EMAIL_SMTP_URL=
|
||||
#EMAIL_SMTP_PORT=587
|
||||
#RESEND_TIME=1 minute, 60 => 1hour, 1440 (60 minutes * 24 hours) => 24 hours
|
||||
#RESEND_TIME=
|
||||
RESEND_TIME=10
|
||||
# Klicktipp
|
||||
KLICKTIPP=false
|
||||
KLICKTTIPP_API_URL=https://api.klicktipp.com
|
||||
KLICKTIPP_USER=gradido_test
|
||||
KLICKTIPP_PASSWORD=secret321
|
||||
KLICKTIPP_APIKEY_DE=SomeFakeKeyDE
|
||||
KLICKTIPP_APIKEY_EN=SomeFakeKeyEN
|
||||
|
||||
#EMAIL_LINK_VERIFICATION=http://localhost/vue/checkEmail/$1
|
||||
# Community
|
||||
COMMUNITY_NAME=Gradido Entwicklung
|
||||
COMMUNITY_URL=http://localhost/
|
||||
COMMUNITY_REGISTER_URL=http://localhost/register
|
||||
COMMUNITY_DESCRIPTION=Die lokale Entwicklungsumgebung von Gradido.
|
||||
|
||||
#KLICKTIPP_USER=
|
||||
#KLICKTIPP_PASSWORD=
|
||||
#KLICKTIPP_APIKEY_DE=
|
||||
#KLICKTIPP_APIKEY_EN=
|
||||
#KLICKTIPP=true
|
||||
COMMUNITY_NAME=
|
||||
COMMUNITY_URL=
|
||||
COMMUNITY_REGISTER_URL=
|
||||
COMMUNITY_DESCRIPTION=
|
||||
# Login Server
|
||||
LOGIN_APP_SECRET=21ffbbc616fe
|
||||
LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
||||
|
||||
# EMail
|
||||
EMAIL=false
|
||||
EMAIL_USERNAME=gradido_email
|
||||
EMAIL_SENDER=info@gradido.net
|
||||
EMAIL_PASSWORD=xxx
|
||||
EMAIL_SMTP_URL=gmail.com
|
||||
EMAIL_SMTP_PORT=587
|
||||
EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{optin}{code}
|
||||
EMAIL_LINK_SETPASSWORD=http://localhost/reset/{optin}
|
||||
EMAIL_CODE_VALID_TIME=10
|
||||
|
||||
# Webhook
|
||||
WEBHOOK_ELOPAGE_SECRET=secret
|
||||
48
backend/.env.template
Normal file
48
backend/.env.template
Normal file
@ -0,0 +1,48 @@
|
||||
CONFIG_VERSION=$BACKEND_CONFIG_VERSION
|
||||
|
||||
# Server
|
||||
JWT_SECRET=$JWT_SECRET
|
||||
JWT_EXPIRES_IN=10m
|
||||
GRAPHIQL=false
|
||||
GDT_API_URL=$GDT_API_URL
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=$DB_USER
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
DB_DATABASE=gradido_community
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=$TYPEORM_LOGGING_RELATIVE_PATH
|
||||
|
||||
# Klicktipp
|
||||
KLICKTIPP=$KLICKTIPP
|
||||
KLICKTTIPP_API_URL=https://api.klicktipp.com
|
||||
KLICKTIPP_USER=$KLICKTIPP_USER
|
||||
KLICKTIPP_PASSWORD=$KLICKTIPP_PASSWORD
|
||||
KLICKTIPP_APIKEY_DE=$KLICKTIPP_APIKEY_DE
|
||||
KLICKTIPP_APIKEY_EN=$KLICKTIPP_APIKEY_EN
|
||||
|
||||
# Community
|
||||
COMMUNITY_NAME=$COMMUNITY_NAME
|
||||
COMMUNITY_URL=$COMMUNITY_URL
|
||||
COMMUNITY_REGISTER_URL=$COMMUNITY_REGISTER_URL
|
||||
COMMUNITY_DESCRIPTION=$COMMUNITY_DESCRIPTION
|
||||
|
||||
# Login Server
|
||||
LOGIN_APP_SECRET=21ffbbc616fe
|
||||
LOGIN_SERVER_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
||||
|
||||
# EMail
|
||||
RESEND_TIME=10
|
||||
EMAIL=$EMAIL
|
||||
EMAIL_USERNAME=$EMAIL_USERNAME
|
||||
EMAIL_SENDER=$EMAIL_SENDER
|
||||
EMAIL_PASSWORD=$EMAIL_PASSWORD
|
||||
EMAIL_SMTP_URL=$EMAIL_SMTP_URL
|
||||
EMAIL_SMTP_PORT=587
|
||||
EMAIL_LINK_VERIFICATION=$EMAIL_LINK_VERIFICATION
|
||||
EMAIL_LINK_SETPASSWORD=$EMAIL_LINK_SETPASSWORD
|
||||
RESEND_TIME=10
|
||||
|
||||
# Webhook
|
||||
WEBHOOK_ELOPAGE_SECRET=$WEBHOOK_ELOPAGE_SECRET
|
||||
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
/node_modules/
|
||||
/.env
|
||||
/.env.bak
|
||||
/build/
|
||||
package-json.lock
|
||||
coverage
|
||||
|
||||
@ -3,10 +3,20 @@ module.exports = {
|
||||
verbose: true,
|
||||
preset: 'ts-jest',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||
moduleNameMapper: {
|
||||
'@entity/(.*)': '<rootDir>/../database/build/entity/$1',
|
||||
// This is hack to fix a problem with the library `ts-mysql-migrate` which does differentiate between its ts/js state
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
'@model/(.*)': '<rootDir>/src/graphql/model/$1',
|
||||
'@arg/(.*)': '<rootDir>/src/graphql/arg/$1',
|
||||
'@enum/(.*)': '<rootDir>/src/graphql/enum/$1',
|
||||
'@repository/(.*)': '<rootDir>/src/typeorm/repository/$1',
|
||||
'@test/(.*)': '<rootDir>/test/$1',
|
||||
'@entity/(.*)':
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/entity/$1'
|
||||
: '<rootDir>/../database/build/entity/$1',
|
||||
'@dbTools/(.*)':
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../database/src/$1'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gradido-backend",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.6",
|
||||
"description": "Gradido unified backend providing an API-Service for Gradido Transactions",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/backend",
|
||||
@ -10,38 +10,39 @@
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "tsc --build --clean",
|
||||
"start": "node build/index.js",
|
||||
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"CI_worklfow_test": "jest --runInBand --coverage ",
|
||||
"test": "NODE_ENV=development jest --runInBand --coverage "
|
||||
"start": "TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
||||
"dev": "TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
||||
"test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||
"seed": "TZ=UTC ts-node -r tsconfig-paths/register src/seeds/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/lodash.clonedeep": "^4.5.6",
|
||||
"apollo-log": "^1.1.0",
|
||||
"apollo-server-express": "^2.25.2",
|
||||
"apollo-server-testing": "^2.25.2",
|
||||
"axios": "^0.21.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"class-validator": "^0.13.1",
|
||||
"cors": "^2.8.5",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^15.5.1",
|
||||
"jest": "^27.2.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"module-alias": "^2.2.2",
|
||||
"moment": "^2.29.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"mysql2": "^2.3.0",
|
||||
"nodemailer": "^6.6.5",
|
||||
"random-bigint": "^0.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sodium-native": "^3.3.0",
|
||||
"ts-jest": "^27.0.5",
|
||||
"type-graphql": "^1.1.1",
|
||||
"typeorm": "^0.2.38"
|
||||
"type-graphql": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
"@types/faker": "^5.5.9",
|
||||
"@types/jsonwebtoken": "^8.5.2",
|
||||
"@types/node": "^16.10.3",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
@ -54,13 +55,11 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"faker": "^5.5.3",
|
||||
"nodemon": "^2.0.7",
|
||||
"prettier": "^2.3.1",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "^3.14.0",
|
||||
"typescript": "^4.3.4"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@entity": "../database/build/entity",
|
||||
"@dbTools": "../database/build/src"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { KlicktippConnector } from './klicktippConnector'
|
||||
import CONFIG from '../config'
|
||||
import CONFIG from '@/config'
|
||||
|
||||
const klicktippConnector = new KlicktippConnector()
|
||||
|
||||
export const signIn = async (
|
||||
export const klicktippSignIn = async (
|
||||
email: string,
|
||||
language: string,
|
||||
firstName?: string,
|
||||
|
||||
@ -7,5 +7,6 @@ export const INALIENABLE_RIGHTS = [
|
||||
RIGHTS.CREATE_USER,
|
||||
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
|
||||
RIGHTS.SET_PASSWORD,
|
||||
RIGHTS.CHECK_USERNAME,
|
||||
RIGHTS.QUERY_TRANSACTION_LINK,
|
||||
RIGHTS.QUERY_OPT_IN,
|
||||
]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import CONFIG from '../config/'
|
||||
import CONFIG from '@/config/'
|
||||
import { CustomJwtPayload } from './CustomJwtPayload'
|
||||
|
||||
export const decode = (token: string): CustomJwtPayload | null => {
|
||||
|
||||
@ -16,9 +16,14 @@ export enum RIGHTS {
|
||||
CREATE_USER = 'CREATE_USER',
|
||||
SEND_RESET_PASSWORD_EMAIL = 'SEND_RESET_PASSWORD_EMAIL',
|
||||
SET_PASSWORD = 'SET_PASSWORD',
|
||||
QUERY_OPT_IN = 'QUERY_OPT_IN',
|
||||
UPDATE_USER_INFOS = 'UPDATE_USER_INFOS',
|
||||
CHECK_USERNAME = 'CHECK_USERNAME',
|
||||
HAS_ELOPAGE = 'HAS_ELOPAGE',
|
||||
CREATE_TRANSACTION_LINK = 'CREATE_TRANSACTION_LINK',
|
||||
DELETE_TRANSACTION_LINK = 'DELETE_TRANSACTION_LINK',
|
||||
QUERY_TRANSACTION_LINK = 'QUERY_TRANSACTION_LINK',
|
||||
REDEEM_TRANSACTION_LINK = 'REDEEM_TRANSACTION_LINK',
|
||||
LIST_TRANSACTION_LINKS = 'LIST_TRANSACTION_LINKS',
|
||||
// Admin
|
||||
SEARCH_USERS = 'SEARCH_USERS',
|
||||
CREATE_PENDING_CREATION = 'CREATE_PENDING_CREATION',
|
||||
@ -26,4 +31,8 @@ export enum RIGHTS {
|
||||
SEARCH_PENDING_CREATION = 'SEARCH_PENDING_CREATION',
|
||||
DELETE_PENDING_CREATION = 'DELETE_PENDING_CREATION',
|
||||
CONFIRM_PENDING_CREATION = 'CONFIRM_PENDING_CREATION',
|
||||
SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL',
|
||||
DELETE_USER = 'DELETE_USER',
|
||||
UNDELETE_USER = 'UNDELETE_USER',
|
||||
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
|
||||
}
|
||||
|
||||
@ -18,6 +18,10 @@ export const ROLE_USER = new Role('user', [
|
||||
RIGHTS.LOGOUT,
|
||||
RIGHTS.UPDATE_USER_INFOS,
|
||||
RIGHTS.HAS_ELOPAGE,
|
||||
RIGHTS.CREATE_TRANSACTION_LINK,
|
||||
RIGHTS.DELETE_TRANSACTION_LINK,
|
||||
RIGHTS.REDEEM_TRANSACTION_LINK,
|
||||
RIGHTS.LIST_TRANSACTION_LINKS,
|
||||
])
|
||||
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
|
||||
|
||||
|
||||
9
backend/src/config/index.test.ts
Normal file
9
backend/src/config/index.test.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import CONFIG from './index'
|
||||
|
||||
describe('config/index', () => {
|
||||
describe('decay start block', () => {
|
||||
it('has the correct date set', () => {
|
||||
expect(CONFIG.DECAY_START_TIME).toEqual(new Date('2021-05-13 17:46:31'))
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,8 +1,24 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
import Decimal from 'decimal.js-light'
|
||||
dotenv.config()
|
||||
|
||||
Decimal.set({
|
||||
precision: 25,
|
||||
rounding: Decimal.ROUND_HALF_UP,
|
||||
})
|
||||
|
||||
const constants = {
|
||||
DB_VERSION: '0033-add_referrer_id',
|
||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v1.2022-03-18',
|
||||
CURRENT: '',
|
||||
},
|
||||
}
|
||||
|
||||
const server = {
|
||||
PORT: process.env.PORT || 4000,
|
||||
JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
||||
@ -18,6 +34,7 @@ const database = {
|
||||
DB_USER: process.env.DB_USER || 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD || '',
|
||||
DB_DATABASE: process.env.DB_DATABASE || 'gradido_community',
|
||||
TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.backend.log',
|
||||
}
|
||||
|
||||
const klicktipp = {
|
||||
@ -31,8 +48,8 @@ const klicktipp = {
|
||||
|
||||
const community = {
|
||||
COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung',
|
||||
COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/vue/',
|
||||
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/vue/register',
|
||||
COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/',
|
||||
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register',
|
||||
COMMUNITY_DESCRIPTION:
|
||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
||||
}
|
||||
@ -42,7 +59,6 @@ const loginServer = {
|
||||
LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a',
|
||||
}
|
||||
|
||||
const resendTime = parseInt(process.env.RESEND_TIME ? process.env.RESEND_TIME : 'null')
|
||||
const email = {
|
||||
EMAIL: process.env.EMAIL === 'true' || false,
|
||||
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
|
||||
@ -51,19 +67,36 @@ const email = {
|
||||
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com',
|
||||
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587',
|
||||
EMAIL_LINK_VERIFICATION:
|
||||
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/vue/checkEmail/$1',
|
||||
EMAIL_LINK_SETPASSWORD: process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/vue/reset/$1',
|
||||
RESEND_TIME: isNaN(resendTime) ? 10 : resendTime,
|
||||
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}',
|
||||
EMAIL_LINK_SETPASSWORD:
|
||||
process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset-password/{optin}',
|
||||
EMAIL_CODE_VALID_TIME: process.env.EMAIL_CODE_VALID_TIME
|
||||
? parseInt(process.env.EMAIL_CODE_VALID_TIME) || 10
|
||||
: 10,
|
||||
}
|
||||
|
||||
const webhook = {
|
||||
// Elopage
|
||||
WEBHOOK_ELOPAGE_SECRET: process.env.WEBHOOK_ELOPAGE_SECRET || 'secret',
|
||||
}
|
||||
|
||||
// This is needed by graphql-directive-auth
|
||||
process.env.APP_SECRET = server.JWT_SECRET
|
||||
|
||||
// Check config version
|
||||
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
|
||||
if (
|
||||
![constants.CONFIG_VERSION.EXPECTED, constants.CONFIG_VERSION.DEFAULT].includes(
|
||||
constants.CONFIG_VERSION.CURRENT,
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
`Fatal: Config Version incorrect - expected "${constants.CONFIG_VERSION.EXPECTED}" or "${constants.CONFIG_VERSION.DEFAULT}", but found "${constants.CONFIG_VERSION.CURRENT}"`,
|
||||
)
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
...constants,
|
||||
...server,
|
||||
...database,
|
||||
...klicktipp,
|
||||
|
||||
1
backend/src/config/mnemonic.uncompressed_buffer13116.txt
Normal file
1
backend/src/config/mnemonic.uncompressed_buffer13116.txt
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class CheckUsernameArgs {
|
||||
@Field(() => String)
|
||||
username: string
|
||||
}
|
||||
@ -16,4 +16,7 @@ export default class CreateUserArgs {
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
publisherId: number
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
redeemCode?: string | null
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
import { Order } from '../enum/Order'
|
||||
import { Order } from '@enum/Order'
|
||||
|
||||
@ArgsType()
|
||||
export default class Paginated {
|
||||
@ -11,10 +11,4 @@ export default class Paginated {
|
||||
|
||||
@Field(() => Order, { nullable: true })
|
||||
order?: Order
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
onlyCreations?: boolean
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
userId?: number
|
||||
}
|
||||
|
||||
19
backend/src/graphql/arg/SearchUsersArgs.ts
Normal file
19
backend/src/graphql/arg/SearchUsersArgs.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class SearchUsersArgs {
|
||||
@Field(() => String)
|
||||
searchText: string
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
currentPage?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
pageSize?: number
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
notActivated?: boolean
|
||||
|
||||
@Field(() => Boolean, { nullable: true })
|
||||
isDeleted?: boolean
|
||||
}
|
||||
11
backend/src/graphql/arg/TransactionLinkArgs.ts
Normal file
11
backend/src/graphql/arg/TransactionLinkArgs.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
@ArgsType()
|
||||
export default class TransactionLinkArgs {
|
||||
@Field(() => Decimal)
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
@ArgsType()
|
||||
export default class TransactionSendArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
amount: number
|
||||
@Field(() => Decimal)
|
||||
amount: Decimal
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
|
||||
@ -8,12 +8,6 @@ export default class UpdateUserInfosArgs {
|
||||
@Field({ nullable: true })
|
||||
lastName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
description?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
username?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
language?: string
|
||||
|
||||
|
||||
@ -2,35 +2,45 @@
|
||||
|
||||
import { AuthChecker } from 'type-graphql'
|
||||
|
||||
import { decode, encode } from '../../auth/JWT'
|
||||
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '../../auth/ROLES'
|
||||
import { RIGHTS } from '../../auth/RIGHTS'
|
||||
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
|
||||
import { getCustomRepository } from 'typeorm'
|
||||
import { UserRepository } from '../../typeorm/repository/User'
|
||||
import { decode, encode } from '@/auth/JWT'
|
||||
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '@/auth/ROLES'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { getCustomRepository } from '@dbTools/typeorm'
|
||||
import { UserRepository } from '@repository/User'
|
||||
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
|
||||
import { ServerUser } from '@entity/ServerUser'
|
||||
|
||||
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
||||
context.role = ROLE_UNAUTHORIZED // unauthorized user
|
||||
|
||||
// Do we have a token?
|
||||
if (context.token) {
|
||||
const decoded = decode(context.token)
|
||||
if (!decoded) {
|
||||
// we always throw on an invalid token
|
||||
throw new Error('403.13 - Client certificate revoked')
|
||||
}
|
||||
// Set context pubKey
|
||||
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
|
||||
// set new header token
|
||||
// TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
|
||||
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
|
||||
const userRepository = await getCustomRepository(UserRepository)
|
||||
const user = await userRepository.findByPubkeyHex(context.pubKey)
|
||||
const serverUserRepository = await getCustomRepository(ServerUserRepository)
|
||||
const countServerUsers = await serverUserRepository.count({ email: user.email })
|
||||
context.role = countServerUsers > 0 ? ROLE_ADMIN : ROLE_USER
|
||||
// is rights an inalienable right?
|
||||
if ((<RIGHTS[]>rights).reduce((acc, right) => acc && INALIENABLE_RIGHTS.includes(right), true))
|
||||
return true
|
||||
|
||||
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
|
||||
// Do we have a token?
|
||||
if (!context.token) {
|
||||
throw new Error('401 Unauthorized')
|
||||
}
|
||||
|
||||
// Decode the token
|
||||
const decoded = decode(context.token)
|
||||
if (!decoded) {
|
||||
throw new Error('403.13 - Client certificate revoked')
|
||||
}
|
||||
// Set context pubKey
|
||||
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
|
||||
|
||||
// TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
|
||||
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
|
||||
const userRepository = await getCustomRepository(UserRepository)
|
||||
try {
|
||||
const user = await userRepository.findByPubkeyHex(context.pubKey)
|
||||
context.user = user
|
||||
const countServerUsers = await ServerUser.count({ email: user.email })
|
||||
context.role = countServerUsers > 0 ? ROLE_ADMIN : ROLE_USER
|
||||
} catch {
|
||||
// in case the database query fails (user deleted)
|
||||
throw new Error('401 Unauthorized')
|
||||
}
|
||||
|
||||
// check for correct rights
|
||||
@ -39,6 +49,8 @@ const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
||||
throw new Error('401 Unauthorized')
|
||||
}
|
||||
|
||||
// set new header token
|
||||
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,10 @@ import { registerEnumType } from 'type-graphql'
|
||||
export enum TransactionTypeId {
|
||||
CREATION = 1,
|
||||
SEND = 2,
|
||||
RECEIVE = 3,
|
||||
// This is a virtual property, never occurring on the database
|
||||
DECAY = 4,
|
||||
LINK_SUMMARY = 5,
|
||||
}
|
||||
|
||||
registerEnumType(TransactionTypeId, {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user