diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..01c8b1b48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +**/node_modules +**/build +**/coverage \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 575533e5c..ec437ad37 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,7 +38,7 @@ jobs: docker build --target production -t "gradido/frontend:latest" -t "gradido/frontend:production" -t "gradido/frontend:${VERSION}" -t "gradido/frontend:${BUILD_VERSION}" frontend/ docker save "gradido/frontend" > /tmp/frontend.tar - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: docker-frontend-production path: /tmp/frontend.tar @@ -75,7 +75,7 @@ jobs: docker build -f ./backend/Dockerfile --target production -t "gradido/backend:latest" -t "gradido/backend:production" -t "gradido/backend:${VERSION}" -t "gradido/backend:${BUILD_VERSION}" . docker save "gradido/backend" > /tmp/backend.tar - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: docker-backend-production path: /tmp/backend.tar @@ -101,7 +101,7 @@ jobs: docker build --target production_up -t "gradido/database:production_up" database/ docker save "gradido/database:production_up" > /tmp/database_up.tar - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: docker-database-production_up path: /tmp/database_up.tar @@ -138,7 +138,7 @@ jobs: docker build -t "gradido/mariadb:latest" -t "gradido/mariadb:production" -t "gradido/mariadb:${VERSION}" -t "gradido/mariadb:${BUILD_VERSION}" -f ./mariadb/Dockerfile ./ docker save "gradido/mariadb" > /tmp/mariadb.tar - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: docker-mariadb-production path: /tmp/mariadb.tar @@ -175,7 +175,7 @@ jobs: 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 + uses: actions/upload-artifact@v4 with: name: docker-nginx-production path: /tmp/nginx.tar @@ -200,35 +200,35 @@ jobs: # DOWNLOAD DOCKER IMAGES ################################################# ########################################################################## - name: Download Docker Image (Frontend) - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: docker-frontend-production path: /tmp - name: Load Docker Image run: docker load < /tmp/frontend.tar - name: Download Docker Image (Backend) - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: docker-backend-production path: /tmp - name: Load Docker Image run: docker load < /tmp/backend.tar - name: Download Docker Image (Database) - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: docker-database-production_up path: /tmp - name: Load Docker Image run: docker load < /tmp/database_up.tar - name: Download Docker Image (MariaDB) - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: docker-mariadb-production path: /tmp - name: Load Docker Image run: docker load < /tmp/mariadb.tar - name: Download Docker Image (Nginx) - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: docker-nginx-production path: /tmp diff --git a/.github/workflows/test_admin_interface.yml b/.github/workflows/test_admin_interface.yml index 160c819d2..d57c78ef8 100644 --- a/.github/workflows/test_admin_interface.yml +++ b/.github/workflows/test_admin_interface.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Admin Interface | Build 'test' image - run: docker build --target test -t "gradido/admin:test" admin/ --build-arg NODE_ENV="test" + run: docker build -f ./admin/Dockerfile --target test -t "gradido/admin:test" --build-arg NODE_ENV="test" . unit_test: if: needs.files-changed.outputs.admin == 'true' diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index ed20f91bb..8d1a8e2c4 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -54,7 +54,7 @@ jobs: run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database - name: Backend | Unit tests - run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test + run: cd database && yarn && yarn build && cd ../config && yarn install && cd ../backend && yarn && yarn test lint: if: needs.files-changed.outputs.backend == 'true' @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v3 - name: Backend | Lint - run: cd database && yarn && cd ../backend && yarn && yarn run lint + run: cd database && yarn && cd ../config && yarn install && cd ../backend && yarn && yarn run lint locales: if: needs.files-changed.outputs.backend == 'true' diff --git a/.github/workflows/test_dht_node.yml b/.github/workflows/test_dht_node.yml index acd19bb57..2bbbd5e6d 100644 --- a/.github/workflows/test_dht_node.yml +++ b/.github/workflows/test_dht_node.yml @@ -36,7 +36,7 @@ jobs: docker save "gradido/dht-node:test" > /tmp/dht-node.tar - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docker-dht-node-test path: /tmp/dht-node.tar @@ -51,7 +51,7 @@ jobs: uses: actions/checkout@v3 - name: Lint - run: cd database && yarn && cd ../dht-node && yarn && yarn run lint + run: cd database && yarn && cd ../config && yarn install && cd ../dht-node && yarn && yarn run lint unit_test: name: Unit Tests - DHT Node @@ -63,7 +63,7 @@ jobs: uses: actions/checkout@v3 - name: Download Docker Image - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: docker-dht-node-test path: /tmp diff --git a/.github/workflows/test_dlt_connector.yml b/.github/workflows/test_dlt_connector.yml index 099f0dd1a..0c81188ba 100644 --- a/.github/workflows/test_dlt_connector.yml +++ b/.github/workflows/test_dlt_connector.yml @@ -35,7 +35,7 @@ jobs: docker save "gradido/dlt-connector:test" > /tmp/dlt-connector.tar - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docker-dlt-connector-test path: /tmp/dlt-connector.tar diff --git a/.github/workflows/test_e2e.yml b/.github/workflows/test_e2e.yml index 590758248..3fb92562c 100644 --- a/.github/workflows/test_e2e.yml +++ b/.github/workflows/test_e2e.yml @@ -5,7 +5,7 @@ on: push jobs: end-to-end-tests: name: End-to-End Tests - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@v3 @@ -13,16 +13,6 @@ jobs: - name: Boot up test system | docker-compose mariadb run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb - - name: Boot up test system | docker-compose database - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database - - - name: Boot up test system | docker-compose backend - run: | - cd backend - cp .env.test_e2e .env - cd .. - docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend - - name: Sleep for 10 seconds run: sleep 10s @@ -31,14 +21,17 @@ jobs: sudo chown runner:docker -R * cd database yarn && yarn dev_reset + cd ../config + yarn install cd ../backend yarn && yarn seed - - name: Boot up test system | docker-compose frontends - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx - - - name: Boot up test system | docker-compose mailserver - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver + - name: Boot up test system | docker-compose backend, frontend, admin, nginx, mailserver + run: | + cd backend + cp .env.test_e2e .env + cd .. + docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend frontend admin nginx mailserver - name: End-to-end tests | prepare run: | @@ -68,7 +61,7 @@ jobs: - name: End-to-end tests | if tests failed, upload report id: e2e-report if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cypress-report-pr-#${{ steps.pr.outputs.number }} path: /home/runner/work/gradido/gradido/e2e-tests/cypress/reports/cucumber_html_report diff --git a/.github/workflows/test_federation.yml b/.github/workflows/test_federation.yml index 3fb08ea64..8f5acb344 100644 --- a/.github/workflows/test_federation.yml +++ b/.github/workflows/test_federation.yml @@ -35,7 +35,7 @@ jobs: docker save "gradido/federation:test" > /tmp/federation.tar - name: Upload Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docker-federation-test path: /tmp/federation.tar @@ -62,7 +62,7 @@ jobs: uses: actions/checkout@v3 - name: Download Docker Image - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: docker-federation-test path: /tmp diff --git a/.github/workflows/test_frontend.yml b/.github/workflows/test_frontend.yml index 44137216b..208607fbd 100644 --- a/.github/workflows/test_frontend.yml +++ b/.github/workflows/test_frontend.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3 - name: Frontend | Build 'test' image - run: docker build --target test -t "gradido/frontend:test" frontend/ --build-arg NODE_ENV="test" + run: docker build -f ./frontend/Dockerfile --target test -t "gradido/frontend:test" --build-arg NODE_ENV="test" . unit_test: if: needs.files-changed.outputs.frontend == 'true' diff --git a/.github/workflows/test_mariadb.yml b/.github/workflows/test_mariadb.yml deleted file mode 100644 index fe101bc48..000000000 --- a/.github/workflows/test_mariadb.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Gradido MariaDB Test CI - -on: push - -jobs: - files-changed: - name: Detect File Changes - MariaDB - runs-on: ubuntu-latest - outputs: - mariadb: ${{ steps.changes.outputs.mariadb }} - steps: - - uses: actions/checkout@v3.3.0 - - - name: Check for frontend file changes - uses: dorny/paths-filter@v2.11.1 - id: changes - with: - token: ${{ github.token }} - filters: .github/file-filters.yml - list-files: shell - - build_test: - if: needs.files-changed.outputs.mariadb == 'true' - name: Docker Build Test - MariaDB - needs: files-changed - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: MariaDB | Build 'test' image - run: docker build --target mariadb_server -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./ diff --git a/CHANGELOG.md b/CHANGELOG.md index a84f8909c..3f712b290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,95 @@ 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). +#### [2.4.5](https://github.com/gradido/gradido/compare/2.3.1...2.4.5) + +- feat(backend): add answer button inside E-Mail [`#3431`](https://github.com/gradido/gradido/pull/3431) +- feat(other): build config in deployment [`#3430`](https://github.com/gradido/gradido/pull/3430) +- fix(dht): and federation using config in bare-metal setup [`#3434`](https://github.com/gradido/gradido/pull/3434) +- fix(frontend): fix meta url from env.template [`#3432`](https://github.com/gradido/gradido/pull/3432) +- fix(frontend): fix problems with dev container [`#3429`](https://github.com/gradido/gradido/pull/3429) +- feat(backend): add config validation with joi [`#3422`](https://github.com/gradido/gradido/pull/3422) +- feat(backend): patch getUsername in PublishNameLogic [`#3428`](https://github.com/gradido/gradido/pull/3428) +- feat(admin): resubmission date localized [`#3426`](https://github.com/gradido/gradido/pull/3426) +- refactor(workflow): increaste artifact version [`#3425`](https://github.com/gradido/gradido/pull/3425) +- feat(backend): make sure, correct user name is used for jwt token [`#3424`](https://github.com/gradido/gradido/pull/3424) +- feat(frontend): speedup frontend build time [`#3423`](https://github.com/gradido/gradido/pull/3423) +- chore(release): v2.4.4 beta [`#3421`](https://github.com/gradido/gradido/pull/3421) +- fix(admin): copy of env variables [`#3420`](https://github.com/gradido/gradido/pull/3420) +- fix(backend): toggle for using worker threads for password encryption [`#3418`](https://github.com/gradido/gradido/pull/3418) +- fix(admin): fix resubmission [`#3416`](https://github.com/gradido/gradido/pull/3416) +- feat(backend): run password encryption in worker thread [`#3410`](https://github.com/gradido/gradido/pull/3410) +- feat(frontend): user location with vue3 in cooperation with montreal [`#3397`](https://github.com/gradido/gradido/pull/3397) +- feat(backend): optimize find user query on login [`#3415`](https://github.com/gradido/gradido/pull/3415) +- feat(frontend): show gdt comments [`#3412`](https://github.com/gradido/gradido/pull/3412) +- fix(backend): humhub use first name instead of username field for initalien based alias [`#3403`](https://github.com/gradido/gradido/pull/3403) +- fix(frontend): repair gdt transaction list pagination [`#3409`](https://github.com/gradido/gradido/pull/3409) +- fix(frontend): updated tests [`#3406`](https://github.com/gradido/gradido/pull/3406) +- feat(backend): speed up gdt request with keep alive connections [`#3392`](https://github.com/gradido/gradido/pull/3392) +- fix(frontend): fix contribution data handling [`#3405`](https://github.com/gradido/gradido/pull/3405) +- feat(database): use the same mariadb version everywhere [`#3396`](https://github.com/gradido/gradido/pull/3396) +- feat(frontend): scale down two img [`#3393`](https://github.com/gradido/gradido/pull/3393) +- feat(other): remove docker autostart [`#3395`](https://github.com/gradido/gradido/pull/3395) +- feat(frontend): add feedback fixes + map feature fixes [`#3400`](https://github.com/gradido/gradido/pull/3400) +- feat(other): increase e2e page load timeout [`#3394`](https://github.com/gradido/gradido/pull/3394) +- chore(release): v2.4.1 beta [`#3388`](https://github.com/gradido/gradido/pull/3388) +- chore(release): v2.4.0 beta [`#3387`](https://github.com/gradido/gradido/pull/3387) +- feat(backend): auto register new user in humhub [`#3386`](https://github.com/gradido/gradido/pull/3386) +- feat(backend): try and catch user sync [`#3385`](https://github.com/gradido/gradido/pull/3385) +- feat(backend): increase initialien count [`#3369`](https://github.com/gradido/gradido/pull/3369) +- feat(frontend): monterail vue3 migration [`#3383`](https://github.com/gradido/gradido/pull/3383) +- fix(frontend): fix postmigration fix [`#3382`](https://github.com/gradido/gradido/pull/3382) +- feat(frontend): update text [`#3373`](https://github.com/gradido/gradido/pull/3373) +- feat(frontend): fix postmigration fix [`#3378`](https://github.com/gradido/gradido/pull/3378) +- feat(frontend): map feature in vue 3 [`#3376`](https://github.com/gradido/gradido/pull/3376) +- feat(frontend): links and emails in messages [`#3377`](https://github.com/gradido/gradido/pull/3377) +- feat(frontend): add transaction link in latest transactions [`#3375`](https://github.com/gradido/gradido/pull/3375) +- fix(frontend): fix logout issue [`#3374`](https://github.com/gradido/gradido/pull/3374) +- fix(frontend): post migration fixes [`#3372`](https://github.com/gradido/gradido/pull/3372) +- feat(frontend): vue3 migration [`#3365`](https://github.com/gradido/gradido/pull/3365) +- fix(frontend): fix index.html [`#3368`](https://github.com/gradido/gradido/pull/3368) +- build(frontend): merged code from master [`#3367`](https://github.com/gradido/gradido/pull/3367) +- fix(frontend): vue3 migration pre deploy setup [`#3366`](https://github.com/gradido/gradido/pull/3366) +- fix(workflow): fix broken tests [`#3363`](https://github.com/gradido/gradido/pull/3363) +- fix(frontend): style fixes, admin fix [`#3364`](https://github.com/gradido/gradido/pull/3364) +- fix(frontend): gdt test [`#3361`](https://github.com/gradido/gradido/pull/3361) +- fix(frontend): style fixes [`#3360`](https://github.com/gradido/gradido/pull/3360) +- fix(frontend): migration feedback fixes [`#3359`](https://github.com/gradido/gradido/pull/3359) +- fix(frontend): scss changes and fixes [`#3358`](https://github.com/gradido/gradido/pull/3358) +- fix(frontend): migration remaining fixes [`#3356`](https://github.com/gradido/gradido/pull/3356) +- fix(admin): fix message update [`#3354`](https://github.com/gradido/gradido/pull/3354) +- fix(admin): fix refetch data in edit creation form [`#3353`](https://github.com/gradido/gradido/pull/3353) +- fix(frontend): fix dropdown in transaction send and link [`#3352`](https://github.com/gradido/gradido/pull/3352) +- fix(frontend): fix newsletter state reactivity [`#3351`](https://github.com/gradido/gradido/pull/3351) +- fix(frontend): fix how community switch is handled [`#3350`](https://github.com/gradido/gradido/pull/3350) +- fix(frontend): fixed after merge [`#3349`](https://github.com/gradido/gradido/pull/3349) +- fix(frontend): fixed logout handler [`#3347`](https://github.com/gradido/gradido/pull/3347) +- chore(frontend): main js cleanup [`#3346`](https://github.com/gradido/gradido/pull/3346) +- feature(frontend): change env config reading [`#3345`](https://github.com/gradido/gradido/pull/3345) +- feature(frontend): bump node in FE .nvmrc [`#3344`](https://github.com/gradido/gradido/pull/3344) +- feat(frontend): migration setup [`#3342`](https://github.com/gradido/gradido/pull/3342) +- fix(admin): Remove "maxAmountPerMonth" from `createContributionLink` gql. [`#3343`](https://github.com/gradido/gradido/pull/3343) +- fix(admin): style fixes [`#3339`](https://github.com/gradido/gradido/pull/3339) +- fix(admin): creation tab disappearing after creating creation [`#3338`](https://github.com/gradido/gradido/pull/3338) +- fix(frontend): show updated gdd amount [`#3337`](https://github.com/gradido/gradido/pull/3337) +- feat(admin): Add remaining fixes [`#3336`](https://github.com/gradido/gradido/pull/3336) +- feat(admin): fix edit creation form [`#3334`](https://github.com/gradido/gradido/pull/3334) +- feat(admin): migration of admin creation components [`#3333`](https://github.com/gradido/gradido/pull/3333) +- feat(admin): automatic contributions updates [`#3332`](https://github.com/gradido/gradido/pull/3332) +- feat(admin): vite config changes [`#3331`](https://github.com/gradido/gradido/pull/3331) +- feat(admin) - fix import in node server [`#3330`](https://github.com/gradido/gradido/pull/3330) +- fix(admin): stylelint fix [`#3329`](https://github.com/gradido/gradido/pull/3329) +- feat(admin): setup migration environment [`#3328`](https://github.com/gradido/gradido/pull/3328) +- fix(admin): fix contribution link [`#3326`](https://github.com/gradido/gradido/pull/3326) +- feat(admin): geo-coordinates for community [`#3323`](https://github.com/gradido/gradido/pull/3323) +- feat(backend): speedup listTransactions [`#3324`](https://github.com/gradido/gradido/pull/3324) +- fix(frontend): link forwarding after using send with url parameters [`#3322`](https://github.com/gradido/gradido/pull/3322) + #### [2.3.1](https://github.com/gradido/gradido/compare/2.3.0...2.3.1) +> 11 June 2024 + +- chore(release): v2.3.1 beta [`#3321`](https://github.com/gradido/gradido/pull/3321) - feat(frontend): more compatible humhub auto-login link [`#3319`](https://github.com/gradido/gradido/pull/3319) - fix(backend): fix test which will only fail at 31. of month, or 30.05 [`#3320`](https://github.com/gradido/gradido/pull/3320) - feat(frontend): remove automatically logged out message [`#3318`](https://github.com/gradido/gradido/pull/3318) diff --git a/admin/.gitignore b/admin/.gitignore index 59b32c33e..704a34221 100644 --- a/admin/.gitignore +++ b/admin/.gitignore @@ -1,6 +1,7 @@ node_modules/ build/ .cache/ +.yarn/install-state.gz /.env /.env.bak diff --git a/admin/Dockerfile b/admin/Dockerfile index a01903cc3..7d06b8251 100644 --- a/admin/Dockerfile +++ b/admin/Dockerfile @@ -11,7 +11,7 @@ ENV BUILD_DATE="1970-01-01T00:00:00.00Z" ## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0 ENV BUILD_VERSION="0.0.0.0" ## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ENV BUILD_COMMIT="0000000" +ENV BUILD_COMMIT_SHORT="0000000" ## SET NODE_ENV ARG NODE_ENV="production" ## App relevant Envs @@ -42,6 +42,8 @@ EXPOSE ${PORT} RUN mkdir -p ${DOCKER_WORKDIR} WORKDIR ${DOCKER_WORKDIR} +RUN mkdir -p /config + ################################################################################## # DEVELOPMENT (Connected to the local environment, to reload on demand) ########## ################################################################################## @@ -53,7 +55,7 @@ FROM base as development # Run command # (for development we need to execute yarn install since the # node_modules are on another volume and need updating) -CMD /bin/sh -c "yarn install && yarn run dev" +CMD /bin/sh -c "cd /config && yarn install && cd /app && yarn && yarn run dev" ################################################################################## # BUILD (Does contain all files and is therefore bloated) ######################## @@ -61,9 +63,16 @@ CMD /bin/sh -c "yarn install && yarn run dev" FROM base as build # Copy everything -COPY . . -# yarn install +COPY ./admin/ . +# Copy everything from config +COPY ./config/ ../config/ + +# yarn install and build config +RUN cd ../config && yarn install --production=false --frozen-lockfile --non-interactive && yarn build + +# yarn install admin RUN yarn install --production=false --frozen-lockfile --non-interactive + # yarn build RUN yarn run build @@ -85,6 +94,7 @@ FROM base as production # Copy "binary"-files from build image COPY --from=build ${DOCKER_WORKDIR}/build ./build +COPY --from=build ${DOCKER_WORKDIR}/../config/build ../config/build # We also copy the node_modules express and serve-static for the run script COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules # Copy static files diff --git a/admin/package.json b/admin/package.json index 3a5d9363b..5b4c7a24c 100644 --- a/admin/package.json +++ b/admin/package.json @@ -3,9 +3,8 @@ "description": "Administration Interface for Gradido", "main": "index.js", "author": "Moriz Wahl", - "version": "2.3.1", + "version": "2.4.5", "license": "Apache-2.0", - "private": false, "scripts": { "start": "node run/server.js", "dev": "vite", @@ -37,7 +36,7 @@ "babel-preset-env": "^1.7.0", "babel-preset-vue": "^2.0.2", "bootstrap": "^5.3.3", - "bootstrap-vue-next": "^0.23.2", + "bootstrap-vue-next": "0.26.8", "date-fns": "^2.29.3", "dotenv-webpack": "^7.0.3", "express": "^4.17.1", @@ -50,10 +49,11 @@ "sass": "^1.77.8", "vite": "3.2.10", "vite-plugin-commonjs": "^0.10.1", - "vue": "3.4.31", + "vue": "3.5.13", "vue-apollo": "3.1.2", "vue-i18n": "9.13.1", "vue-router": "4.4.0", + "vue3-datepicker": "^0.4.0", "vuex": "4.1.0", "vuex-persistedstate": "4.1.0" }, @@ -74,6 +74,8 @@ "eslint-plugin-prettier": "5.2.1", "eslint-plugin-promise": "^5.1.1", "eslint-plugin-vue": "8.7.1", + "gradido-config": "../config", + "joi": "^17.13.3", "jsdom": "^25.0.0", "mock-apollo-client": "^1.2.1", "postcss": "^8.4.8", diff --git a/admin/prepare-and-build.sh b/admin/prepare-and-build.sh new file mode 100755 index 000000000..44ed47276 --- /dev/null +++ b/admin/prepare-and-build.sh @@ -0,0 +1,7 @@ +# TODO this is the quick&dirty solution for the openssl security topic, please see https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported +$env:NODE_OPTIONS = "--openssl-legacy-provider" + +nvm use +yarn cache clean +yarn install +yarn build diff --git a/admin/run/server.js b/admin/run/server.js index 730fa03b5..2b6482367 100644 --- a/admin/run/server.js +++ b/admin/run/server.js @@ -1,11 +1,12 @@ // Imports +import CONFIG from '../src/config' + const express = require('express') const path = require('path') // Host & Port -const hostname = '127.0.0.1' -const port = import.meta.env.PORT || 8080 - +const hostname = CONFIG.ADMIN_MODULE_HOST // '127.0.0.1' +const port = CONFIG.ADMIN_MODULE_PORT // process.env.PORT || 8080 // Express Server const app = express() // Serve files diff --git a/admin/src/components/ContributionLink/ContributionLinkForm.vue b/admin/src/components/ContributionLink/ContributionLinkForm.vue index 20d78dc99..f67383aa4 100644 --- a/admin/src/components/ContributionLink/ContributionLinkForm.vue +++ b/admin/src/components/ContributionLink/ContributionLinkForm.vue @@ -125,6 +125,7 @@ import { createContributionLink } from '@/graphql/createContributionLink.js' import { updateContributionLink } from '@/graphql/updateContributionLink.js' import { useAppToast } from '@/composables/useToast' import { useI18n } from 'vue-i18n' +import { useDateFormatter } from '@/composables/useDateFormatter' const props = defineProps({ contributionLinkData: { @@ -138,6 +139,8 @@ const emit = defineEmits(['get-contribution-links', 'close-contribution-form']) const { t } = useI18n() +const { formatDateFromDateTime } = useDateFormatter() + const contributionLinkForm = ref(null) const form = ref({ @@ -201,11 +204,6 @@ const onSubmit = async () => { } } -const formatDateFromDateTime = (datetimeString) => { - if (!datetimeString || !datetimeString?.includes('T')) return datetimeString - return datetimeString.split('T')[0] -} - const onReset = () => { form.value = { validFrom: null, validTo: null } } diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js b/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js index a64a7dfe9..fdf562a80 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js @@ -21,6 +21,7 @@ vi.mock('@vue/apollo-composable', () => ({ vi.mock('vue-i18n', () => ({ useI18n: () => ({ + locale: { value: 'en' }, t: (key) => key, }), })) diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue index af946fd56..0ae31c905 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue @@ -8,10 +8,18 @@ - - +
+ + +
- + diff --git a/frontend/src/components/GddTransactionList.vue b/frontend/src/components/GddTransactionList.vue index b0cd1bd5e..8396f8d52 100644 --- a/frontend/src/components/GddTransactionList.vue +++ b/frontend/src/components/GddTransactionList.vue @@ -24,27 +24,18 @@
-
+
- - + + diff --git a/frontend/src/components/Transaction.spec.js b/frontend/src/components/Transaction.spec.js index 764be7894..aaf80430f 100644 --- a/frontend/src/components/Transaction.spec.js +++ b/frontend/src/components/Transaction.spec.js @@ -1,242 +1,3 @@ -// import { mount } from '@vue/test-utils' -// import Transaction from './Transaction' -// import Vue from 'vue' -// import flushPromises from 'flush-promises' -// -// const localVue = global.localVue -// -// const consoleErrorMock = jest.fn() -// -// describe('Transaction', () => { -// let wrapper -// -// const mocks = { -// $i18n: { -// locale: 'en', -// }, -// $t: jest.fn((t) => t), -// $n: jest.fn((n) => n), -// $d: jest.fn((d) => d), -// } -// -// const Wrapper = () => { -// return mount(Transaction, { localVue, mocks }) -// } -// -// describe('mount', () => { -// beforeEach(() => { -// wrapper = Wrapper() -// }) -// -// it('renders the component', () => { -// expect(wrapper.find('div.gdt-transaction-list').exists()).toBeTruthy() -// }) -// -// it('has a collapse icon bi-arrow-down-circle', () => { -// expect(wrapper.find('div.gdt-transaction-list').findAll('svg').at(1).classes()).toEqual([ -// 'bi-arrow-down-circle', -// 'h1', -// 'b-icon', -// 'bi', -// 'text-muted', -// ]) -// }) -// -// describe('no valid GDT entry type', () => { -// beforeEach(async () => { -// // disable throwing Errors on warnings to catch the warning -// Vue.config.warnHandler = (w) => {} -// // eslint-disable-next-line no-console -// console.error = consoleErrorMock -// await wrapper.setProps({ gdtEntryType: 'NOT_VALID' }) -// }) -// -// it('throws an error', () => { -// expect(consoleErrorMock).toBeCalledWith( -// expect.objectContaining({ message: 'no lines for this type: NOT_VALID' }), -// ) -// }) -// }) -// -// describe('default entry type FORM', () => { -// beforeEach(async () => { -// await wrapper.setProps({ -// amount: 100, -// date: '2021-05-02T17:20:11+00:00', -// comment: 'This is a comment', -// factor: 17, -// gdt: 1700, -// id: 42, -// }) -// }) -// -// it('has the heart icon', () => { -// expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy() -// }) -// -// it('has the description gdt.contribution', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.contribution') -// }) -// -// it('renders the amount of euros', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('100 €') -// }) -// -// it('renders the amount of GDT', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('1700 GDT') -// }) -// -// it.skip('renders the comment message', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('This is a comment') -// }) -// -// it.skip('renders the date', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('Sun May 02 2021') -// }) -// -// it('does not show the collapse by default', () => { -// expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy() -// }) -// -// describe('without comment', () => { -// it('does not render the message row', async () => { -// await wrapper.setProps({ comment: undefined }) -// expect(wrapper.findAll('div.row').at(1).text()).toContain('gdt.calculation') -// }) -// }) -// // how to open the collapse ????? -// describe.skip('collapse is open', () => { -// beforeEach(async () => { -// await wrapper.find('div#gdt-collapse-42').trigger('click') -// await wrapper.vm.$nextTick() -// await flushPromises() -// await wrapper.vm.$nextTick() -// await flushPromises() -// }) -// -// it('shows the collapse', () => { -// expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeTruthy() -// }) -// }) -// }) -// -// describe('GdtEntryType.CVS', () => { -// it('behaves as default FORM', async () => { -// await wrapper.setProps({ gdtEntryType: 'CVS' }) -// expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy() -// }) -// }) -// -// describe('GdtEntryType.ELOPAGE', () => { -// it('behaves as default FORM', async () => { -// await wrapper.setProps({ gdtEntryType: 'ELOPAGE' }) -// expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy() -// }) -// }) -// -// describe('GdtEntryType.DIGISTORE', () => { -// it('behaves as default FORM', async () => { -// await wrapper.setProps({ gdtEntryType: 'DIGISTORE' }) -// expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy() -// }) -// }) -// -// describe('GdtEntryType.CVS2', () => { -// it('behaves as default FORM', async () => { -// await wrapper.setProps({ gdtEntryType: 'CVS2' }) -// expect(wrapper.find('svg.bi-heart').exists()).toBeTruthy() -// }) -// }) -// -// describe('GdtEntryType.ELOPAGE_PUBLISHER', () => { -// beforeEach(async () => { -// await wrapper.setProps({ -// amount: 365.67, -// date: '2020-04-10T13:28:00+00:00', -// comment: 'This is a comment', -// gdtEntryType: 'ELOPAGE_PUBLISHER', -// factor: 22, -// gdt: 967.65, -// id: 42, -// }) -// }) -// -// it('has the person-check icon', () => { -// expect(wrapper.find('svg.bi-person-check').exists()).toBeTruthy() -// }) -// -// it('has the description gdt.recruited-member', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.recruited-member') -// }) -// -// it('renders the percentage', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('5%') -// }) -// -// it('renders the amount of GDT', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('365.67 GDT') -// }) -// -// it('renders the gdt.publisher', () => { -// expect(wrapper.findAll('div.row').at(1).text()).toContain('gdt.publisher') -// }) -// -// it.skip('renders the date', () => { -// expect(wrapper.findAll('div.row').at(2).text()).toContain('Fri Apr 10 2020') -// }) -// -// it('does not show the collapse by default', () => { -// expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy() -// }) -// -// describe.skip('without comment', () => { -// it('does not render the message row', async () => { -// await wrapper.setProps({ comment: undefined }) -// expect(wrapper.findAll('div.row').at(0).text()).toContain('form.date') -// }) -// }) -// }) -// -// describe('GdtEntryType.GLOBAL_MODIFICATOR', () => { -// beforeEach(async () => { -// await wrapper.setProps({ -// amount: 123.45, -// date: '2020-03-12T13:28:00+00:00', -// comment: 'This is a comment', -// gdtEntryType: 'GLOBAL_MODIFICATOR', -// factor: 19, -// gdt: 61.23, -// id: 42, -// }) -// }) -// -// it('has the gift icon', () => { -// expect(wrapper.find('svg.bi-gift').exists()).toBeTruthy() -// }) -// -// it('has the description gdt.gdt-received', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.gdt-received') -// }) -// -// it('renders the comment', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('This is a comment') -// }) -// -// it('renders the amount of GDT', () => { -// expect(wrapper.findAll('div.row').at(0).text()).toContain('61.23 GDT') -// }) -// -// it('renders the gdt.conversion-gdt-euro', () => { -// expect(wrapper.findAll('div.row').at(1).text()).toContain('gdt.conversion-gdt-euro') -// }) -// -// it('does not show the collapse by default', () => { -// expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBeFalsy() -// }) -// }) -// }) -// }) - import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' import Transaction from './Transaction' @@ -245,6 +6,7 @@ import { BAvatar, BCol, BCollapse, BRow } from 'bootstrap-vue-next' import TransactionCollapse from '@/components/TransactionCollapse.vue' import { GdtEntryType } from '@/graphql/enums' import VariantIcon from '@/components/VariantIcon.vue' +import { createStore } from 'vuex' const mockToastError = vi.fn() vi.mock('@/composables/useToast', () => ({ @@ -265,9 +27,19 @@ vi.mock('vue-i18n', () => ({ describe('Transaction', () => { let wrapper - const Wrapper = () => { + const createWrapper = (props = {}, options = {}) => { return mount(Transaction, { + props: { + ...props, + }, global: { + plugins: [ + createStore({ + state: { + transactionToHighlightId: '', + }, + }), + ], mocks: { $d: (value) => value?.toString() ?? '', $n: (value) => value?.toString() ?? '', @@ -277,8 +49,21 @@ describe('Transaction', () => { BRow, BCol, BAvatar, - BCollapse, + BCollapse: { + template: ` +
+ +
+ `, + props: ['id', 'modelValue'], + }, VariantIcon, + ...options.stubs, }, }, }) @@ -286,7 +71,7 @@ describe('Transaction', () => { describe('mount', () => { beforeEach(() => { - wrapper = Wrapper() + wrapper = createWrapper() }) it('renders the component', () => { @@ -322,7 +107,7 @@ describe('Transaction', () => { describe('default entry type FORM', () => { beforeEach(async () => { - await wrapper.setProps({ + wrapper = createWrapper({ amount: 100, date: '2021-05-02T17:20:11+00:00', comment: 'This is a comment', @@ -337,7 +122,7 @@ describe('Transaction', () => { }) it('has the description gdt.contribution', () => { - expect(wrapper.findAll('div.row').at(0).text()).toContain('gdt.contribution') + expect(wrapper.findAll('div.row').at(0).text()).toContain('This is a comment') }) it('renders the amount of euros', () => { @@ -348,10 +133,6 @@ describe('Transaction', () => { expect(wrapper.findAll('div.row').at(0).text()).toContain('1700 GDT') }) - // it('does not show the collapse by default', () => { - // expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBe(false) - // }) - describe('without comment', () => { it('does not render the message row', async () => { await wrapper.setProps({ comment: undefined }) @@ -366,7 +147,10 @@ describe('Transaction', () => { }) it('shows the collapse', () => { - expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBe(true) + const collapse = wrapper.find('div#gdt-collapse-42') + expect(collapse.exists()).toBe(true) + expect(collapse.attributes('data-test')).toBe('collapse') + expect(wrapper.find('[data-test="collapse"]').classes()).toContain('show') }) }) }) @@ -401,7 +185,7 @@ describe('Transaction', () => { describe('GdtEntryType.ELOPAGE_PUBLISHER', () => { beforeEach(async () => { - await wrapper.setProps({ + wrapper = createWrapper({ amount: 365.67, date: '2020-04-10T13:28:00+00:00', comment: 'This is a comment', @@ -427,15 +211,11 @@ describe('Transaction', () => { it('renders the amount of GDT', () => { expect(wrapper.findAll('div.row').at(0).text()).toContain('365.67 GDT') }) - - // it('does not show the collapse by default', () => { - // expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBe(false) - // }) }) describe('GdtEntryType.GLOBAL_MODIFICATOR', () => { beforeEach(async () => { - await wrapper.setProps({ + wrapper = createWrapper({ amount: 123.45, date: '2020-03-12T13:28:00+00:00', comment: 'This is a comment', @@ -444,7 +224,6 @@ describe('Transaction', () => { gdt: 61.23, id: 42, }) - wrapper.attachTo = document.body }) it('has the gift icon', () => { @@ -462,10 +241,6 @@ describe('Transaction', () => { it('renders the amount of GDT', () => { expect(wrapper.findAll('div.row').at(0).text()).toContain('61.23 GDT') }) - - // it('does not show the collapse by default', () => { - // expect(wrapper.find('div#gdt-collapse-42').isVisible()).toBe(false) - // }) }) }) }) diff --git a/frontend/src/components/Transaction.vue b/frontend/src/components/Transaction.vue index eb005da8d..0551f8a18 100644 --- a/frontend/src/components/Transaction.vue +++ b/frontend/src/components/Transaction.vue @@ -49,11 +49,12 @@ diff --git a/frontend/src/components/TransactionRows/Name.spec.js b/frontend/src/components/TransactionRows/Name.spec.js index 90bec127f..1504120b4 100644 --- a/frontend/src/components/TransactionRows/Name.spec.js +++ b/frontend/src/components/TransactionRows/Name.spec.js @@ -1,4 +1,4 @@ -import { mount } from '@vue/test-utils' +import { mount, RouterLinkStub } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' import Name from './Name' import { BLink } from 'bootstrap-vue-next' @@ -44,7 +44,7 @@ describe('Name', () => { global: { mocks, stubs: { - BLink, + RouterLink: RouterLinkStub, }, }, props: propsData, @@ -88,31 +88,18 @@ describe('Name', () => { it('has a link', () => { expect( - wrapper - .find('div.gdd-transaction-list-item-name') - .findComponent({ name: 'BLink' }) - .exists(), + wrapper.find('div.gdd-transaction-list-item-name').findComponent(RouterLinkStub).exists(), ).toBe(true) }) - describe('click link', () => { - beforeEach(async () => { - await wrapper.findComponent({ name: 'BLink' }).trigger('click') - }) - - it('pushes router to send', () => { - expect(routerPushMock).toHaveBeenCalledWith({ - path: '/send', - }) - }) - - it('pushes params for gradidoID and community UUID', () => { - expect(routerPushMock).toHaveBeenCalledWith({ - params: { - communityIdentifier: 'community UUID', - userIdentifier: 'gradido-ID', - }, - }) + it('RouterLink has correct to prop', () => { + const routerLink = wrapper.findComponent(RouterLinkStub) + expect(routerLink.props().to).toEqual({ + name: 'Send', + params: { + communityIdentifier: 'community UUID', + userIdentifier: 'gradido-ID', + }, }) }) }) diff --git a/frontend/src/components/TransactionRows/Name.vue b/frontend/src/components/TransactionRows/Name.vue index 1eb94319b..246fbf933 100644 --- a/frontend/src/components/TransactionRows/Name.vue +++ b/frontend/src/components/TransactionRows/Name.vue @@ -2,9 +2,9 @@
- + {{ itemText }} - +
{{ itemText }}
@@ -45,6 +45,15 @@ export default { (this.linkedUser.communityName ? ' / ' + this.linkedUser.communityName : '') : this.text }, + pushTo() { + return { + name: 'Send', + params: { + userIdentifier: this.linkedUser.gradidoID, + communityIdentifier: this.linkedUser.communityUuid, + }, + } + }, }, methods: { async tunnelEmail() { diff --git a/frontend/src/components/Transactions/GddTransaction.vue b/frontend/src/components/Transactions/GddTransaction.vue new file mode 100644 index 000000000..b5e6939e1 --- /dev/null +++ b/frontend/src/components/Transactions/GddTransaction.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/frontend/src/components/Transactions/TransactionReceive.spec.js b/frontend/src/components/Transactions/TransactionReceive.spec.js index c7d2c7314..7d1d5d1cd 100644 --- a/frontend/src/components/Transactions/TransactionReceive.spec.js +++ b/frontend/src/components/Transactions/TransactionReceive.spec.js @@ -1,16 +1,16 @@ import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' import TransactionReceive from './TransactionReceive' -import Avatar from 'vue-avatar' import CollapseIcon from '../TransactionRows/CollapseIcon' import Name from '../TransactionRows/Name' import DecayInformation from '../DecayInformations/DecayInformation' import { BCol, BCollapse, BRow } from 'bootstrap-vue-next' +import AppAvatar from '@/components/AppAvatar.vue' // Mock child components -vi.mock('vue-avatar', () => ({ +vi.mock('app-avatar', () => ({ default: { - name: 'Avatar', + name: 'AppAvatar', render: () => null, }, })) @@ -86,7 +86,7 @@ describe('TransactionReceive', () => { $d: (date, format) => `Mocked ${format} date for ${date}`, }, components: { - Avatar, + AppAvatar, CollapseIcon, Name, DecayInformation, diff --git a/frontend/src/components/Transactions/TransactionReceive.vue b/frontend/src/components/Transactions/TransactionReceive.vue index 0c24bcce0..3d974a141 100644 --- a/frontend/src/components/Transactions/TransactionReceive.vue +++ b/frontend/src/components/Transactions/TransactionReceive.vue @@ -3,12 +3,14 @@ - + :border="false" + />
@@ -46,7 +48,6 @@
+ + diff --git a/frontend/src/components/UserSettings/UserCard.spec.js b/frontend/src/components/UserSettings/UserCard.spec.js index 057e0fd20..3fd48488e 100644 --- a/frontend/src/components/UserSettings/UserCard.spec.js +++ b/frontend/src/components/UserSettings/UserCard.spec.js @@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' import UserCard from './UserCard.vue' import { BCol, BRow } from 'bootstrap-vue-next' +import AppAvatar from '@/components/AppAvatar.vue' vi.mock('vue-avatar', () => ({ default: { @@ -39,6 +40,7 @@ describe('UserCard', () => { components: { BRow, BCol, + AppAvatar, }, mocks, }, @@ -53,12 +55,12 @@ describe('UserCard', () => { expect(wrapper.find('.userdata-card').exists()).toBe(true) }) - it('renders the SPAN Element ".vue-avatar--wrapper"', () => { - expect(wrapper.find('.vue-avatar--wrapper').exists()).toBe(true) + it('renders the DIV Element ".app-avatar"', () => { + expect(wrapper.find('.app-avatar').exists()).toBe(true) }) it('displays the first letters of the firstName and lastName', () => { - expect(wrapper.find('.vue-avatar--wrapper span').text()).toBe('BB') + expect(wrapper.find('.app-avatar').text()).toBe('BB') }) it('displays the correct balance', () => { diff --git a/frontend/src/components/UserSettings/UserCard.vue b/frontend/src/components/UserSettings/UserCard.vue index 93f44c59f..6d2c556e5 100755 --- a/frontend/src/components/UserSettings/UserCard.vue +++ b/frontend/src/components/UserSettings/UserCard.vue @@ -1,12 +1,14 @@ diff --git a/frontend/src/components/UserSettings/UserGMSLocationFormat.vue b/frontend/src/components/UserSettings/UserGMSLocationFormat.vue index f3dab95aa..3bae2208d 100644 --- a/frontend/src/components/UserSettings/UserGMSLocationFormat.vue +++ b/frontend/src/components/UserSettings/UserGMSLocationFormat.vue @@ -1,7 +1,6 @@ + + diff --git a/frontend/src/components/UserSettings/UserLocationMap.vue b/frontend/src/components/UserSettings/UserLocationMap.vue new file mode 100644 index 000000000..3b8064f75 --- /dev/null +++ b/frontend/src/components/UserSettings/UserLocationMap.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/frontend/src/components/UserSettings/UserName.spec.js b/frontend/src/components/UserSettings/UserName.spec.js index dc6ad04aa..4c5cf1fca 100644 --- a/frontend/src/components/UserSettings/UserName.spec.js +++ b/frontend/src/components/UserSettings/UserName.spec.js @@ -1,5 +1,6 @@ import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi } from 'vitest' +import { ref } from 'vue' import UserName from './UserName.vue' import { createStore } from 'vuex' import { createI18n } from 'vue-i18n' @@ -60,20 +61,22 @@ vi.mock('@/composables/useToast', () => ({ }), })) -const valuesMock = {} -const errorsMock = {} +// Updated to use Vue's reactivity +const valuesMock = ref({ username: '' }) +const errorsMock = ref({}) const setFieldValueMock = vi.fn((field, value) => { - valuesMock[field] = value + valuesMock.value[field] = value }) const handleSubmitMock = vi.fn((callback) => { - return () => callback(valuesMock) + return () => callback(valuesMock.value) }) + vi.mock('vee-validate', () => ({ useForm: () => ({ handleSubmit: handleSubmitMock, setFieldValue: setFieldValueMock, - values: valuesMock, - errors: errorsMock, + values: valuesMock.value, + errors: errorsMock.value, }), })) @@ -94,13 +97,13 @@ describe('UserName Form', () => { beforeEach(() => { vi.clearAllMocks() - valuesMock.username = '' + valuesMock.value.username = '' wrapper = mountComponent() }) describe('when no username is set', () => { it('renders the component', () => { - expect(wrapper.find('div#username_form').exists()).toBe(true) + expect(wrapper.find('div#username-form').exists()).toBe(true) }) it('displays the no-username alert', () => { @@ -132,8 +135,13 @@ describe('UserName Form', () => { }) it('enables submit button when a new username is entered', async () => { - setFieldValueMock('username', 'newUser') + valuesMock.value.username = 'newUser' // Directly set the reactive value await wrapper.vm.$nextTick() + + // Trigger input change to ensure reactivity + await wrapper.find('[data-test="component-input-username"]').trigger('input') + await wrapper.vm.$nextTick() + expect(wrapper.find('[data-test="submit-username-button"]').exists()).toBe(true) expect( wrapper.find('[data-test="submit-username-button"]').attributes('disabled'), @@ -143,7 +151,8 @@ describe('UserName Form', () => { it('submits the form and updates the store on success', async () => { mutationMock.mockResolvedValue({ data: { updateUserInfos: { validValues: 3 } } }) - setFieldValueMock('username', 'newUser') + valuesMock.value.username = 'newUser' + await wrapper.vm.$nextTick() await wrapper.find('form').trigger('submit') expect(mutationMock).toHaveBeenCalledWith({ alias: 'newUser' }) @@ -154,7 +163,8 @@ describe('UserName Form', () => { it('shows an error toast on submission failure', async () => { mutationMock.mockRejectedValue(new Error('API Error')) - setFieldValueMock('username', 'newUser') + valuesMock.value.username = 'newUser' + await wrapper.vm.$nextTick() await wrapper.find('form').trigger('submit') expect(mutationMock).toHaveBeenCalledWith({ alias: 'newUser' }) diff --git a/frontend/src/components/UserSettings/UserName.vue b/frontend/src/components/UserSettings/UserName.vue index c34999048..2ba9713ca 100644 --- a/frontend/src/components/UserSettings/UserName.vue +++ b/frontend/src/components/UserSettings/UserName.vue @@ -1,5 +1,5 @@ diff --git a/frontend/src/composables/useToast.js b/frontend/src/composables/useToast.js index bc9bdbe49..c3f6697dd 100644 --- a/frontend/src/composables/useToast.js +++ b/frontend/src/composables/useToast.js @@ -1,9 +1,9 @@ import { useI18n } from 'vue-i18n' -import { useToast } from 'bootstrap-vue-next' +import { useToastController } from 'bootstrap-vue-next' export function useAppToast() { const { t } = useI18n() - const { show } = useToast() + const { show } = useToastController() const toastSuccess = (message) => { toast(message, { title: t('success'), @@ -22,6 +22,7 @@ export function useAppToast() { toast(message, { title: t('navigation.info'), variant: 'warning', + bodyClass: 'gdd-toaster-body-darken', }) } diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 78ebc1b5b..1ace61a74 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -6,20 +6,37 @@ const pkg = require('../../package') const constants = { DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 - CONFIG_VERSION: { - DEFAULT: 'DEFAULT', - EXPECTED: 'v6.2024-02-27', - CURRENT: '', - }, } const version = { + FRONTEND_MODULE_PROTOCOL: process.env.FRONTEND_MODULE_PROTOCOL ?? 'http', + FRONTEND_MODULE_HOST: process.env.FRONTEND_MODULE_HOST ?? '0.0.0.0', + FRONTEND_MODULE_PORT: process.env.FRONTEND_MODULE_PORT ?? '3000', APP_VERSION: pkg.version, - BUILD_COMMIT: process.env.BUILD_COMMIT ?? null, + BUILD_COMMIT: process.env.BUILD_COMMIT ?? undefined, // self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7), } +let FRONTEND_MODULE_URL + +// in case of hosting the frontend module with a nodejs-instance +if (process.env.FRONTEND_HOSTING === 'nodejs') { + FRONTEND_MODULE_URL = + version.FRONTEND_MODULE_PROTOCOL + + '://' + + version.FRONTEND_MODULE_HOST + + ':' + + version.FRONTEND_MODULE_PORT +} else { + // in case of hosting the frontend module with a nginx + FRONTEND_MODULE_URL = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST +} + +// const FRONTEND_MODULE_URI = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST // + +// ':' + +// version.FRONTEND_MODULE_PORT + const features = { GMS_ACTIVE: process.env.GMS_ACTIVE ?? false, HUMHUB_ACTIVE: process.env.HUMHUB_ACTIVE ?? false, @@ -29,18 +46,17 @@ const environment = { NODE_ENV: process.env.NODE_ENV, DEBUG: process.env.NODE_ENV !== 'production' ?? false, PRODUCTION: process.env.NODE_ENV === 'production' ?? false, - DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID ?? 2896, - PORT: process.env.PORT ?? 3000, } -const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost' -const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http' -const COMMUNITY_URL = process.env.COMMUNITY_URL ?? `${URL_PROTOCOL}://${COMMUNITY_HOST}` +// const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost' +// const URL_PROTOCOL = process.env.URL_PROTOCOL ?? 'http' +const COMMUNITY_URL = process.env.COMMUNITY_URL ?? FRONTEND_MODULE_URL const endpoints = { - GRAPHQL_URI: COMMUNITY_URL + (process.env.GRAPHQL_PATH ?? '/graphql'), + GRAPHQL_URI: process.env.GRAPHQL_URI ?? COMMUNITY_URL + (process.env.GRAPHQL_PATH ?? '/graphql'), ADMIN_AUTH_URL: - COMMUNITY_URL + (process.env.ADMIN_AUTH_PATH ?? '/admin/authenticate?token={token}'), + process.env.ADMIN_AUTH_URL ?? + COMMUNITY_URL + (process.env.ADMIN_AUTH_PATH ?? '/admin/authenticate?token='), } const community = { @@ -50,6 +66,7 @@ const community = { COMMUNITY_DESCRIPTION: process.env.COMMUNITY_DESCRIPTION ?? 'Die lokale Entwicklungsumgebung von Gradido.', COMMUNITY_SUPPORT_MAIL: process.env.COMMUNITY_SUPPORT_MAIL ?? 'support@supportmail.com', + COMMUNITY_LOCATION: process.env.COMMUNITY_LOCATION ?? '49.280377, 9.690151', } const meta = { @@ -71,26 +88,15 @@ const meta = { META_AUTHOR: process.env.META_AUTHOR ?? 'Bernd Hückstädt - Gradido-Akademie', } -// 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, ...features, ...environment, ...endpoints, ...community, ...meta, + ...constants, + FRONTEND_MODULE_URL, } -export default CONFIG +module.exports = CONFIG diff --git a/frontend/src/config/schema.js b/frontend/src/config/schema.js new file mode 100644 index 000000000..dc5a22c43 --- /dev/null +++ b/frontend/src/config/schema.js @@ -0,0 +1,167 @@ +const { + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_DESCRIPTION, + COMMUNITY_NAME, + COMMUNITY_SUPPORT_MAIL, + COMMUNITY_LOCATION, + COMMUNITY_URL, + DEBUG, + DECAY_START_TIME, + GMS_ACTIVE, + GRAPHQL_URI, + HUMHUB_ACTIVE, + NODE_ENV, + PRODUCTION, +} = require('gradido-config/build/src/commonSchema.js') +const Joi = require('joi') + +// console.log(commonSchema) + +module.exports = Joi.object({ + APP_VERSION, + BUILD_COMMIT, + BUILD_COMMIT_SHORT, + COMMUNITY_DESCRIPTION, + COMMUNITY_NAME, + COMMUNITY_SUPPORT_MAIL, + COMMUNITY_LOCATION, + COMMUNITY_URL, + DEBUG, + DECAY_START_TIME, + GMS_ACTIVE, + GRAPHQL_URI, + HUMHUB_ACTIVE, + NODE_ENV, + PRODUCTION, + + ADMIN_AUTH_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('Extern Url for admin-frontend') + .default('http://0.0.0.0/admin/authenticate?token=') + .required(), + + COMMUNITY_REGISTER_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('URL for Register a new Account in frontend.') + .required(), + + FRONTEND_HOSTING: Joi.string() + .valid('nodejs', 'nginx') + .description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance') + .optional(), + + FRONTEND_MODULE_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .when('COMMUNITY_URL', { + is: Joi.exist(), + then: Joi.optional(), // not required if COMMUNITY_URL is provided + otherwise: Joi.required(), // required if COMMUNITY_URL is missing + }) + .description( + "Base Url for reaching frontend in browser, only needed if COMMUNITY_URL wasn't set", + ) + .optional(), // optional in general, but conditionally required + + FRONTEND_MODULE_PROTOCOL: Joi.string() + .when('FRONTEND_HOSTING', { + is: Joi.valid('nodejs'), + then: Joi.valid('http').required(), + otherwise: Joi.valid('http', 'https').required(), + }) + .description( + ` + Protocol for frontend module hosting + - it has to be the same as for backend api url and admin to prevent mixed block errors, + - if frontend is served with nodejs: + is have to be http or setup must be updated to include a ssl certificate + `, + ) + .default('http') + .required(), + + FRONTEND_MODULE_HOST: Joi.alternatives() + .try( + Joi.string().valid('localhost').messages({ 'any.invalid': 'Must be localhost' }), + Joi.string() + .ip({ version: ['ipv4'] }) + .messages({ 'string.ip': 'Must be a valid IPv4 address' }), + Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }), + ) + .when('FRONTEND_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }) + .when('COMMUNITY_URL', { + is: null, + then: Joi.required(), + otherwise: Joi.optional(), + }) + .description( + 'Host (domain, IPv4, or localhost) for the frontend, default is 0.0.0.0 for local hosting during development.', + ) + .default('0.0.0.0'), + + FRONTEND_MODULE_PORT: Joi.number() + .integer() + .min(1024) + .max(49151) + .description('Port for hosting Frontend with Vite as a Node.js instance, default: 3000') + .default(3000) + .when('FRONTEND_HOSTING', { + is: 'nodejs', + then: Joi.required(), + otherwise: Joi.optional(), + }), + + META_URL: Joi.string() + .uri({ scheme: ['http', 'https'] }) + .description('The base URL for the meta tags.') + .default('http://localhost') + .required(), + + META_TITLE_DE: Joi.string() + .description('Meta title in German.') + .default('Gradido – Dein Dankbarkeitskonto') + .required(), + + META_TITLE_EN: Joi.string() + .description('Meta title in English.') + .default('Gradido - Your gratitude account') + .required(), + + META_DESCRIPTION_DE: Joi.string() + .description('Meta description in German.') + .default( + 'Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle.', + ) + .required(), + + META_DESCRIPTION_EN: Joi.string() + .description('Meta description in English.') + .default( + 'Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all.', + ) + .required(), + + META_KEYWORDS_DE: Joi.string() + .description('Meta keywords in German.') + .default( + 'Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem', + ) + .required(), + + META_KEYWORDS_EN: Joi.string() + .description('Meta keywords in English.') + .default( + 'Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System', + ) + .required(), + + META_AUTHOR: Joi.string() + .description('The author for the meta tags.') + .default('Bernd Hückstädt - Gradido-Akademie') + .required(), +}) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index b9095a7e4..0d4858e98 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -28,6 +28,15 @@ export const authenticateGmsUserSearch = gql` } ` +export const userLocationQuery = gql` + query { + userLocation { + userLocation + communityLocation + } + } +` + export const authenticateHumhubAutoLogin = gql` query { authenticateHumhubAutoLogin diff --git a/frontend/src/layouts/AuthLayout.vue b/frontend/src/layouts/AuthLayout.vue index ab6973a0e..bc77b102f 100644 --- a/frontend/src/layouts/AuthLayout.vue +++ b/frontend/src/layouts/AuthLayout.vue @@ -30,7 +30,7 @@
{{ $t('1000thanks') }}
- +
@@ -62,7 +62,7 @@ diff --git a/frontend/src/layouts/DashboardLayout.spec.js b/frontend/src/layouts/DashboardLayout.spec.js index 37feb0d2f..0906793ff 100644 --- a/frontend/src/layouts/DashboardLayout.spec.js +++ b/frontend/src/layouts/DashboardLayout.spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils' import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest' -import { nextTick } from 'vue' +import { nextTick, ref } from 'vue' import DashboardLayout from './DashboardLayout' import { createStore } from 'vuex' import { createRouter, createWebHistory } from 'vue-router' @@ -15,11 +15,15 @@ vi.mock('@/composables/useToast', () => ({ })) const mockQueryFn = vi.fn() +const mockRefetchFn = vi.fn() const mockMutateFn = vi.fn() +const mockQueryResult = ref(null) vi.mock('@vue/apollo-composable', () => ({ useLazyQuery: vi.fn(() => ({ load: mockQueryFn, + refetch: mockRefetchFn, + result: mockQueryResult, onResult: vi.fn(), onError: vi.fn(), })), @@ -136,25 +140,25 @@ describe('DashboardLayout', () => { describe('update transactions', () => { beforeEach(async () => { - mockQueryFn.mockResolvedValue({ + mockQueryResult.value = { transactionList: { balance: { - balanceGDT: 100, + balanceGDT: '100', count: 4, linkCount: 8, - balance: 1450, - decay: 1250, + balance: '1450', }, - transactions: ['transaction', 'transaction', 'transaction', 'transaction'], + transactions: ['transaction1', 'transaction2', 'transaction3', 'transaction4'], }, - }) - await wrapper - .findComponent({ ref: 'router-view' }) - .vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 }) - await nextTick() + } + + mockQueryFn.mockResolvedValue(mockQueryResult.value) + + await wrapper.vm.updateTransactions({ currentPage: 2, pageSize: 5 }) + await nextTick() // Ensure all promises are resolved }) - it('calls the API', () => { + it('load call to the API', () => { expect(mockQueryFn).toHaveBeenCalled() }) @@ -164,10 +168,10 @@ describe('DashboardLayout', () => { it('updates transactions', () => { expect(wrapper.vm.transactions).toEqual([ - 'transaction', - 'transaction', - 'transaction', - 'transaction', + 'transaction1', + 'transaction2', + 'transaction3', + 'transaction4', ]) }) diff --git a/frontend/src/layouts/DashboardLayout.vue b/frontend/src/layouts/DashboardLayout.vue index c06f46c46..95bd858f9 100755 --- a/frontend/src/layouts/DashboardLayout.vue +++ b/frontend/src/layouts/DashboardLayout.vue @@ -181,7 +181,7 @@ - +
@@ -215,7 +215,11 @@ import { useAppToast } from '@/composables/useToast' const store = useStore() const router = useRouter() const { load: useCommunityStatsQuery } = useLazyQuery(communityStatistics) -const { load: useTransactionsQuery } = useLazyQuery(transactionsQuery) +const { + load: useTransactionsQuery, + refetch: useRefetchTransactionsQuery, + result: transactionQueryResult, +} = useLazyQuery(transactionsQuery, {}, { fetchPolicy: 'network-only' }) const { mutate: useLogoutMutation } = useMutation(logout) const { t } = useI18n() const { toastError } = useAppToast() @@ -232,14 +236,8 @@ const darkMode = ref(false) const skeleton = ref(true) const totalUsers = ref(null) -const sessionModal = ref(null) - -const testModal = () => { - sessionModal.value.showTimeoutModalForTesting() -} - onMounted(() => { - updateTransactions({ currentPage: 0, pageSize: 10 }) + updateTransactions({ currentPage: 1, pageSize: 10 }) getCommunityStatistics() setTimeout(() => { skeleton.value = false @@ -251,7 +249,7 @@ const logoutUser = async () => { await useLogoutMutation() await store.dispatch('logout') await router.push('/login') - } catch { + } catch (err) { await store.dispatch('logout') if (router.currentRoute.value.path !== '/login') await router.push('/login') } @@ -260,9 +258,9 @@ const logoutUser = async () => { const updateTransactions = async ({ currentPage, pageSize }) => { pending.value = true try { - const result = await useTransactionsQuery() - if (!result) return // TODO this return mitigate an error when this method is called second time but without actual request - const { transactionList } = result + await loadOrFetchTransactionQuery({ currentPage, pageSize }) + if (!transactionQueryResult) return + const { transactionList } = transactionQueryResult.value GdtBalance.value = transactionList.balance.balanceGDT === null ? 0 : Number(transactionList.balance.balanceGDT) transactions.value = transactionList.transactions @@ -277,6 +275,13 @@ const updateTransactions = async ({ currentPage, pageSize }) => { } } +const loadOrFetchTransactionQuery = async (queryVariables = { currentPage: 1, pageSize: 25 }) => { + return ( + (await useTransactionsQuery(transactionsQuery, queryVariables)) || + (await useRefetchTransactionsQuery(queryVariables)) + ) +} + const getCommunityStatistics = async () => { try { const result = await useCommunityStatsQuery() @@ -287,7 +292,7 @@ const getCommunityStatistics = async () => { } const admin = () => { - window.location.assign(CONFIG.ADMIN_AUTH_URL.replace('{token}', store.state.token)) + window.location.assign(CONFIG.ADMIN_AUTH_URL + store.state.token) store.dispatch('logout') // logout without redirect } @@ -298,6 +303,7 @@ const setVisible = (bool) => { diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index e04778830..dd34fbad4 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -32,8 +32,8 @@ "back": "Zurück", "circles": { "headline": "Gemeinsam unterstützen wir einander – achtsam in Kreiskultur.", - "text": "In geschützten Räumen können wir frei kommunizieren und kooperieren, ohne auf die sozialen Medien angewiesen zu sein. Mit Klick auf den Button öffnest Du die Kooperationsplattform in einem neuen Browser-Fenster.", - "button": "Gradido-Kreise starten..." + "text": "Mit Klick auf den Button öffnest Du die Kooperationsplattform in einem neuen Browser-Fenster.", + "button": "Kreise starten..." }, "community": { "admins": "Administratoren", @@ -231,7 +231,6 @@ }, "gdt": { "calculation": "Berechnung der Gradido Transform", - "contribution": "Beitrag", "conversion": "Umrechnung", "conversion-gdt-euro": "Umrechnung Euro / Gradido Transform (GDT)", "credit": "Gutschrift", @@ -248,7 +247,8 @@ }, "h": "h", "language": "Sprache", - "link-load": "den letzten Link nachladen | die letzten {n} Links nachladen | weitere {n} Links nachladen", + "link-load": "den letzten Link nachladen | die letzten {n} Links nachladen", + "link-load-more": "weitere {n} Links nachladen", "login": "Anmelden", "math": { "asterisk": "*", @@ -291,7 +291,7 @@ "overview": "Willkommen {name}", "send": "Sende Gradidos", "settings": "Einstellungen", - "circles": "Gradido Kreise (Beta)", + "circles": "Gradido Kreise", "transactions": "Deine Transaktionen", "usersearch": "Geografische Nutzersuche" }, @@ -312,10 +312,20 @@ "disabled": "Daten werden nicht nach GMS exportiert", "enabled": "Daten werden nach GMS exportiert", "location": { + "button": "Klick mich!", "label": "Positionsbestimmung", - "button": "Klick mich!" + "saveLocation": "Standort speichern", + "updateSuccess": "Standort erfolgreich gespeichert" }, "location-format": "Position auf Karte anzeigen:", + "map": { + "communityCoords": "Ihr Gemeinschafts-Standort: Breitengrad {lat}, Längengrad {lng}", + "communityLocationLabel": "Ihr Gemeinschafts-Standort", + "headline": "Geografische Standorterfassung des Benutzers", + "userCoords": "Ihr Standort: Breitengrad {lat}, Längengrad {lng}", + "userLocationLabel": "Ihr Standort", + "search": "Nach einem Standort suchen" + }, "naming-format": "Namen anzeigen:", "publish-location": { "exact": "Genaue Position", @@ -376,13 +386,13 @@ }, "publish-name": { "alias-or-initials": "Benutzername oder Initialen", - "alias-or-initials-tooltip": "Benutzername, falls vorhanden, oder die Initialen von Vorname und Nachname", + "alias-or-initials-tooltip": "Benutzername, falls vorhanden, oder die Initialen von Vorname und Nachname jeweils die ersten zwei Buchstaben", "first": "Vorname", "first-tooltip": "Nur der Vornamen", "first-initial": "Vorname und Initial", - "first-initial-tooltip": "Vornamen plus Anfangsbuchstabe des Nachnamens", + "first-initial-tooltip": "Vornamen plus die ersten beiden Anfangsbuchstabe des Nachnamens", "initials": "Initialen", - "initials-tooltip": "Initialen von Vor- und Nachname unabhängig von der Existenz des Benutzernamens", + "initials-tooltip": "Initialen von Vor- und Nachname also jeweils die ersten zwei Buchstaben unabhängig von der Existenz des Benutzernamens", "name-full": "Vorname und Nachname", "name-full-tooltip": "Vollständiger Name: Vorname plus Nachname" }, diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 2aceda935..9346ea609 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -32,8 +32,8 @@ "back": "Back", "circles": { "headline": "Together we support each other - mindful in circle culture.", - "text": "We can communicate and collaborate freely in protected spaces without having to rely on social media. Click on the button to open the collaboration platform in a new browser window.", - "button": "Gradido circles start..." + "text": "Click on the button to open the cooperation platform in a new browser window.", + "button": "Start Circles..." }, "community": { "admins": "Administrators", @@ -231,7 +231,6 @@ }, "gdt": { "calculation": "Calculation of Gradido Transform", - "contribution": "Contribution", "conversion": "Conversion", "conversion-gdt-euro": "Conversion Euro / Gradido Transform (GDT)", "credit": "Credit", @@ -248,7 +247,8 @@ }, "h": "h", "language": "Language", - "link-load": "Load the last link | Load the last {n} links | Load more {n} links", + "link-load": "Load the last link | Load the last {n} links", + "link-load-more": "Load more {n} links", "login": "Sign in", "math": { "asterisk": "*", @@ -278,7 +278,7 @@ "overview": "Overview", "send": "Send", "settings": "Settings", - "circles": "Circle", + "circles": "Circles", "support": "Support", "transactions": "Transactions", "usersearch": "Geographical User Search" @@ -292,7 +292,7 @@ "send": "Send Gradidos", "settings": "Settings", "transactions": "Your transactions", - "circles": "Gradido Circles (Beta)", + "circles": "Gradido Circles", "usersearch": "Geographical User Search" }, "qrCode": "QR Code", @@ -312,10 +312,20 @@ "disabled": "Data not exported to GMS", "enabled": "Data exported to GMS", "location": { - "label": "pinpoint location", - "button": "click me!" + "button": "Click me!", + "label": "Pinpoint location", + "saveLocation": "Save Location", + "updateSuccess": "Location successfully saved" }, "location-format": "Show position on map:", + "map": { + "communityCoords": "Your Community Location: Lat {lat}, Lng {lng}", + "communityLocationLabel": "Your Community-Location", + "headline": "Geographic Location-Capturing of the User", + "userCoords": "Your Location: Lat {lat}, Lng {lng}", + "userLocationLabel": "Your Location", + "search": "Search for a location" + }, "naming-format": "Show Name:", "publish-location": { "exact": "exact position", @@ -376,15 +386,15 @@ }, "publish-name": { "alias-or-initials": "Username or initials (Default)", - "alias-or-initials-tooltip": "username if exists or Initials of firstname and lastname", - "first": "firstname", - "first-tooltip": "the firstname only", - "first-initial": "firstname and initial", - "first-initial-tooltip": "firstname plus initial of lastname", + "alias-or-initials-tooltip": "username, if available, or the initials of the first name and last name, the first two letters in each case", + "first": "Firstname", + "first-tooltip": "the first name only", + "first-initial": "First name and initial", + "first-initial-tooltip": "first name plus the first two initial letters of the last name", "initials": "Initials", - "initials-tooltip": "Initials of firstname and lastname independent if username exists", - "name-full": "firstname and lastname", - "name-full-tooltip": "fullname: firstname plus lastname" + "initials-tooltip": "Initials of first name and last name, i.e. the first two letters of each regardless of the existence of the user name", + "name-full": "first name and last name", + "name-full-tooltip": "full name: first name plus last name" }, "showAmountGDD": "Your GDD amount is visible.", "showAmountGDT": "Your GDT amount is visible.", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index cc01acb9c..b220c13a0 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -20,6 +20,11 @@ } }, "back": "Volver", + "circles": { + "headline": "Juntos nos apoyamos - atentos a la cultura de los círculos.", + "text": "Haga clic en el botón para abrir la plataforma de cooperación en una nueva ventana del navegador.", + "button": "Iniciar Círculos..." + }, "community": { "admins": "Administradores", "choose-another-community": "Escoger otra comunidad", @@ -213,7 +218,8 @@ "recruited-member": "Miembro invitado" }, "language": "Idioma", - "link-load": "recargar el último enlace |recargar los últimos {n} enlaces | descargar más {n} enlaces", + "link-load": "recargar el último enlace | recargar los últimos {n} enlaces", + "link-load-more": "descargar más {n} enlaces", "login": "iniciar sesión", "math": { "aprox": "~", @@ -243,6 +249,7 @@ "overview": "Resumen", "profile": "Mi Perfil", "send": "Enviar", + "circles": "Círculos", "support": "Soporte", "transactions": "Transacciones", "usersearch": "Buscar usuarios" @@ -256,6 +263,7 @@ "send": "Enviar Gradidos", "settings": "Soporte", "transactions": "Tu Transacciones", + "circles": "Círculos Gradido", "usersearch": "Búsqueda geográfica de usuarios" }, "qrCode": "Código QR", @@ -268,6 +276,23 @@ "warningText": "Aún estas?" }, "settings": { + "GMS": { + "location": { + "button": "¡Haz clic aquí!", + "label": "Ubicación exacta", + "saveLocation": "Guardar ubicación", + "updateSuccess": "Ubicación guardada exitosamente" + }, + "map": { + "communityCoords": "Ubicación de tu comunidad: Lat {lat}, Lng {lng}", + "communityLocationLabel": "Ubicación de tu comunidad", + "headline": "Captura de ubicación geográfica del usuario", + "userCoords": "Tu ubicación: Lat {lat}, Lng {lng}", + "userLocationLabel": "Tu ubicación", + "search": "Buscar una ubicación", + "success": "Ubicación guardada exitosamente" + } + }, "language": { "changeLanguage": "Cambiar idioma", "de": "Deutsch", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 408983190..e1ff9acb9 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -22,6 +22,11 @@ } }, "back": "Retour", + "circles": { + "headline": "Ensemble, nous nous soutenons mutuellement - attentifs à la culture du cercle.", + "text": "En cliquant sur le bouton, tu ouvres la plateforme de coopération dans une nouvelle fenêtre de navigation.", + "button": "Démarrer les Cercles..." + }, "community": { "admins": "Administrateurs", "choose-another-community": "Choisissez une autre communauté", @@ -221,7 +226,8 @@ }, "h": "h", "language": "Langage", - "link-load": "Enregistrer le dernier lien | Enregistrer les derniers {n} liens | Enregistrer plus de {n} liens", + "link-load": "Enregistrer le dernier lien | Enregistrer les derniers {n} liens", + "link-load-more": "Enregistrer plus de {n} liens", "login": "Connexion", "math": { "asterisk": "*", @@ -251,6 +257,7 @@ "overview": "Aperçu", "send": "Envoyer", "settings": "Configuration", + "circles": "Cercles", "support": "Aide", "transactions": "Transactions", "usersearch": "Recherche d'utilisateurs" @@ -264,6 +271,7 @@ "send": "Envoyé Gradidos", "settings": "Configuration", "transactions": "Vos transactions", + "circles": "Cercles Gradido", "usersearch": "Recherche géographique d'utilisateurs" }, "qrCode": "QR Code", @@ -276,6 +284,23 @@ "warningText": "Êtes-vous toujours connecté?" }, "settings": { + "GMS": { + "location": { + "button": "Cliquez ici !", + "label": "Emplacement précis", + "saveLocation": "Enregistrer l'emplacement", + "updateSuccess": "Emplacement enregistré avec succès" + }, + "map": { + "communityCoords": "Emplacement de votre communauté : Lat {lat}, Long {lng}", + "communityLocationLabel": "Emplacement de votre communauté", + "headline": "Capture de la localisation géographique de l'utilisateur", + "userCoords": "Votre emplacement : Lat {lat}, Long {lng}", + "userLocationLabel": "Votre emplacement", + "search": "Rechercher un emplacement", + "success": "Emplacement enregistré avec succès" + } + }, "hideAmountGDD": "Votre montant GDD est caché.", "hideAmountGDT": "Votre montant GDT est caché.", "language": { diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index a0b6aefad..d09634c80 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -20,6 +20,11 @@ } }, "back": "Terug", + "circles": { + "headline": "Samen ondersteunen we elkaar - mindful in de cirkelcultuur.", + "text": "Klik op de knop om het samenwerkingsplatform te openen in een nieuw browservenster.", + "button": "Cirkels starten..." + }, "community": { "admins": "Beheerders", "choose-another-community": "Kies een andere gemeenschap", @@ -213,7 +218,8 @@ "recruited-member": "Uitgenodigd lid" }, "language": "Taal", - "link-load": "de laatste link herladen | de laatste links herladen | verdere {n} links herladen", + "link-load": "de laatste link herladen | de laatste links herladen", + "link-load-more": "verdere {n} links herladen", "login": "Aanmelding", "math": { "aprox": "~", @@ -243,6 +249,7 @@ "overview": "Overzicht", "profile": "Mijn profiel", "send": "Sturen", + "circles": "Cirkels", "support": "Support", "transactions": "Transacties", "usersearch": "Gebruiker zoeken" @@ -256,6 +263,7 @@ "send": "Send Gradidos", "settings": "Settings", "transactions": "Your transactions", + "circles": "Gradido Cirkels", "usersearch": "Geografisch zoeken naar gebruikers" }, "qrCode": "QR Code", @@ -268,6 +276,23 @@ "warningText": "Ben je er nog?" }, "settings": { + "GMS": { + "location": { + "button": "Klik hier!", + "label": "Exacte locatie", + "saveLocation": "Locatie opslaan", + "updateSuccess": "Locatie succesvol opgeslagen" + }, + "map": { + "communityCoords": "Locatie van je gemeenschap: Lat {lat}, Lng {lng}", + "communityLocationLabel": "Locatie van je gemeenschap", + "headline": "Geografische locatiebepaling van de gebruiker", + "userCoords": "Jouw locatie: Lat {lat}, Lng {lng}", + "userLocationLabel": "Jouw locatie", + "search": "Zoek een locatie", + "success": "Locatie succesvol opgeslagen" + } + }, "language": { "changeLanguage": "Taal veranderen", "de": "Deutsch", diff --git a/frontend/src/locales/tr.json b/frontend/src/locales/tr.json index 3e3ac0a04..e4c6d3385 100644 --- a/frontend/src/locales/tr.json +++ b/frontend/src/locales/tr.json @@ -204,7 +204,8 @@ "recruited-member": "Davetli üye" }, "language": "Dil", - "link-load": "Son linki yükle| Son {n} linki yükle | {n} link daha yükle", + "link-load": "Son linki yükle| Son {n} linki yükle", + "link-load-more": "{n} link daha yükle", "login": "Giriş", "math": { "aprox": "~", @@ -245,6 +246,23 @@ "warningText": "Hala orada mısın?" }, "settings": { + "GMS": { + "location": { + "button": "Buraya tıklayın!", + "label": "Tam konum", + "saveLocation": "Konumu Kaydet", + "updateSuccess": "Konum başarıyla kaydedildi" + }, + "map": { + "communityCoords": "Topluluk Konumunuz: Enlem {lat}, Boylam {lng}", + "communityLocationLabel": "Topluluk Konumunuz", + "headline": "Kullanıcının Coğrafi Konum Tespiti", + "userCoords": "Konumunuz: Enlem {lat}, Boylam {lng}", + "userLocationLabel": "Konumunuz", + "search": "Konum ara", + "success": "Konum başarıyla kaydedildi" + } + }, "language": { "changeLanguage": "Dili değiştir", "de": "Deutsch", diff --git a/frontend/src/main.js b/frontend/src/main.js index cd1940802..b9f1537f7 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,7 +1,9 @@ import { createApp } from 'vue' -import 'bootstrap/dist/css/bootstrap.css' +// import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue-next/dist/bootstrap-vue-next.css' +import './assets/css/gradido.css' +// import './assets/scss/gradido.scss' import App from './App' import i18n from './i18n.js' diff --git a/frontend/src/pages/Circles.spec.js b/frontend/src/pages/Circles.spec.js index 66eb3fcf1..8f22573f4 100644 --- a/frontend/src/pages/Circles.spec.js +++ b/frontend/src/pages/Circles.spec.js @@ -76,7 +76,7 @@ describe('Circles', () => { null, expect.objectContaining({ fetchPolicy: 'network-only', - enabled: false, + enabled: true, }), ) expect(mockRefetch).toHaveBeenCalled() diff --git a/frontend/src/pages/Circles.vue b/frontend/src/pages/Circles.vue index 7dc315342..f9cef737e 100644 --- a/frontend/src/pages/Circles.vue +++ b/frontend/src/pages/Circles.vue @@ -51,7 +51,7 @@ const { onError, } = useQuery(authenticateHumhubAutoLogin, null, { fetchPolicy: 'network-only', - enabled: false, + enabled: true, }) onResult(({ data }) => { diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index 064aee33c..6ab7d9875 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -11,7 +11,7 @@
{ return `${form.value.id}_${form.value.date}_${form.value.memo}_${form.value.amount}_${form.value.hours}` }) -const { onResult: onOpenCreationsResult, refetch: refetchOpenCreations } = useQuery(openCreations) +const { onResult: onOpenCreationsResult, refetch: refetchOpenCreations } = useQuery( + openCreations, + () => ({}), + { + fetchPolicy: 'network-only', + }, +) const { onResult: onListAllContributionsResult, refetch: refetchAllContributions } = useQuery( listAllContributions, () => ({ diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue index 26828257e..f02fc64ed 100644 --- a/frontend/src/pages/Login.vue +++ b/frontend/src/pages/Login.vue @@ -19,10 +19,11 @@ - + { }) it('displays a message that firstname is required', async () => { + // First set some value to make the field dirty + await wrapper.find('#registerFirstname').setValue('test') + await wrapper.find('#registerFirstname').trigger('blur') + + // Then clear it to trigger validation await wrapper.find('#registerFirstname').setValue('') await wrapper.find('#registerFirstname').trigger('blur') await flushPromises() + + expect(wrapper.find('#registerFirstnameLiveFeedback').exists()).toBe(true) expect(wrapper.find('#registerFirstnameLiveFeedback').text()).toBe( 'The field firstname is invalid', ) }) it('displays a message that lastname is required', async () => { + // First set some value to make the field dirty + await wrapper.find('#registerLastname').setValue('test') + await wrapper.find('#registerLastname').trigger('blur') + + // Then clear it to trigger validation await wrapper.find('#registerLastname').setValue('') await wrapper.find('#registerLastname').trigger('blur') await flushPromises() + + expect(wrapper.find('#registerLastnameLiveFeedback').exists()).toBe(true) expect(wrapper.find('#registerLastnameLiveFeedback').text()).toBe( 'The field lastname is invalid', ) diff --git a/frontend/src/pages/Settings.vue b/frontend/src/pages/Settings.vue index b4b2d458b..53a926d39 100644 --- a/frontend/src/pages/Settings.vue +++ b/frontend/src/pages/Settings.vue @@ -81,100 +81,102 @@ -
- -
{{ $t('settings.allow-community-services') }}
-
- + +
{{ $t('settings.allow-community-services') }}
+
+ + +
{{ $t('Humhub.title') }}
+
+ + + +
+
{{ $t('Humhub.desc') }}
+ + + {{ $t('settings.humhub.naming-format') }} + + + + + +
+
+ + +
{{ $t('GMS.title') }}
+
+ + + +
+
{{ $t('GMS.desc') }}
+
+ -
{{ $t('Humhub.title') }}
-
- - - -
-
{{ $t('Humhub.desc') }}
- - - {{ $t('settings.humhub.naming-format') }} + {{ $t('settings.GMS.naming-format') }} -
-
- + -
{{ $t('GMS.title') }}
+ {{ $t('settings.GMS.location-format') }}
- - + +
-
{{ $t('GMS.desc') }}
-
- - - {{ $t('settings.GMS.naming-format') }} - - - - - - - - {{ $t('settings.GMS.location-format') }} - - - - - - - - {{ $t('settings.GMS.location.label') }} - - - - - -
-
-
- + -
{{ $t('GMS.title') }}
+ {{ $t('settings.GMS.location.label') }}
- - + +
-
{{ $t('GMS.desc') }}
- -
+
+
+ + +
{{ $t('GMS.title') }}
+
+ + + +
+
{{ $t('GMS.desc') }}
+
+