diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3c097993..18d1143db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -185,7 +185,7 @@ jobs: ########################################################################## # LOCALES FRONTEND ####################################################### ########################################################################## - - name: frontend | Locales + - name: Frontend | Locales run: docker run --rm gradido/frontend:test yarn run locales ############################################################################## @@ -214,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 ################################################## ############################################################################## @@ -247,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 @@ -380,7 +438,7 @@ jobs: report_name: Coverage Frontend type: lcov result_path: ./coverage/lcov.info - min_coverage: 94 + min_coverage: 95 token: ${{ github.token }} ############################################################################## @@ -451,16 +509,13 @@ jobs: ########################################################################## # UNIT TESTS BACKEND ##################################################### ########################################################################## - - name: backend | docker-compose + - 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: Sleep for 30 seconds - run: sleep 30s - shell: bash - name: backend Unit tests | 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 @@ -473,7 +528,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 48 + min_coverage: 54 token: ${{ github.token }} ########################################################################## diff --git a/admin/.dockerignore b/admin/.dockerignore index 88249539f..1ddda051a 100644 --- a/admin/.dockerignore +++ b/admin/.dockerignore @@ -1,3 +1,4 @@ node_modules .git -.gitignore \ No newline at end of file +.gitignore +!.eslintignore \ No newline at end of file diff --git a/admin/.env.dist b/admin/.env.dist index 66c84dda8..d7044669a 100644 --- a/admin/.env.dist +++ b/admin/.env.dist @@ -1,3 +1,5 @@ +CONFIG_VERSION=v1.2022-03-18 + GRAPHQL_URI=http://localhost:4000/graphql WALLET_AUTH_URL=http://localhost/authenticate?token={token} WALLET_URL=http://localhost/login diff --git a/admin/.env.template b/admin/.env.template index a965b1bb1..488c9aba4 100644 --- a/admin/.env.template +++ b/admin/.env.template @@ -1,3 +1,5 @@ +CONFIG_VERSION=$ADMIN_CONFIG_VERSION + GRAPHQL_URI=$GRAPHQL_URI WALLET_AUTH_URL=$WALLET_AUTH_URL WALLET_URL=$WALLET_URL diff --git a/admin/.eslintignore b/admin/.eslintignore index 433390392..94934f9e7 100644 --- a/admin/.eslintignore +++ b/admin/.eslintignore @@ -1,4 +1,3 @@ -node_modules -coverage -**/*.min.js -dist \ No newline at end of file +node_modules/ +dist/ +coverage/ \ No newline at end of file diff --git a/admin/.eslintrc.js b/admin/.eslintrc.js index 8c410feba..73dce291f 100644 --- a/admin/.eslintrc.js +++ b/admin/.eslintrc.js @@ -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', + }, + }, } diff --git a/admin/.stylelintrc.js b/admin/.stylelintrc.js new file mode 100644 index 000000000..061b87a79 --- /dev/null +++ b/admin/.stylelintrc.js @@ -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"], + } + ] + +}; \ No newline at end of file diff --git a/admin/package.json b/admin/package.json index d499117af..2b4be2851 100644 --- a/admin/package.json +++ b/admin/package.json @@ -9,11 +9,13 @@ "scripts": { "start": "node run/server.js", "serve": "vue-cli-service serve --open", - "dev": "yarn run serve", "build": "vue-cli-service build", - "lint": "eslint --max-warnings=0 --ext .js,.vue .", + "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/missing-keys.sh && scripts/sort.sh" + "locales": "scripts/sort.sh" }, "dependencies": { "@babel/core": "^7.15.8", @@ -49,6 +51,7 @@ }, "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", @@ -65,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": [ diff --git a/admin/src/components/ContentFooter.vue b/admin/src/components/ContentFooter.vue index 510318e61..c10e53596 100644 --- a/admin/src/components/ContentFooter.vue +++ b/admin/src/components/ContentFooter.vue @@ -4,24 +4,24 @@ diff --git a/admin/src/components/CreationTransactionListFormular.spec.js b/admin/src/components/CreationTransactionListFormular.spec.js index 5acff8ab7..fb137e516 100644 --- a/admin/src/components/CreationTransactionListFormular.spec.js +++ b/admin/src/components/CreationTransactionListFormular.spec.js @@ -6,32 +6,30 @@ const localVue = global.localVue const apolloQueryMock = jest.fn().mockResolvedValue({ data: { - transactionList: { - transactions: [ - { - id: 1, - amount: 100, - balanceDate: 0, - creationDate: new Date(), - memo: 'Testing', - linkedUser: { - firstName: 'Gradido', - lastName: 'Akademie', - }, + creationTransactionList: [ + { + id: 1, + amount: 100, + balanceDate: 0, + creationDate: new Date(), + memo: 'Testing', + linkedUser: { + firstName: 'Gradido', + lastName: 'Akademie', }, - { - id: 2, - amount: 200, - balanceDate: 0, - creationDate: new Date(), - memo: 'Testing 2', - linkedUser: { - firstName: 'Gradido', - lastName: 'Akademie', - }, + }, + { + id: 2, + amount: 200, + balanceDate: 0, + creationDate: new Date(), + memo: 'Testing 2', + linkedUser: { + firstName: 'Gradido', + lastName: 'Akademie', }, - ], - }, + }, + ], }, }) @@ -67,7 +65,6 @@ describe('CreationTransactionListFormular', () => { currentPage: 1, pageSize: 25, order: 'DESC', - onlyCreations: true, userId: 1, }, }), diff --git a/admin/src/components/CreationTransactionListFormular.vue b/admin/src/components/CreationTransactionListFormular.vue index 0b78ca4b8..ce2b136a4 100644 --- a/admin/src/components/CreationTransactionListFormular.vue +++ b/admin/src/components/CreationTransactionListFormular.vue @@ -5,7 +5,7 @@ + diff --git a/admin/src/plugins/apolloProvider.js b/admin/src/plugins/apolloProvider.js index cde5408da..1f51be20a 100644 --- a/admin/src/plugins/apolloProvider.js +++ b/admin/src/plugins/apolloProvider.js @@ -2,7 +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 i18n from '../i18n' const httpLink = new HttpLink({ uri: CONFIG.GRAPHQL_URI }) @@ -15,7 +14,6 @@ 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) window.location.assign(CONFIG.WALLET_URL) return response diff --git a/admin/vue.config.js b/admin/vue.config.js index 18929c38c..8cc1e4b89 100644 --- a/admin/vue.config.js +++ b/admin/vue.config.js @@ -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'), } diff --git a/admin/yarn.lock b/admin/yarn.lock index 4e5d587e5..d5fae27fe 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -932,6 +932,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.0": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7", "@babel/template@^7.0.0", "@babel/template@^7.15.4", "@babel/template@^7.3.3", "@babel/template@^7.4.0": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -1001,6 +1008,21 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -1042,6 +1064,83 @@ cssnano-preset-default "^4.0.0" postcss "^7.0.0" +"@intlify/core-base@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.1.9.tgz#e4e8c951010728e4af3a0d13d74cf3f9e7add7f6" + integrity sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw== + dependencies: + "@intlify/devtools-if" "9.1.9" + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@intlify/vue-devtools" "9.1.9" + +"@intlify/devtools-if@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.1.9.tgz#a30e1dd1256ff2c5c98d8d75d075384fba898e5d" + integrity sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ== + dependencies: + "@intlify/shared" "9.1.9" + +"@intlify/eslint-plugin-vue-i18n@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@intlify/eslint-plugin-vue-i18n/-/eslint-plugin-vue-i18n-1.4.0.tgz#f8fe791892c2dce7d189a364b6a908c87e1c3ac9" + integrity sha512-anB1eBf6rpxpWyW883gi6O1hozQy4Q02VyzyodOUnohOqT07GATVSxnr2J9/qQSV47xWukV+9LiRErJcU7d/uA== + dependencies: + "@eslint/eslintrc" "^1.2.0" + "@intlify/core-base" "^9.1.9" + "@intlify/message-compiler" "^9.1.9" + debug "^4.3.1" + glob "^7.1.3" + ignore "^5.0.5" + is-language-code "^3.1.0" + js-yaml "^4.0.0" + json5 "^2.1.3" + jsonc-eslint-parser "^2.0.0" + lodash "^4.17.11" + parse5 "^6.0.0" + semver "^7.3.4" + vue-eslint-parser "^8.0.0" + yaml-eslint-parser "^0.5.0" + +"@intlify/message-compiler@9.1.9", "@intlify/message-compiler@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.1.9.tgz#1193cbd224a71c2fb981455b8534a3c766d2948d" + integrity sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + source-map "0.6.1" + +"@intlify/message-resolver@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-resolver/-/message-resolver-9.1.9.tgz#3155ccd2f5e6d0dc16cad8b7f1d8e97fcda05bfc" + integrity sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA== + +"@intlify/runtime@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/runtime/-/runtime-9.1.9.tgz#2c12ce29518a075629efed0a8ed293ee740cb285" + integrity sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg== + dependencies: + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + +"@intlify/shared@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.1.9.tgz#0baaf96128b85560666bec784ffb01f6623cc17a" + integrity sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw== + +"@intlify/vue-devtools@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz#2be8f4dbe7f7ed4115676eb32348141d411e426b" + integrity sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1673,6 +1772,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "@types/prettier@^2.0.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.1.tgz#e1303048d5389563e130f5bdd89d37a99acb75eb" @@ -2394,6 +2498,11 @@ acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== +acorn@^8.5.0, acorn@^8.7.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + address@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -2640,6 +2749,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -2712,6 +2826,11 @@ array.prototype.flat@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + asn1.js@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" @@ -3508,6 +3627,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + base64-js@^1.0.2: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3923,6 +4047,15 @@ camel-case@3.0.x: no-case "^2.2.0" upper-case "^1.1.1" +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -4141,6 +4274,16 @@ cli-spinners@^2.0.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== +cli-table3@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -4191,6 +4334,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" + clone@2.x: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -4268,6 +4418,16 @@ color@^3.0.0: color-convert "^1.9.3" color-string "^1.6.0" +colord@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" + integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== + +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -4486,6 +4646,17 @@ cosmiconfig@^5.0.0: js-yaml "^3.13.1" parse-json "^4.0.0" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -4576,6 +4747,11 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" +css-functions-list@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.0.1.tgz#1460df7fb584d1692c30b105151dbb988c8094f9" + integrity sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw== + css-loader@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" @@ -4825,7 +5001,22 @@ debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -decamelize@^1.2.0: +debug@^4.3.0, debug@^4.3.1, debug@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +decamelize-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -4862,7 +5053,7 @@ deepmerge@^1.5.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== -deepmerge@^4.2.2: +deepmerge@^4.2.0, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== @@ -5101,6 +5292,13 @@ domhandler@^4.0.0, domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" +domhandler@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== + dependencies: + domelementtype "^2.2.0" + domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -5109,7 +5307,7 @@ domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.5.2, domutils@^2.6.0: +domutils@^2.5.2, domutils@^2.6.0, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -5118,6 +5316,14 @@ domutils@^2.5.2, domutils@^2.6.0: domelementtype "^2.2.0" domhandler "^4.2.0" +dot-object@^1.7.1: + version "1.9.0" + resolved "https://registry.yarnpkg.com/dot-object/-/dot-object-1.9.0.tgz#6e3d6d8379f794c5174599ddf05528f5990f076e" + integrity sha512-7MPN6y7XhAO4vM4eguj5+5HNKLjJYfkVG1ZR1Aput4Q4TR6SYeSjhpVQ77IzJHoSHffKbDxBC+48aCiiRurDPw== + dependencies: + commander "^2.20.0" + glob "^7.1.4" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -5273,6 +5479,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -5513,6 +5724,14 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -5542,6 +5761,11 @@ eslint-visitor-keys@^3.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186" integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q== +eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + eslint@7.25.0: version "7.25.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.25.0.tgz#1309e4404d94e676e3e831b3a3ad2b050031eb67" @@ -5585,6 +5809,11 @@ eslint@7.25.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +esm@^3.2.13: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + espree@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" @@ -5603,6 +5832,15 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" +espree@^9.0.0, espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -5734,6 +5972,13 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== + dependencies: + clone-regexp "^2.1.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -5905,6 +6150,17 @@ fast-glob@^3.1.1: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.2.11, fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -5915,6 +6171,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -6076,6 +6337,11 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flat@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flatted@^3.1.0: version "3.2.2" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" @@ -6239,6 +6505,11 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -6310,6 +6581,22 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -6339,6 +6626,18 @@ globby@^11.0.4: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -6376,6 +6675,11 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -6424,6 +6728,11 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + harmony-reflect@^1.4.6: version "1.6.2" resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" @@ -6569,6 +6878,13 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -6659,6 +6975,16 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" +htmlparser2@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" + integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.2" + domutils "^2.8.0" + entities "^3.0.1" + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -6808,6 +7134,11 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.0.5, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + ignore@^5.1.1, ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" @@ -6843,6 +7174,11 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -6864,6 +7200,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -6897,7 +7238,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: +ini@^1.3.4, ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7074,6 +7415,13 @@ is-core-module@^2.2.0, is-core-module@^2.7.0: dependencies: has "^1.0.3" +is-core-module@^2.5.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -7169,6 +7517,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-language-code@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-language-code/-/is-language-code-3.1.0.tgz#b2386b49227e7010636f16d0c2c681ca40136ab5" + integrity sha512-zJdQ3QTeLye+iphMeK3wks+vXSRFKh68/Pnlw7aOfApFSEIOhYa8P9vwwa6QrImNNBMJTiL1PpYF0f4BxDuEgA== + dependencies: + "@babel/runtime" "^7.14.0" + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -7217,7 +7572,7 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-plain-obj@^1.0.0: +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= @@ -7234,6 +7589,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -7247,6 +7607,11 @@ is-regex@^1.0.4, is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== + is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -7286,6 +7651,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-valid-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" + integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= + is-weakref@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" @@ -8339,6 +8709,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.0.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8491,7 +8868,7 @@ json3@^3.3.3: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.2: +json5@2.x, json5@^2.1.2, json5@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== @@ -8510,6 +8887,16 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-eslint-parser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz#4c126b530aa583d85308d0b3041ff81ce402bbb2" + integrity sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g== + dependencies: + acorn "^8.5.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + semver "^7.3.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -8551,7 +8938,7 @@ kind-of@^5.0.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -8561,6 +8948,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +known-css-properties@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.24.0.tgz#19aefd85003ae5698a5560d2b55135bf5432155c" + integrity sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA== + launch-editor-middleware@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz#e14b07e6c7154b0a4b86a0fd345784e45804c157" @@ -8821,6 +9213,16 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -8828,6 +9230,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -8868,6 +9275,24 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -8885,7 +9310,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3, merge2@^1.3.0: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -8962,6 +9387,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + mini-css-extract-plugin@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" @@ -8989,6 +9419,15 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -9096,6 +9535,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== +nanoid@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -9266,6 +9710,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" @@ -9288,6 +9742,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= + normalize-url@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -9710,7 +10169,7 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parse5@6.0.1, parse5@^6.0.1: +parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -9984,6 +10443,15 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" +postcss-html@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-1.3.0.tgz#5b28b44a9cc8351cdba6ae1f26017c2303bd7642" + integrity sha512-ewbwd7OGW4dLsErtvZH9HpVMEcXnlhYSzKsr7MepGlOT8imHTIZ/+pdfEruLS+hTYapLTQAWDnoQcJpsYU4uRw== + dependencies: + htmlparser2 "^7.1.2" + postcss "^8.4.0" + postcss-safe-parser "^6.0.0" + postcss-load-config@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" @@ -10002,6 +10470,11 @@ postcss-loader@^3.0.0: postcss-load-config "^2.0.0" schema-utils "^1.0.0" +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= + postcss-merge-longhand@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" @@ -10207,6 +10680,21 @@ postcss-reduce-transforms@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss-scss@^4.0.2, postcss-scss@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.3.tgz#36c23c19a804274e722e83a54d20b838ab4767ac" + integrity sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA== + postcss-selector-parser@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" @@ -10224,6 +10712,14 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" + integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" @@ -10252,6 +10748,11 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: version "7.0.39" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" @@ -10260,6 +10761,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.3 picocolors "^0.2.1" source-map "^0.6.1" +postcss@^8.4.0, postcss@^8.4.6, postcss@^8.4.8: + version "8.4.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032" + integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -10480,6 +10990,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -10601,6 +11116,14 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + regenerate-unicode-properties@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" @@ -11040,7 +11563,7 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -11199,6 +11722,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -11298,6 +11826,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -11322,16 +11855,16 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -11386,6 +11919,11 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +specificity@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" + integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -11537,7 +12075,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^2.0.0: +string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -11646,6 +12184,13 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -11656,6 +12201,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -11665,6 +12215,112 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +stylelint-config-html@>=1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz#172acb996ee4c854e8c670fcff77ff0d98d3bec2" + integrity sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ== + +stylelint-config-recommended-scss@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz#193f483861c76a36ece24c52eb6baca4838f4a48" + integrity sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg== + dependencies: + postcss-scss "^4.0.2" + stylelint-config-recommended "^6.0.0" + stylelint-scss "^4.0.0" + +stylelint-config-recommended-vue@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.3.0.tgz#a40f1efa5bff8bd3e2c1c58c3faacbe73b5b69ec" + integrity sha512-UEY/MZlBO2yiQdZoJm33trtqJ1MEDzBADhIAM0pTvfYbyXe5KwdbTtmALHETG1cXfDxuuUbJTvL9uENMy2Rjug== + dependencies: + semver "^7.3.5" + stylelint-config-html ">=1.0.0" + stylelint-config-recommended ">=6.0.0" + +stylelint-config-recommended@>=6.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz#7497372ae83ab7a6fffc18d7d7b424c6480ae15e" + integrity sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q== + +stylelint-config-recommended@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz#fd2523a322836005ad9bf473d3e5534719c09f9d" + integrity sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw== + +stylelint-config-standard-scss@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz#dafc4fa5538d0ed833bf0a7d391e075683ffd96c" + integrity sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw== + dependencies: + stylelint-config-recommended-scss "^5.0.2" + stylelint-config-standard "^24.0.0" + +stylelint-config-standard@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz#6823f207ab997ae0b641f9a636d007cc44d77541" + integrity sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw== + dependencies: + stylelint-config-recommended "^6.0.0" + +stylelint-scss@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.1.0.tgz#39b808696f8152081163d970449257ff80b5c041" + integrity sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA== + dependencies: + lodash "^4.17.21" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.6" + postcss-value-parser "^4.1.0" + +stylelint@^14.5.3: + version "14.5.3" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.5.3.tgz#103b6670128ba3dea69fe3a1a07c4a5d3e0e3450" + integrity sha512-omHETL+kGHR+fCXFK1SkZD/A+emCP9esggAdWEl8GPjTNeyRYj+H6uetRDcU+7E451zwWiUYGVAX+lApsAZgsQ== + dependencies: + balanced-match "^2.0.0" + colord "^2.9.2" + cosmiconfig "^7.0.1" + css-functions-list "^3.0.1" + debug "^4.3.3" + execall "^2.0.0" + fast-glob "^3.2.11" + fastest-levenshtein "^1.0.12" + file-entry-cache "^6.0.1" + get-stdin "^8.0.0" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.1.0" + ignore "^5.2.0" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.24.0" + mathml-tag-names "^2.1.3" + meow "^9.0.0" + micromatch "^4.0.4" + normalize-path "^3.0.0" + normalize-selector "^0.2.0" + picocolors "^1.0.0" + postcss "^8.4.6" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + specificity "^0.4.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + style-search "^0.1.0" + supports-hyperlinks "^2.2.0" + svg-tags "^1.0.0" + table "^6.8.0" + v8-compile-cache "^2.3.0" + write-file-atomic "^4.0.1" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -11698,7 +12354,7 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: +supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -11752,6 +12408,17 @@ table@^6.0.4: string-width "^4.2.3" strip-ansi "^6.0.1" +table@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -11995,6 +12662,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -12101,6 +12773,11 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -12359,7 +13036,7 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.3: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -12421,6 +13098,20 @@ vue-apollo@^3.0.8: serialize-javascript "^4.0.0" throttle-debounce "^2.1.0" +vue-cli-plugin-i18n@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/vue-cli-plugin-i18n/-/vue-cli-plugin-i18n-2.3.1.tgz#193b1e481a02e044d847392b3d070e1a3dec954e" + integrity sha512-1bNVZtLIAL9Pge8hiw986vixofyqF/tlgsqe4fF5JWn9c8xhsqVugEBuUeaYxevrE9efhhFk9mRmEDwBwQnbNg== + dependencies: + debug "^4.3.0" + deepmerge "^4.2.0" + dotenv "^8.2.0" + flat "^5.0.0" + rimraf "^3.0.0" + vue "^2.6.11" + vue-i18n "^8.17.0" + vue-i18n-extract "1.0.2" + vue-eslint-parser@^7.10.0: version "7.11.0" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz#214b5dea961007fcffb2ee65b8912307628d0daf" @@ -12434,6 +13125,19 @@ vue-eslint-parser@^7.10.0: lodash "^4.17.21" semver "^6.3.0" +vue-eslint-parser@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz#5d31129a1b3dd89c0069ca0a1c88f970c360bd0d" + integrity sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g== + dependencies: + debug "^4.3.2" + eslint-scope "^7.0.0" + eslint-visitor-keys "^3.1.0" + espree "^9.0.0" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.5" + vue-functional-data-merge@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz#08a7797583b7f35680587f8a1d51d729aa1dc657" @@ -12444,6 +13148,23 @@ vue-hot-reload-api@^2.3.0: resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== +vue-i18n-extract@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vue-i18n-extract/-/vue-i18n-extract-1.0.2.tgz#0a136e12d1634d6799e187aad81a7003d02f67a5" + integrity sha512-+zwDKvle4KcfloXZnj5hF01ViKDiFr5RMx5507D7oyDXpSleRpekF5YHgZa/+Ra6Go68//z0Nya58J9tKFsCjw== + dependencies: + cli-table3 "^0.5.1" + dot-object "^1.7.1" + esm "^3.2.13" + glob "^7.1.3" + is-valid-glob "^1.0.0" + yargs "^13.2.2" + +vue-i18n@^8.17.0: + version "8.27.0" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.27.0.tgz#3e3b3ed2c107ccbd7f20dbdd7a96763a9990253e" + integrity sha512-SX35iJHL5PJ4Gfh0Mo/q0shyHiI2V6Zkh51c+k8E9O1RKv5BQyYrCxRzpvPrsIOJEnLaeiovet3dsUB0e/kDzw== + vue-i18n@^8.26.5: version "8.26.5" resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.26.5.tgz#b61e994e7ae83564c6aebd004543156bbdc7c7e9" @@ -12818,7 +13539,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9, which@^1.3.0: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -12895,6 +13616,14 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" + integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + ws@^5.2.0: version "5.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" @@ -12954,6 +13683,20 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml-eslint-parser@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.5.0.tgz#01d4e4d992a820769ea85ef5fd526dfc20ebc6f5" + integrity sha512-nJeyLA3YHAzhBTZbRAbu3W6xrSCucyxExmA+ZDtEdUFpGllxAZpto2Zxo2IG0r0eiuEiBM4e+wiAdxTziTq94g== + dependencies: + eslint-visitor-keys "^3.0.0" + lodash "^4.17.21" + yaml "^1.10.2" + +yaml@^1.10.0, yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@10.x: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -12977,12 +13720,12 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^13.3.0, yargs@^13.3.2: +yargs@^13.2.2, yargs@^13.3.0, yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== diff --git a/backend/.env.dist b/backend/.env.dist index b1b16972f..b8a3d5dbe 100644 --- a/backend/.env.dist +++ b/backend/.env.dist @@ -1,3 +1,5 @@ +CONFIG_VERSION=v1.2022-03-18 + # Server PORT=4000 JWT_SECRET=secret123 @@ -38,9 +40,9 @@ EMAIL_SENDER=info@gradido.net EMAIL_PASSWORD=xxx EMAIL_SMTP_URL=gmail.com EMAIL_SMTP_PORT=587 -EMAIL_LINK_VERIFICATION=http://localhost/checkEmail/{code} -EMAIL_LINK_SETPASSWORD=http://localhost/reset/{code} -RESEND_TIME=10 +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 \ No newline at end of file diff --git a/backend/.env.template b/backend/.env.template index b3a5eb4c6..454b25d3c 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -1,3 +1,5 @@ +CONFIG_VERSION=$BACKEND_CONFIG_VERSION + # Server JWT_SECRET=$JWT_SECRET JWT_EXPIRES_IN=10m diff --git a/backend/jest.config.js b/backend/jest.config.js index 0e4643c63..6ab44002c 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -3,9 +3,16 @@ module.exports = { verbose: true, preset: 'ts-jest', collectCoverage: true, - collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'], + collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], setupFiles: ['/test/testSetup.ts'], + modulePathIgnorePatterns: ['/build/'], moduleNameMapper: { + '@/(.*)': '/src/$1', + '@model/(.*)': '/src/graphql/model/$1', + '@arg/(.*)': '/src/graphql/arg/$1', + '@enum/(.*)': '/src/graphql/enum/$1', + '@repository/(.*)': '/src/typeorm/repository/$1', + '@test/(.*)': '/test/$1', '@entity/(.*)': process.env.NODE_ENV === 'development' ? '/../database/entity/$1' diff --git a/backend/package.json b/backend/package.json index c27419ddb..3ce04bded 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,10 +10,11 @@ "scripts": { "build": "tsc --build", "clean": "tsc --build --clean", - "start": "node build/src/index.js", - "dev": "nodemon -w src --ext ts --exec ts-node src/index.ts", + "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" + "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", @@ -31,7 +32,6 @@ "jest": "^27.2.4", "jsonwebtoken": "^8.5.1", "lodash.clonedeep": "^4.5.0", - "module-alias": "^2.2.2", "mysql2": "^2.3.0", "nodemailer": "^6.6.5", "random-bigint": "^0.0.1", @@ -42,6 +42,7 @@ }, "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" } } diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index 0777211ad..824d40af2 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -1,7 +1,7 @@ /* 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() diff --git a/backend/src/auth/INALIENABLE_RIGHTS.ts b/backend/src/auth/INALIENABLE_RIGHTS.ts index 10d745806..348cd5b20 100644 --- a/backend/src/auth/INALIENABLE_RIGHTS.ts +++ b/backend/src/auth/INALIENABLE_RIGHTS.ts @@ -7,4 +7,6 @@ export const INALIENABLE_RIGHTS = [ RIGHTS.CREATE_USER, RIGHTS.SEND_RESET_PASSWORD_EMAIL, RIGHTS.SET_PASSWORD, + RIGHTS.QUERY_TRANSACTION_LINK, + RIGHTS.QUERY_OPT_IN, ] diff --git a/backend/src/auth/JWT.ts b/backend/src/auth/JWT.ts index 06c6507b8..e32e68223 100644 --- a/backend/src/auth/JWT.ts +++ b/backend/src/auth/JWT.ts @@ -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 => { diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 3b3f7580c..f40088779 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -16,8 +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', 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', @@ -28,4 +34,5 @@ export enum RIGHTS { SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL', DELETE_USER = 'DELETE_USER', UNDELETE_USER = 'UNDELETE_USER', + CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST', } diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index ada6a2cef..82c689848 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -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 diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 448eb57ab..dcfea2bdb 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,8 +10,13 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0029-clean_transaction_table', + 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 = { @@ -54,8 +59,6 @@ const loginServer = { LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a', } -// TODO: Hannes if I find you... this looks like blasphemy -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', @@ -64,9 +67,12 @@ 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/checkEmail/{code}', - EMAIL_LINK_SETPASSWORD: process.env.EMAIL_LINK_SETPASSWORD || 'http://localhost/reset/{code}', - 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 = { @@ -77,6 +83,18 @@ const webhook = { // 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, diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 0d63e76bb..af915b91a 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -16,4 +16,7 @@ export default class CreateUserArgs { @Field(() => Int, { nullable: true }) publisherId: number + + @Field(() => String, { nullable: true }) + redeemCode?: string | null } diff --git a/backend/src/graphql/arg/Paginated.ts b/backend/src/graphql/arg/Paginated.ts index 85853a1c7..97326caf2 100644 --- a/backend/src/graphql/arg/Paginated.ts +++ b/backend/src/graphql/arg/Paginated.ts @@ -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 } diff --git a/backend/src/graphql/arg/TransactionLinkArgs.ts b/backend/src/graphql/arg/TransactionLinkArgs.ts new file mode 100644 index 000000000..553efcfbe --- /dev/null +++ b/backend/src/graphql/arg/TransactionLinkArgs.ts @@ -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 +} diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index 89da13ff7..84756c45a 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -2,46 +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 { 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 '../../typeorm/repository/User' -import { INALIENABLE_RIGHTS } from '../../auth/INALIENABLE_RIGHTS' +import { UserRepository } from '@repository/User' +import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS' import { ServerUser } from '@entity/ServerUser' const isAuthorized: AuthChecker = async ({ context }, rights) => { context.role = ROLE_UNAUTHORIZED // unauthorized user + // is rights an inalienable right? + if ((rights).reduce((acc, right) => acc && INALIENABLE_RIGHTS.includes(right), true)) + return true + // Do we have a token? - if (context.token) { - // Decode the token - const decoded = decode(context.token) - if (!decoded) { - // Are all rights requested public? - const isInalienable = (rights).reduce( - (acc, right) => acc && INALIENABLE_RIGHTS.includes(right), - true, - ) - if (isInalienable) { - // If public dont throw and permit access - return true - } else { - // Throw on a protected route - 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) + 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 - - context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) + } catch { + // in case the database query fails (user deleted) + throw new Error('401 Unauthorized') } // check for correct rights @@ -50,6 +49,8 @@ const isAuthorized: AuthChecker = async ({ context }, rights) => { throw new Error('401 Unauthorized') } + // set new header token + context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) return true } diff --git a/backend/src/graphql/enum/TransactionTypeId.ts b/backend/src/graphql/enum/TransactionTypeId.ts index 497ad5055..a7e39eebc 100644 --- a/backend/src/graphql/enum/TransactionTypeId.ts +++ b/backend/src/graphql/enum/TransactionTypeId.ts @@ -6,6 +6,7 @@ export enum TransactionTypeId { RECEIVE = 3, // This is a virtual property, never occurring on the database DECAY = 4, + LINK_SUMMARY = 5, } registerEnumType(TransactionTypeId, { diff --git a/backend/src/graphql/model/CreatePendingCreation.ts b/backend/src/graphql/model/CreatePendingCreation.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/src/graphql/model/GdtEntry.ts b/backend/src/graphql/model/GdtEntry.ts index 2f4b31b00..43529fb06 100644 --- a/backend/src/graphql/model/GdtEntry.ts +++ b/backend/src/graphql/model/GdtEntry.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { ObjectType, Field } from 'type-graphql' -import { GdtEntryType } from '../enum/GdtEntryType' +import { GdtEntryType } from '@enum/GdtEntryType' @ObjectType() export class GdtEntry { diff --git a/backend/src/graphql/model/GdtSumPerEmail.ts b/backend/src/graphql/model/GdtSumPerEmail.ts deleted file mode 100644 index c90c7293b..000000000 --- a/backend/src/graphql/model/GdtSumPerEmail.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* -import { ObjectType, Field } from 'type-graphql' - -@ObjectType() -export class GdtSumPerEmail { - constructor(email: string, summe: number) { - this.email = email - this.summe = summe - } - - @Field(() => String) - email: string - - @Field(() => Number) - summe: number -} -*/ diff --git a/backend/src/graphql/model/Transaction.ts b/backend/src/graphql/model/Transaction.ts index cf2e899b2..684224175 100644 --- a/backend/src/graphql/model/Transaction.ts +++ b/backend/src/graphql/model/Transaction.ts @@ -2,7 +2,7 @@ import { ObjectType, Field } from 'type-graphql' import { Decay } from './Decay' import { Transaction as dbTransaction } from '@entity/Transaction' import Decimal from 'decimal.js-light' -import { TransactionTypeId } from '../enum/TransactionTypeId' +import { TransactionTypeId } from '@enum/TransactionTypeId' import { User } from './User' @ObjectType() @@ -30,6 +30,7 @@ export class Transaction { this.creationDate = transaction.creationDate this.linkedUser = linkedUser this.linkedTransactionId = transaction.linkedTransactionId + this.transactionLinkId = transaction.transactionLinkId } @Field(() => Number) @@ -67,4 +68,8 @@ export class Transaction { @Field(() => Number, { nullable: true }) linkedTransactionId?: number | null + + // Links to the TransactionLink when transaction was created by a link + @Field(() => Number, { nullable: true }) + transactionLinkId?: number | null } diff --git a/backend/src/graphql/model/TransactionLink.ts b/backend/src/graphql/model/TransactionLink.ts new file mode 100644 index 000000000..414bba73f --- /dev/null +++ b/backend/src/graphql/model/TransactionLink.ts @@ -0,0 +1,54 @@ +import { ObjectType, Field } from 'type-graphql' +import Decimal from 'decimal.js-light' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' +import { User } from './User' + +@ObjectType() +export class TransactionLink { + constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) { + this.id = transactionLink.id + this.user = user + this.amount = transactionLink.amount + this.holdAvailableAmount = transactionLink.holdAvailableAmount + this.memo = transactionLink.memo + this.code = transactionLink.code + this.createdAt = transactionLink.createdAt + this.validUntil = transactionLink.validUntil + this.deletedAt = transactionLink.deletedAt + this.redeemedAt = transactionLink.redeemedAt + this.redeemedBy = redeemedBy + } + + @Field(() => Number) + id: number + + @Field(() => User) + user: User + + @Field(() => Decimal) + amount: Decimal + + @Field(() => Decimal) + holdAvailableAmount: Decimal + + @Field(() => String) + memo: string + + @Field(() => String) + code: string + + @Field(() => Date) + createdAt: Date + + @Field(() => Date, { nullable: true }) + deletedAt: Date | null + + @Field(() => Date) + validUntil: Date + + @Field(() => Date, { nullable: true }) + redeemedAt: Date | null + + @Field(() => User, { nullable: true }) + redeemedBy: User | null +} diff --git a/backend/src/graphql/model/TransactionList.ts b/backend/src/graphql/model/TransactionList.ts index 364e98f09..9e8356747 100644 --- a/backend/src/graphql/model/TransactionList.ts +++ b/backend/src/graphql/model/TransactionList.ts @@ -1,5 +1,5 @@ import { ObjectType, Field } from 'type-graphql' -import CONFIG from '../../config' +import CONFIG from '@/config' import Decimal from 'decimal.js-light' import { Transaction } from './Transaction' @@ -9,12 +9,14 @@ export class TransactionList { balance: Decimal, transactions: Transaction[], count: number, + linkCount: number, balanceGDT?: number | null, decayStartBlock: Date = CONFIG.DECAY_START_TIME, ) { this.balance = balance this.transactions = transactions this.count = count + this.linkCount = linkCount this.balanceGDT = balanceGDT || null this.decayStartBlock = decayStartBlock } @@ -25,6 +27,9 @@ export class TransactionList { @Field(() => Number) count: number + @Field(() => Number) + linkCount: number + @Field(() => Decimal) balance: Decimal diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 4ea1f2fd1..d98b38b7f 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql' +import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx, Int } from 'type-graphql' import { getCustomRepository, IsNull, @@ -10,25 +10,30 @@ import { getConnection, In, } from '@dbTools/typeorm' -import { UserAdmin, SearchUsersResult } from '../model/UserAdmin' -import { PendingCreation } from '../model/PendingCreation' -import { CreatePendingCreations } from '../model/CreatePendingCreations' -import { UpdatePendingCreation } from '../model/UpdatePendingCreation' -import { RIGHTS } from '../../auth/RIGHTS' -import { UserRepository } from '../../typeorm/repository/User' -import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs' -import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs' -import SearchUsersArgs from '../arg/SearchUsersArgs' -import { Transaction } from '@entity/Transaction' -import { TransactionRepository } from '../../typeorm/repository/Transaction' -import { calculateDecay } from '../../util/decay' +import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' +import { PendingCreation } from '@model/PendingCreation' +import { CreatePendingCreations } from '@model/CreatePendingCreations' +import { UpdatePendingCreation } from '@model/UpdatePendingCreation' +import { RIGHTS } from '@/auth/RIGHTS' +import { UserRepository } from '@repository/User' +import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs' +import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs' +import SearchUsersArgs from '@arg/SearchUsersArgs' +import { Transaction as DbTransaction } from '@entity/Transaction' +import { Transaction } from '@model/Transaction' +import { TransactionRepository } from '@repository/Transaction' +import { calculateDecay } from '@/util/decay' import { AdminPendingCreation } from '@entity/AdminPendingCreation' -import { hasElopageBuys } from '../../util/hasElopageBuys' +import { hasElopageBuys } from '@/util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' -import { User } from '@entity/User' -import { TransactionTypeId } from '../enum/TransactionTypeId' +import { User as dbUser } from '@entity/User' +import { User } from '@model/User' +import { TransactionTypeId } from '@enum/TransactionTypeId' import Decimal from 'decimal.js-light' -import { Decay } from '../model/Decay' +import { Decay } from '@model/Decay' +import Paginated from '@arg/Paginated' +import { Order } from '@enum/Order' +import { communityUser } from '@/util/communityUser' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -122,28 +127,30 @@ export class AdminResolver { @Authorized([RIGHTS.DELETE_USER]) @Mutation(() => Date, { nullable: true }) - async deleteUser(@Arg('userId') userId: number, @Ctx() context: any): Promise { - const user = await User.findOne({ id: userId }) + async deleteUser( + @Arg('userId', () => Int) userId: number, + @Ctx() context: any, + ): Promise { + const user = await dbUser.findOne({ id: userId }) // user exists ? if (!user) { throw new Error(`Could not find user with userId: ${userId}`) } // moderator user disabled own account? - const userRepository = getCustomRepository(UserRepository) - const moderatorUser = await userRepository.findByPubkeyHex(context.pubKey) + const moderatorUser = context.user if (moderatorUser.id === userId) { throw new Error('Moderator can not delete his own account!') } // soft-delete user await user.softRemove() - const newUser = await User.findOne({ id: userId }, { withDeleted: true }) + const newUser = await dbUser.findOne({ id: userId }, { withDeleted: true }) return newUser ? newUser.deletedAt : null } @Authorized([RIGHTS.UNDELETE_USER]) @Mutation(() => Date, { nullable: true }) - async unDeleteUser(@Arg('userId') userId: number): Promise { - const user = await User.findOne({ id: userId }, { withDeleted: true }) + async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise { + const user = await dbUser.findOne({ id: userId }, { withDeleted: true }) // user exists ? if (!user) { throw new Error(`Could not find user with userId: ${userId}`) @@ -158,7 +165,7 @@ export class AdminResolver { async createPendingCreation( @Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs, ): Promise { - const user = await User.findOne({ email }, { withDeleted: true }) + const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { throw new Error(`Could not find user with email: ${email}`) } @@ -215,7 +222,7 @@ export class AdminResolver { async updatePendingCreation( @Args() { id, email, amount, memo, creationDate, moderator }: UpdatePendingCreationArgs, ): Promise { - const user = await User.findOne({ email }, { withDeleted: true }) + const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { throw new Error(`Could not find user with email: ${email}`) } @@ -265,7 +272,7 @@ export class AdminResolver { const userIds = pendingCreations.map((p) => p.userId) const userCreations = await getUserCreations(userIds) - const users = await User.find({ where: { id: In(userIds) }, withDeleted: true }) + const users = await dbUser.find({ where: { id: In(userIds) }, withDeleted: true }) return pendingCreations.map((pendingCreation) => { const user = users.find((u) => u.id === pendingCreation.userId) @@ -284,7 +291,7 @@ export class AdminResolver { @Authorized([RIGHTS.DELETE_PENDING_CREATION]) @Mutation(() => Boolean) - async deletePendingCreation(@Arg('id') id: number): Promise { + async deletePendingCreation(@Arg('id', () => Int) id: number): Promise { const entity = await AdminPendingCreation.findOneOrFail(id) const res = await AdminPendingCreation.delete(entity) return !!res @@ -292,14 +299,16 @@ export class AdminResolver { @Authorized([RIGHTS.CONFIRM_PENDING_CREATION]) @Mutation(() => Boolean) - async confirmPendingCreation(@Arg('id') id: number, @Ctx() context: any): Promise { + async confirmPendingCreation( + @Arg('id', () => Int) id: number, + @Ctx() context: any, + ): Promise { const pendingCreation = await AdminPendingCreation.findOneOrFail(id) - const userRepository = getCustomRepository(UserRepository) - const moderatorUser = await userRepository.findByPubkeyHex(context.pubKey) + const moderatorUser = context.user if (moderatorUser.id === pendingCreation.userId) throw new Error('Moderator can not confirm own pending creation') - const user = await User.findOneOrFail({ id: pendingCreation.userId }, { withDeleted: true }) + const user = await dbUser.findOneOrFail({ id: pendingCreation.userId }, { withDeleted: true }) if (user.deletedAt) throw new Error('This user was deleted. Cannot confirm a creation.') const creations = await getUserCreation(pendingCreation.userId, false) @@ -321,7 +330,7 @@ export class AdminResolver { // TODO pending creations decimal newBalance = newBalance.add(new Decimal(Number(pendingCreation.amount)).toString()) - const transaction = new Transaction() + const transaction = new DbTransaction() transaction.typeId = TransactionTypeId.CREATION transaction.memo = pendingCreation.memo transaction.userId = pendingCreation.userId @@ -339,6 +348,27 @@ export class AdminResolver { return true } + + @Authorized([RIGHTS.CREATION_TRANSACTION_LIST]) + @Query(() => [Transaction]) + async creationTransactionList( + @Args() + { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated, + @Arg('userId', () => Int) userId: number, + ): Promise { + const offset = (currentPage - 1) * pageSize + const transactionRepository = getCustomRepository(TransactionRepository) + const [userTransactions] = await transactionRepository.findByUserPaged( + userId, + pageSize, + offset, + order, + true, + ) + + const user = await dbUser.findOneOrFail({ id: userId }) + return userTransactions.map((t) => new Transaction(t, new User(user), communityUser)) + } } interface CreationMap { diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index e536093bb..09d2fdc92 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -2,11 +2,9 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Ctx, Authorized } from 'type-graphql' -import { getCustomRepository } from '@dbTools/typeorm' -import { Balance } from '../model/Balance' -import { UserRepository } from '../../typeorm/repository/User' -import { calculateDecay } from '../../util/decay' -import { RIGHTS } from '../../auth/RIGHTS' +import { Balance } from '@model/Balance' +import { calculateDecay } from '@/util/decay' +import { RIGHTS } from '@/auth/RIGHTS' import { Transaction } from '@entity/Transaction' import Decimal from 'decimal.js-light' @@ -16,9 +14,7 @@ export class BalanceResolver { @Query(() => Balance) async balance(@Ctx() context: any): Promise { // load user and balance - const userRepository = getCustomRepository(UserRepository) - - const user = await userRepository.findByPubkeyHex(context.pubKey) + const { user } = context const now = new Date() const lastTransaction = await Transaction.findOne( diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index cc481c101..cb916e47c 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -2,10 +2,10 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server/createServer' -import CONFIG from '../../config' +import createServer from '@/server/createServer' +import CONFIG from '@/config' -jest.mock('../../config') +jest.mock('@/config') let query: any diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 1fa3887dc..1693574cb 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -2,9 +2,9 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Authorized } from 'type-graphql' -import { RIGHTS } from '../../auth/RIGHTS' -import CONFIG from '../../config' -import { Community } from '../model/Community' +import { RIGHTS } from '@/auth/RIGHTS' +import CONFIG from '@/config' +import { Community } from '@model/Community' @Resolver() export class CommunityResolver { diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index c515b4e26..26ae9b210 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -2,14 +2,12 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Args, Ctx, Authorized, Arg } from 'type-graphql' -import { getCustomRepository } from '@dbTools/typeorm' -import CONFIG from '../../config' -import { GdtEntryList } from '../model/GdtEntryList' -import Paginated from '../arg/Paginated' -import { apiGet } from '../../apis/HttpRequest' -import { UserRepository } from '../../typeorm/repository/User' -import { Order } from '../enum/Order' -import { RIGHTS } from '../../auth/RIGHTS' +import CONFIG from '@/config' +import { GdtEntryList } from '@model/GdtEntryList' +import Paginated from '@arg/Paginated' +import { apiGet } from '@/apis/HttpRequest' +import { Order } from '@enum/Order' +import { RIGHTS } from '@/auth/RIGHTS' @Resolver() export class GdtResolver { @@ -22,8 +20,7 @@ export class GdtResolver { @Ctx() context: any, ): Promise { // load user - const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const userEntity = context.user try { const resultGDT = await apiGet( diff --git a/backend/src/graphql/resolver/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index 0ba2387e3..d13f1dd8e 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -7,9 +7,9 @@ import { getKlicktippTagMap, unsubscribe, klicktippSignIn, -} from '../../apis/KlicktippController' -import { RIGHTS } from '../../auth/RIGHTS' -import SubscribeNewsletterArgs from '../arg/SubscribeNewsletterArgs' +} from '@/apis/KlicktippController' +import { RIGHTS } from '@/auth/RIGHTS' +import SubscribeNewsletterArgs from '@arg/SubscribeNewsletterArgs' @Resolver() export class KlicktippResolver { diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts new file mode 100644 index 000000000..5a1a39dca --- /dev/null +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -0,0 +1,14 @@ +import { transactionLinkCode } from './TransactionLinkResolver' + +describe('transactionLinkCode', () => { + const date = new Date() + + it('returns a string of length 24', () => { + expect(transactionLinkCode(date)).toHaveLength(24) + }) + + it('returns a string that ends with the hex value of date', () => { + const regexp = new RegExp(date.getTime().toString(16) + '$') + expect(transactionLinkCode(date)).toEqual(expect.stringMatching(regexp)) + }) +}) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts new file mode 100644 index 000000000..646a7c296 --- /dev/null +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -0,0 +1,169 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { Resolver, Args, Arg, Authorized, Ctx, Mutation, Query, Int } from 'type-graphql' +import { TransactionLink } from '@model/TransactionLink' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' +import { User as dbUser } from '@entity/User' +import TransactionLinkArgs from '@arg/TransactionLinkArgs' +import Paginated from '@arg/Paginated' +import { calculateBalance } from '@/util/validate' +import { RIGHTS } from '@/auth/RIGHTS' +import { randomBytes } from 'crypto' +import { User } from '@model/User' +import { calculateDecay } from '@/util/decay' +import { executeTransaction } from './TransactionResolver' +import { Order } from '@enum/Order' + +// TODO: do not export, test it inside the resolver +export const transactionLinkCode = (date: Date): string => { + const time = date.getTime().toString(16) + return ( + randomBytes(12) + .toString('hex') + .substring(0, 24 - time.length) + time + ) +} + +const CODE_VALID_DAYS_DURATION = 14 + +export const transactionLinkExpireDate = (date: Date): Date => { + const validUntil = new Date(date) + return new Date(validUntil.setDate(date.getDate() + CODE_VALID_DAYS_DURATION)) +} + +@Resolver() +export class TransactionLinkResolver { + @Authorized([RIGHTS.CREATE_TRANSACTION_LINK]) + @Mutation(() => TransactionLink) + async createTransactionLink( + @Args() { amount, memo }: TransactionLinkArgs, + @Ctx() context: any, + ): Promise { + const { user } = context + + const createdDate = new Date() + const validUntil = transactionLinkExpireDate(createdDate) + + const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay) + + // validate amount + const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate) + if (!sendBalance) { + throw new Error("user hasn't enough GDD or amount is < 0") + } + + const transactionLink = dbTransactionLink.create() + transactionLink.userId = user.id + transactionLink.amount = amount + transactionLink.memo = memo + transactionLink.holdAvailableAmount = holdAvailableAmount + transactionLink.code = transactionLinkCode(createdDate) + transactionLink.createdAt = createdDate + transactionLink.validUntil = validUntil + await dbTransactionLink.save(transactionLink).catch(() => { + throw new Error('Unable to save transaction link') + }) + + return new TransactionLink(transactionLink, new User(user)) + } + + @Authorized([RIGHTS.DELETE_TRANSACTION_LINK]) + @Mutation(() => Boolean) + async deleteTransactionLink( + @Arg('id', () => Int) id: number, + @Ctx() context: any, + ): Promise { + const { user } = context + + const transactionLink = await dbTransactionLink.findOne({ id }) + if (!transactionLink) { + throw new Error('Transaction Link not found!') + } + + if (transactionLink.userId !== user.id) { + throw new Error('Transaction Link cannot be deleted!') + } + + if (transactionLink.redeemedBy) { + throw new Error('Transaction Link already redeemed!') + } + + await transactionLink.softRemove().catch(() => { + throw new Error('Transaction Link could not be deleted!') + }) + + return true + } + + @Authorized([RIGHTS.QUERY_TRANSACTION_LINK]) + @Query(() => TransactionLink) + async queryTransactionLink(@Arg('code') code: string): Promise { + const transactionLink = await dbTransactionLink.findOneOrFail({ code }, { withDeleted: true }) + const user = await dbUser.findOneOrFail({ id: transactionLink.userId }) + let redeemedBy: User | null = null + if (transactionLink && transactionLink.redeemedBy) { + redeemedBy = new User(await dbUser.findOneOrFail({ id: transactionLink.redeemedBy })) + } + return new TransactionLink(transactionLink, new User(user), redeemedBy) + } + + @Authorized([RIGHTS.LIST_TRANSACTION_LINKS]) + @Query(() => [TransactionLink]) + async listTransactionLinks( + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + @Ctx() context: any, + ): Promise { + const { user } = context + // const now = new Date() + const transactionLinks = await dbTransactionLink.find({ + where: { + userId: user.id, + redeemedBy: null, + // validUntil: MoreThan(now), + }, + order: { + createdAt: order, + }, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) + return transactionLinks.map((tl) => new TransactionLink(tl, new User(user))) + } + + @Authorized([RIGHTS.REDEEM_TRANSACTION_LINK]) + @Mutation(() => Boolean) + async redeemTransactionLink( + @Arg('code', () => String) code: string, + @Ctx() context: any, + ): Promise { + const { user } = context + const transactionLink = await dbTransactionLink.findOneOrFail({ code }) + const linkedUser = await dbUser.findOneOrFail({ id: transactionLink.userId }) + + const now = new Date() + + if (user.id === linkedUser.id) { + throw new Error('Cannot redeem own transaction link.') + } + + if (transactionLink.validUntil.getTime() < now.getTime()) { + throw new Error('Transaction Link is not valid anymore.') + } + + if (transactionLink.redeemedBy) { + throw new Error('Transaction Link already redeemed.') + } + + await executeTransaction( + transactionLink.amount, + transactionLink.memo, + linkedUser, + user, + transactionLink, + ) + + return true + } +} diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 9e70155ac..ed9528b48 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -6,32 +6,124 @@ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection } from '@dbTools/typeorm' -import CONFIG from '../../config' -import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail' +import CONFIG from '@/config' +import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' -import { Transaction } from '../model/Transaction' -import { TransactionList } from '../model/TransactionList' +import { Transaction } from '@model/Transaction' +import { TransactionList } from '@model/TransactionList' -import TransactionSendArgs from '../arg/TransactionSendArgs' -import Paginated from '../arg/Paginated' +import TransactionSendArgs from '@arg/TransactionSendArgs' +import Paginated from '@arg/Paginated' -import { Order } from '../enum/Order' +import { Order } from '@enum/Order' -import { UserRepository } from '../../typeorm/repository/User' -import { TransactionRepository } from '../../typeorm/repository/Transaction' +import { TransactionRepository } from '@repository/Transaction' +import { TransactionLinkRepository } from '@repository/TransactionLink' import { User as dbUser } from '@entity/User' import { Transaction as dbTransaction } from '@entity/Transaction' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' -import { apiPost } from '../../apis/HttpRequest' -import { TransactionTypeId } from '../enum/TransactionTypeId' -import { calculateBalance, isHexPublicKey } from '../../util/validate' -import { RIGHTS } from '../../auth/RIGHTS' -import { User } from '../model/User' -import { communityUser } from '../../util/communityUser' -import { virtualDecayTransaction } from '../../util/virtualDecayTransaction' +import { apiPost } from '@/apis/HttpRequest' +import { TransactionTypeId } from '@enum/TransactionTypeId' +import { calculateBalance, isHexPublicKey } from '@/util/validate' +import { RIGHTS } from '@/auth/RIGHTS' +import { User } from '@model/User' +import { communityUser } from '@/util/communityUser' +import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import Decimal from 'decimal.js-light' -import { calculateDecay } from '../../util/decay' +import { calculateDecay } from '@/util/decay' + +export const executeTransaction = async ( + amount: Decimal, + memo: string, + sender: dbUser, + recipient: dbUser, + transactionLink?: dbTransactionLink | null, +): Promise => { + if (sender.id === recipient.id) { + throw new Error('Sender and Recipient are the same.') + } + + // validate amount + const receivedCallDate = new Date() + const sendBalance = await calculateBalance(sender.id, amount.mul(-1), receivedCallDate) + if (!sendBalance) { + throw new Error("user hasn't enough GDD or amount is < 0") + } + + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('READ UNCOMMITTED') + try { + // transaction + const transactionSend = new dbTransaction() + transactionSend.typeId = TransactionTypeId.SEND + transactionSend.memo = memo + transactionSend.userId = sender.id + transactionSend.linkedUserId = recipient.id + transactionSend.amount = amount.mul(-1) + transactionSend.balance = sendBalance.balance + transactionSend.balanceDate = receivedCallDate + transactionSend.decay = sendBalance.decay.decay + transactionSend.decayStart = sendBalance.decay.start + transactionSend.previous = sendBalance.lastTransactionId + transactionSend.transactionLinkId = transactionLink ? transactionLink.id : null + await queryRunner.manager.insert(dbTransaction, transactionSend) + + const transactionReceive = new dbTransaction() + transactionReceive.typeId = TransactionTypeId.RECEIVE + transactionReceive.memo = memo + transactionReceive.userId = recipient.id + transactionReceive.linkedUserId = sender.id + transactionReceive.amount = amount + const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate) + transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount + transactionReceive.balanceDate = receivedCallDate + transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) + transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null + transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null + transactionReceive.linkedTransactionId = transactionSend.id + transactionReceive.transactionLinkId = transactionLink ? transactionLink.id : null + await queryRunner.manager.insert(dbTransaction, transactionReceive) + + // Save linked transaction id for send + transactionSend.linkedTransactionId = transactionReceive.id + await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) + + if (transactionLink) { + transactionLink.redeemedAt = receivedCallDate + transactionLink.redeemedBy = recipient.id + await queryRunner.manager.update( + dbTransactionLink, + { id: transactionLink.id }, + transactionLink, + ) + } + + await queryRunner.commitTransaction() + } catch (e) { + await queryRunner.rollbackTransaction() + throw new Error(`Transaction was not successful: ${e}`) + } finally { + await queryRunner.release() + } + + // send notification email + // TODO: translate + await sendTransactionReceivedEmail({ + senderFirstName: sender.firstName, + senderLastName: sender.lastName, + recipientFirstName: recipient.firstName, + recipientLastName: recipient.lastName, + email: recipient.email, + senderEmail: sender.email, + amount, + memo, + }) + + return true +} @Resolver() export class TransactionResolver { @@ -39,22 +131,11 @@ export class TransactionResolver { @Query(() => TransactionList) async transactionList( @Args() - { - currentPage = 1, - pageSize = 25, - order = Order.DESC, - onlyCreations = false, - userId, - }: Paginated, + { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated, @Ctx() context: any, ): Promise { const now = new Date() - // find user - const userRepository = getCustomRepository(UserRepository) - // TODO: separate those usecases - this is a security issue - const user = userId - ? await userRepository.findOneOrFail({ id: userId }, { withDeleted: true }) - : await userRepository.findByPubkeyHex(context.pubKey) + const user = context.user // find current balance const lastTransaction = await dbTransaction.findOne( @@ -78,7 +159,7 @@ export class TransactionResolver { } if (!lastTransaction) { - return new TransactionList(new Decimal(0), [], 0, balanceGDT) + return new TransactionList(new Decimal(0), [], 0, 0, balanceGDT) } // find transactions @@ -90,7 +171,6 @@ export class TransactionResolver { pageSize, offset, order, - onlyCreations, ) // find involved users; I am involved @@ -111,11 +191,29 @@ export class TransactionResolver { const self = new User(user) const transactions: Transaction[] = [] - // decay transaction - if (!onlyCreations && currentPage === 1 && order === Order.DESC) { + const transactionLinkRepository = getCustomRepository(TransactionLinkRepository) + const { sumHoldAvailableAmount, sumAmount, lastDate, firstDate, transactionLinkcount } = + await transactionLinkRepository.summary(user.id, now) + + // decay & link transactions + if (currentPage === 1 && order === Order.DESC) { transactions.push( virtualDecayTransaction(lastTransaction.balance, lastTransaction.balanceDate, now, self), ) + // virtual transaction for pending transaction-links sum + if (sumHoldAvailableAmount.greaterThan(0)) { + transactions.push( + virtualLinkTransaction( + lastTransaction.balance.minus(sumHoldAvailableAmount.toString()), + sumAmount.mul(-1), + sumHoldAvailableAmount.mul(-1), + sumHoldAvailableAmount.minus(sumAmount.toString()).mul(-1), + firstDate || now, + lastDate || now, + self, + ), + ) + } } // transactions @@ -129,9 +227,12 @@ export class TransactionResolver { // Construct Result return new TransactionList( - calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance, + calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, now).balance.minus( + sumHoldAvailableAmount.toString(), + ), transactions, userTransactionsCount, + transactionLinkcount, balanceGDT, ) } @@ -143,17 +244,10 @@ export class TransactionResolver { @Ctx() context: any, ): Promise { // TODO this is subject to replay attacks - const userRepository = getCustomRepository(UserRepository) - const senderUser = await userRepository.findByPubkeyHex(context.pubKey) + const senderUser = context.user if (senderUser.pubKey.length !== 32) { throw new Error('invalid sender public key') } - // validate amount - const receivedCallDate = new Date() - const sendBalance = await calculateBalance(senderUser.id, amount.mul(-1), receivedCallDate) - if (!sendBalance) { - throw new Error("user hasn't enough GDD or amount is < 0") - } // validate recipient user const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true }) @@ -167,62 +261,7 @@ export class TransactionResolver { throw new Error('invalid recipient public key') } - const queryRunner = getConnection().createQueryRunner() - await queryRunner.connect() - await queryRunner.startTransaction('READ UNCOMMITTED') - try { - // transaction - const transactionSend = new dbTransaction() - transactionSend.typeId = TransactionTypeId.SEND - transactionSend.memo = memo - transactionSend.userId = senderUser.id - transactionSend.linkedUserId = recipientUser.id - transactionSend.amount = amount.mul(-1) - transactionSend.balance = sendBalance.balance - transactionSend.balanceDate = receivedCallDate - transactionSend.decay = sendBalance.decay.decay - transactionSend.decayStart = sendBalance.decay.start - transactionSend.previous = sendBalance.lastTransactionId - await queryRunner.manager.insert(dbTransaction, transactionSend) - - const transactionReceive = new dbTransaction() - transactionReceive.typeId = TransactionTypeId.RECEIVE - transactionReceive.memo = memo - transactionReceive.userId = recipientUser.id - transactionReceive.linkedUserId = senderUser.id - transactionReceive.amount = amount - const receiveBalance = await calculateBalance(recipientUser.id, amount, receivedCallDate) - transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount - transactionReceive.balanceDate = receivedCallDate - transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) - transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null - transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null - transactionReceive.linkedTransactionId = transactionSend.id - await queryRunner.manager.insert(dbTransaction, transactionReceive) - - // Save linked transaction id for send - transactionSend.linkedTransactionId = transactionReceive.id - await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) - - await queryRunner.commitTransaction() - } catch (e) { - await queryRunner.rollbackTransaction() - throw new Error(`Transaction was not successful: ${e}`) - } finally { - await queryRunner.release() - } - - // send notification email - // TODO: translate - await sendTransactionReceivedEmail({ - senderFirstName: senderUser.firstName, - senderLastName: senderUser.lastName, - recipientFirstName: recipientUser.firstName, - recipientLastName: recipientUser.lastName, - email: recipientUser.email, - amount, - memo, - }) + await executeTransaction(amount, memo, senderUser, recipientUser) return true } diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index b01c99552..53f39668e 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1,20 +1,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' +import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers' +import { userFactory } from '@/seeds/factory/user' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { createUser, setPassword } from '@/seeds/graphql/mutations' +import { login, logout } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' -import createServer from '../../server/createServer' -import { resetDB, initialize } from '@dbTools/helpers' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User } from '@entity/User' -import CONFIG from '../../config' -import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail' -// import { klicktippSignIn } from '../../apis/KlicktippController' +import CONFIG from '@/config' +import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' -jest.setTimeout(10000) +// import { klicktippSignIn } from '@/apis/KlicktippController' -jest.mock('../../mailer/sendAccountActivationEmail', () => { +jest.mock('@/mailer/sendAccountActivationEmail', () => { return { __esModule: true, sendAccountActivationEmail: jest.fn(), @@ -22,7 +22,7 @@ jest.mock('../../mailer/sendAccountActivationEmail', () => { }) /* -jest.mock('../../apis/KlicktippController', () => { +jest.mock('@/apis/KlicktippController', () => { return { __esModule: true, klicktippSignIn: jest.fn(), @@ -30,19 +30,19 @@ jest.mock('../../apis/KlicktippController', () => { }) */ -let mutate: any -let con: any +let mutate: any, query: any, con: any +let testEnv: any beforeAll(async () => { - const server = await createServer({}) - con = server.con - mutate = createTestClient(server.apollo).mutate - await initialize() - await resetDB() + testEnv = await testEnvironment() + mutate = testEnv.mutate + query = testEnv.query + con = testEnv.con + await cleanDB() }) afterAll(async () => { - await resetDB(true) + await cleanDB() await con.close() }) @@ -56,37 +56,22 @@ describe('UserResolver', () => { publisherId: 1234, } - const mutation = gql` - mutation ( - $email: String! - $firstName: String! - $lastName: String! - $language: String! - $publisherId: Int - ) { - createUser( - email: $email - firstName: $firstName - lastName: $lastName - language: $language - publisherId: $publisherId - ) - } - ` - let result: any let emailOptIn: string beforeAll(async () => { - result = await mutate({ mutation, variables }) + jest.clearAllMocks() + result = await mutate({ mutation: createUser, variables }) }) afterAll(async () => { - await resetDB() + await cleanDB() }) it('returns success', () => { - expect(result).toEqual(expect.objectContaining({ data: { createUser: 'success' } })) + expect(result).toEqual( + expect.objectContaining({ data: { createUser: { id: expect.any(Number) } } }), + ) }) describe('valid input data', () => { @@ -116,6 +101,7 @@ describe('UserResolver', () => { language: 'de', deletedAt: null, publisherId: 1234, + referrerId: null, }, ]) }) @@ -138,7 +124,10 @@ describe('UserResolver', () => { describe('account activation email', () => { it('sends an account activation email', () => { - const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(/{code}/g, emailOptIn) + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + /{optin}/g, + emailOptIn, + ).replace(/{code}/g, '') expect(sendAccountActivationEmail).toBeCalledWith({ link: activationLink, firstName: 'Peter', @@ -150,7 +139,7 @@ describe('UserResolver', () => { describe('email already exists', () => { it('throws an error', async () => { - await expect(mutate({ mutation, variables })).resolves.toEqual( + await expect(mutate({ mutation: createUser, variables })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('User already exists.')], }), @@ -161,7 +150,7 @@ describe('UserResolver', () => { describe('unknown language', () => { it('sets "de" as default language', async () => { await mutate({ - mutation, + mutation: createUser, variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' }, }) await expect(User.find()).resolves.toEqual( @@ -178,7 +167,7 @@ describe('UserResolver', () => { describe('no publisher id', () => { it('sets publisher id to null', async () => { await mutate({ - mutation, + mutation: createUser, variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined }, }) await expect(User.find()).resolves.toEqual( @@ -194,24 +183,6 @@ describe('UserResolver', () => { }) describe('setPassword', () => { - const createUserMutation = gql` - mutation ( - $email: String! - $firstName: String! - $lastName: String! - $language: String! - $publisherId: Int - ) { - createUser( - email: $email - firstName: $firstName - lastName: $lastName - language: $language - publisherId: $publisherId - ) - } - ` - const createUserVariables = { email: 'peter@lustig.de', firstName: 'Peter', @@ -220,11 +191,6 @@ describe('UserResolver', () => { publisherId: 1234, } - const setPasswordMutation = gql` - mutation ($code: String!, $password: String!) { - setPassword(code: $code, password: $password) - } - ` let result: any let emailOptIn: string @@ -232,18 +198,18 @@ describe('UserResolver', () => { let newUser: any beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) const loginEmailOptIn = await LoginEmailOptIn.find() emailOptIn = loginEmailOptIn[0].verificationCode.toString() result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: emailOptIn, password: 'Aa12345_' }, }) newUser = await User.find() }) afterAll(async () => { - await resetDB() + await cleanDB() }) it('sets email checked to true', () => { @@ -276,17 +242,17 @@ describe('UserResolver', () => { describe('no valid password', () => { beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) const loginEmailOptIn = await LoginEmailOptIn.find() emailOptIn = loginEmailOptIn[0].verificationCode.toString() result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: emailOptIn, password: 'not-valid' }, }) }) afterAll(async () => { - await resetDB() + await cleanDB() }) it('throws an error', () => { @@ -304,15 +270,15 @@ describe('UserResolver', () => { describe('no valid optin code', () => { beforeAll(async () => { - await mutate({ mutation: createUserMutation, variables: createUserVariables }) + await mutate({ mutation: createUser, variables: createUserVariables }) result = await mutate({ - mutation: setPasswordMutation, + mutation: setPassword, variables: { code: 'not valid', password: 'Aa12345_' }, }) }) afterAll(async () => { - await resetDB() + await cleanDB() }) it('throws an error', () => { @@ -324,4 +290,128 @@ describe('UserResolver', () => { }) }) }) + + describe('login', () => { + const variables = { + email: 'bibi@bloxberg.de', + password: 'Aa12345_', + publisherId: 1234, + } + + let result: User + + afterAll(async () => { + await cleanDB() + }) + + describe('no users in database', () => { + beforeAll(async () => { + result = await query({ query: login, variables }) + }) + + it('throws an error', () => { + expect(result).toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No user with this credentials')], + }), + ) + }) + }) + + describe('user is in database and correct login data', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + result = await query({ query: login, variables }) + }) + + afterAll(async () => { + await cleanDB() + }) + + it('returns the user object', () => { + expect(result).toEqual( + expect.objectContaining({ + data: { + login: { + coinanimation: true, + email: 'bibi@bloxberg.de', + firstName: 'Bibi', + hasElopage: false, + id: expect.any(Number), + isAdmin: false, + klickTipp: { + newsletterState: false, + }, + language: 'de', + lastName: 'Bloxberg', + publisherId: 1234, + }, + }, + }), + ) + }) + + it('sets the token in the header', () => { + expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) }) + }) + }) + + describe('user is in database and wrong password', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + }) + + afterAll(async () => { + await cleanDB() + }) + + it('returns an error', () => { + expect( + query({ query: login, variables: { ...variables, password: 'wrong' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No user with this credentials')], + }), + ) + }) + }) + }) + + describe('logout', () => { + describe('unauthenticated', () => { + it('throws an error', async () => { + resetToken() + await expect(query({ query: logout })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + const variables = { + email: 'bibi@bloxberg.de', + password: 'Aa12345_', + } + + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await query({ query: login, variables }) + }) + + afterAll(async () => { + await cleanDB() + }) + + it('returns true', async () => { + await expect(query({ query: logout })).resolves.toEqual( + expect.objectContaining({ + data: { logout: 'true' }, + errors: undefined, + }), + ) + }) + }) + }) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index dcb3e03b6..b24aa1b58 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -4,24 +4,24 @@ import fs from 'fs' import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql' import { getConnection, getCustomRepository, QueryRunner } from '@dbTools/typeorm' -import CONFIG from '../../config' -import { User } from '../model/User' +import CONFIG from '@/config' +import { User } from '@model/User' import { User as DbUser } from '@entity/User' -import { encode } from '../../auth/JWT' -import CreateUserArgs from '../arg/CreateUserArgs' -import UnsecureLoginArgs from '../arg/UnsecureLoginArgs' -import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs' -import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMiddleware' -import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' -import { Setting } from '../enum/Setting' -import { UserRepository } from '../../typeorm/repository/User' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' +import { encode } from '@/auth/JWT' +import CreateUserArgs from '@arg/CreateUserArgs' +import UnsecureLoginArgs from '@arg/UnsecureLoginArgs' +import UpdateUserInfosArgs from '@arg/UpdateUserInfosArgs' +import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddleware' +import { UserSettingRepository } from '@repository/UserSettingRepository' +import { Setting } from '@enum/Setting' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' -import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail' -import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail' -import { klicktippSignIn } from '../../apis/KlicktippController' -import { RIGHTS } from '../../auth/RIGHTS' -import { ROLE_ADMIN } from '../../auth/ROLES' -import { hasElopageBuys } from '../../util/hasElopageBuys' +import { sendResetPasswordEmail } from '@/mailer/sendResetPasswordEmail' +import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' +import { klicktippSignIn } from '@/apis/KlicktippController' +import { RIGHTS } from '@/auth/RIGHTS' +import { ROLE_ADMIN } from '@/auth/ROLES' +import { hasElopageBuys } from '@/util/hasElopageBuys' import { ServerUser } from '@entity/ServerUser' const EMAIL_OPT_IN_RESET_PASSWORD = 2 @@ -157,15 +157,11 @@ const createEmailOptIn = async ( emailOptInTypeId: EMAIL_OPT_IN_REGISTER, }) if (emailOptIn) { - const timeElapsed = Date.now() - new Date(emailOptIn.updatedAt).getTime() - if (timeElapsed <= parseInt(CONFIG.RESEND_TIME.toString()) * 60 * 1000) { - throw new Error( - 'email already sent less than ' + parseInt(CONFIG.RESEND_TIME.toString()) + ' minutes ago', - ) - } else { - emailOptIn.updatedAt = new Date() - emailOptIn.resendCount++ + if (isOptInCodeValid(emailOptIn)) { + throw new Error(`email already sent less than $(CONFIG.EMAIL_CODE_VALID_TIME} minutes ago`) } + emailOptIn.updatedAt = new Date() + emailOptIn.resendCount++ } else { emailOptIn = new LoginEmailOptIn() emailOptIn.verificationCode = random(64) @@ -186,17 +182,13 @@ const getOptInCode = async (loginUserId: number): Promise => { emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD, }) - // Check for 10 minute delay + // Check for `CONFIG.EMAIL_CODE_VALID_TIME` minute delay if (optInCode) { - const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() - if (timeElapsed <= parseInt(CONFIG.RESEND_TIME.toString()) * 60 * 1000) { - throw new Error( - 'email already sent less than ' + parseInt(CONFIG.RESEND_TIME.toString()) + ' minutes ago', - ) - } else { - optInCode.updatedAt = new Date() - optInCode.resendCount++ + if (isOptInCodeValid(optInCode)) { + throw new Error(`email already sent less than $(CONFIG.EMAIL_CODE_VALID_TIME} minutes ago`) } + optInCode.updatedAt = new Date() + optInCode.resendCount++ } else { optInCode = new LoginEmailOptIn() optInCode.verificationCode = random(64) @@ -214,8 +206,7 @@ export class UserResolver { @UseMiddleware(klicktippNewsletterStateMiddleware) async verifyLogin(@Ctx() context: any): Promise { // TODO refactor and do not have duplicate code with login(see below) - const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const userEntity = context.user const user = new User(userEntity) // user.pubkey = userEntity.pubKey.toString('hex') // Elopage Status & Stored PublisherId @@ -313,10 +304,11 @@ export class UserResolver { } @Authorized([RIGHTS.CREATE_USER]) - @Mutation(() => String) + @Mutation(() => User) async createUser( - @Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs, - ): Promise { + @Args() + { email, firstName, lastName, language, publisherId, redeemCode = null }: CreateUserArgs, + ): Promise { // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; @@ -348,6 +340,12 @@ export class UserResolver { dbUser.language = language dbUser.publisherId = publisherId dbUser.passphrase = passphrase.join(' ') + if (redeemCode) { + const transactionLink = await dbTransactionLink.findOne({ code: redeemCode }) + if (transactionLink) { + dbUser.referrerId = transactionLink.userId + } + } // TODO this field has no null allowed unlike the loginServer table // dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... // dbUser.pubkey = keyPair[0] @@ -370,9 +368,11 @@ export class UserResolver { const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{code}/g, + /{optin}/g, emailOptIn.verificationCode.toString(), - ) + ).replace(/{code}/g, redeemCode ? '/' + redeemCode : '') + + // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ link: activationLink, firstName, @@ -380,11 +380,14 @@ export class UserResolver { email, }) + /* uncomment this, when you need the activation link on the console // In case EMails are disabled log the activation link for the user if (!emailSent) { // eslint-disable-next-line no-console console.log(`Account confirmation link: ${activationLink}`) } + */ + await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() @@ -392,7 +395,7 @@ export class UserResolver { } finally { await queryRunner.release() } - return 'success' + return new User(dbUser) } // THis is used by the admin only - should we move it to the admin resolver? @@ -410,10 +413,11 @@ export class UserResolver { const emailOptIn = await createEmailOptIn(user.id, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( - /{code}/g, + /{optin}/g, emailOptIn.verificationCode.toString(), ) + // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ link: activationLink, firstName: user.firstName, @@ -421,11 +425,13 @@ export class UserResolver { email, }) + /* uncomment this, when you need the activation link on the console // In case EMails are disabled log the activation link for the user if (!emailSent) { // eslint-disable-next-line no-console console.log(`Account confirmation link: ${activationLink}`) } + */ await queryRunner.commitTransaction() } catch (e) { await queryRunner.rollbackTransaction() @@ -446,10 +452,11 @@ export class UserResolver { const optInCode = await getOptInCode(user.id) const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( - /{code}/g, + /{optin}/g, optInCode.verificationCode.toString(), ) + // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendResetPasswordEmail({ link, firstName: user.firstName, @@ -457,11 +464,13 @@ export class UserResolver { email, }) + /* uncomment this, when you need the activation link on the console // In case EMails are disabled log the activation link for the user if (!emailSent) { // eslint-disable-next-line no-console console.log(`Reset password link: ${link}`) } + */ return true } @@ -484,10 +493,9 @@ export class UserResolver { throw new Error('Could not login with emailVerificationCode') }) - // Code is only valid for 10minutes - const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() - if (timeElapsed > 10 * 60 * 1000) { - throw new Error('Code is older than 10 minutes') + // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes + if (!isOptInCodeValid(optInCode)) { + throw new Error(`email already more than $(CONFIG.EMAIL_CODE_VALID_TIME} minutes ago`) } // load user @@ -551,13 +559,26 @@ export class UserResolver { } catch { // TODO is this a problem? // eslint-disable-next-line no-console + /* uncomment this, when you need the activation link on the console console.log('Could not subscribe to klicktipp') + */ } } return true } + @Authorized([RIGHTS.QUERY_OPT_IN]) + @Query(() => Boolean) + async queryOptIn(@Arg('optIn') optIn: string): Promise { + const optInCode = await LoginEmailOptIn.findOneOrFail({ verificationCode: optIn }) + // Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes + if (!isOptInCodeValid(optInCode)) { + throw new Error(`email was sent more than $(CONFIG.EMAIL_CODE_VALID_TIME} minutes ago`) + } + return true + } + @Authorized([RIGHTS.UPDATE_USER_INFOS]) @Mutation(() => Boolean) async updateUserInfos( @@ -573,8 +594,7 @@ export class UserResolver { }: UpdateUserInfosArgs, @Ctx() context: any, ): Promise { - const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey) + const userEntity = context.user if (firstName) { userEntity.firstName = firstName @@ -652,8 +672,7 @@ export class UserResolver { @Authorized([RIGHTS.HAS_ELOPAGE]) @Query(() => Boolean) async hasElopage(@Ctx() context: any): Promise { - const userRepository = getCustomRepository(UserRepository) - const userEntity = await userRepository.findByPubkeyHex(context.pubKey).catch() + const userEntity = context.user if (!userEntity) { return false } @@ -661,3 +680,7 @@ export class UserResolver { return hasElopageBuys(userEntity.email) } } +function isOptInCodeValid(optInCode: LoginEmailOptIn) { + const timeElapsed = Date.now() - new Date(optInCode.updatedAt).getTime() + return timeElapsed <= CONFIG.EMAIL_CODE_VALID_TIME * 60 * 1000 +} diff --git a/backend/src/mailer/sendEMail.test.ts b/backend/src/mailer/sendEMail.test.ts index 5baae00ab..b7cc06a60 100644 --- a/backend/src/mailer/sendEMail.test.ts +++ b/backend/src/mailer/sendEMail.test.ts @@ -1,6 +1,6 @@ import { sendEMail } from './sendEMail' import { createTransport } from 'nodemailer' -import CONFIG from '../config' +import CONFIG from '@/config' CONFIG.EMAIL = false CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL' diff --git a/backend/src/mailer/sendEMail.ts b/backend/src/mailer/sendEMail.ts index e26589347..13c28996b 100644 --- a/backend/src/mailer/sendEMail.ts +++ b/backend/src/mailer/sendEMail.ts @@ -1,6 +1,6 @@ import { createTransport } from 'nodemailer' -import CONFIG from '../config' +import CONFIG from '@/config' export const sendEMail = async (emailDef: { to: string diff --git a/backend/src/mailer/sendTransactionReceivedEmail.test.ts b/backend/src/mailer/sendTransactionReceivedEmail.test.ts index 5fd013650..1ebc9dae3 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.test.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.test.ts @@ -17,6 +17,7 @@ describe('sendTransactionReceivedEmail', () => { recipientFirstName: 'Peter', recipientLastName: 'Lustig', email: 'peter@lustig.de', + senderEmail: 'bibi@bloxberg.de', amount: new Decimal(42.0), memo: 'Vielen herzlichen Dank für den neuen Hexenbesen!', }) @@ -30,6 +31,7 @@ describe('sendTransactionReceivedEmail', () => { expect.stringContaining('Hallo Peter Lustig') && expect.stringContaining('42,00 GDD') && expect.stringContaining('Bibi Bloxberg') && + expect.stringContaining('(bibi@bloxberg.de)') && expect.stringContaining('Vielen herzlichen Dank für den neuen Hexenbesen!'), }) }) diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 3b417b10a..934783449 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -8,6 +8,7 @@ export const sendTransactionReceivedEmail = (data: { recipientFirstName: string recipientLastName: string email: string + senderEmail: string amount: Decimal memo: string }): Promise => { diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index f685c60ae..520ee43bf 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -9,6 +9,7 @@ export const transactionReceived = { recipientFirstName: string recipientLastName: string email: string + senderEmail: string amount: Decimal memo: string }): string => @@ -16,7 +17,7 @@ export const transactionReceived = { Du hast soeben ${data.amount.toFixed(2).replace('.', ',')} GDD von ${data.senderFirstName} ${ data.senderLastName - } erhalten. + } (${data.senderEmail}) erhalten. ${data.senderFirstName} ${data.senderLastName} schreibt: ${data.memo} diff --git a/backend/src/middleware/klicktippMiddleware.ts b/backend/src/middleware/klicktippMiddleware.ts index d0fde8195..b3699f29b 100644 --- a/backend/src/middleware/klicktippMiddleware.ts +++ b/backend/src/middleware/klicktippMiddleware.ts @@ -1,7 +1,7 @@ import { MiddlewareFn } from 'type-graphql' -import { /* klicktippSignIn, */ getKlickTippUser } from '../apis/KlicktippController' -import { KlickTipp } from '../graphql/model/KlickTipp' -import CONFIG from '../config/index' +import { /* klicktippSignIn, */ getKlickTippUser } from '@/apis/KlicktippController' +import { KlickTipp } from '@model/KlickTipp' +import CONFIG from '@/config' // export const klicktippRegistrationMiddleware: MiddlewareFn = async ( // // Only for demo diff --git a/backend/src/seeds/creation/CreationInterface.ts b/backend/src/seeds/creation/CreationInterface.ts new file mode 100644 index 000000000..8723f441d --- /dev/null +++ b/backend/src/seeds/creation/CreationInterface.ts @@ -0,0 +1,7 @@ +export interface CreationInterface { + email: string + amount: number + memo: string + creationDate: string + confirmed?: boolean +} diff --git a/backend/src/seeds/creation/index.ts b/backend/src/seeds/creation/index.ts new file mode 100644 index 000000000..7396a7ec8 --- /dev/null +++ b/backend/src/seeds/creation/index.ts @@ -0,0 +1,29 @@ +import { CreationInterface } from './CreationInterface' + +const lastMonth = (date: Date): string => { + return new Date(date.getFullYear(), date.getMonth() - 1, 1).toISOString() +} + +export const creations: CreationInterface[] = [ + { + email: 'bibi@bloxberg.de', + amount: 1000, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: lastMonth(new Date()), + confirmed: true, + }, + { + email: 'bob@baumeister.de', + amount: 1000, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: lastMonth(new Date()), + confirmed: true, + }, + { + email: 'raeuber@hotzenplotz.de', + amount: 1000, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: lastMonth(new Date()), + confirmed: true, + }, +] diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts new file mode 100644 index 000000000..d4d4c8101 --- /dev/null +++ b/backend/src/seeds/factory/creation.ts @@ -0,0 +1,38 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { createPendingCreation, confirmPendingCreation } from '@/seeds/graphql/mutations' +import { login } from '@/seeds/graphql/queries' +import { CreationInterface } from '@/seeds/creation/CreationInterface' +import { ApolloServerTestClient } from 'apollo-server-testing' +import { User } from '@entity/User' +import { AdminPendingCreation } from '@entity/AdminPendingCreation' +// import CONFIG from '@/config/index' + +export const creationFactory = async ( + client: ApolloServerTestClient, + creation: CreationInterface, +): Promise => { + const { mutate, query } = client + + // login as Peter Lustig (admin) and get his user ID + const { + data: { + login: { id }, + }, + } = await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) + + await mutate({ mutation: createPendingCreation, variables: { ...creation, moderator: id } }) + + // get User + const user = await User.findOneOrFail({ where: { email: creation.email } }) + + if (creation.confirmed) { + const pendingCreation = await AdminPendingCreation.findOneOrFail({ + where: { userId: user.id }, + order: { created: 'DESC' }, + }) + + await mutate({ mutation: confirmPendingCreation, variables: { id: pendingCreation.id } }) + } +} diff --git a/backend/src/seeds/factory/transactionLink.ts b/backend/src/seeds/factory/transactionLink.ts new file mode 100644 index 000000000..2f54dc70c --- /dev/null +++ b/backend/src/seeds/factory/transactionLink.ts @@ -0,0 +1,43 @@ +import { ApolloServerTestClient } from 'apollo-server-testing' +import { createTransactionLink } from '@/seeds/graphql/mutations' +import { login } from '@/seeds/graphql/queries' +import { TransactionLinkInterface } from '@/seeds/transactionLink/TransactionLinkInterface' +import { transactionLinkExpireDate } from '@/graphql/resolver/TransactionLinkResolver' +import { TransactionLink } from '@entity/TransactionLink' + +export const transactionLinkFactory = async ( + client: ApolloServerTestClient, + transactionLink: TransactionLinkInterface, +): Promise => { + const { mutate, query } = client + + // login + await query({ query: login, variables: { email: transactionLink.email, password: 'Aa12345_' } }) + + const variables = { + amount: transactionLink.amount, + memo: transactionLink.memo, + } + + // get the transaction links's id + const { + data: { + createTransactionLink: { id }, + }, + } = await mutate({ mutation: createTransactionLink, variables }) + + if (transactionLink.createdAt || transactionLink.deletedAt) { + const dbTransactionLink = await TransactionLink.findOneOrFail({ id }) + + if (transactionLink.createdAt) { + dbTransactionLink.createdAt = transactionLink.createdAt + dbTransactionLink.validUntil = transactionLinkExpireDate(transactionLink.createdAt) + await dbTransactionLink.save() + } + + if (transactionLink.deletedAt) { + dbTransactionLink.deletedAt = new Date() + await dbTransactionLink.save() + } + } +} diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts new file mode 100644 index 000000000..ff4c1d6c9 --- /dev/null +++ b/backend/src/seeds/factory/user.ts @@ -0,0 +1,51 @@ +import { createUser, setPassword } from '@/seeds/graphql/mutations' +import { User } from '@entity/User' +import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' +import { ServerUser } from '@entity/ServerUser' +import { UserInterface } from '@/seeds/users/UserInterface' +import { ApolloServerTestClient } from 'apollo-server-testing' + +export const userFactory = async ( + client: ApolloServerTestClient, + user: UserInterface, +): Promise => { + const { mutate } = client + + const { + data: { + createUser: { id }, + }, + } = await mutate({ mutation: createUser, variables: user }) + + if (user.emailChecked) { + const optin = await LoginEmailOptIn.findOneOrFail({ userId: id }) + await mutate({ + mutation: setPassword, + variables: { password: 'Aa12345_', code: optin.verificationCode }, + }) + } + + if (user.createdAt || user.deletedAt || user.isAdmin) { + // get user from database + const dbUser = await User.findOneOrFail({ id }) + + if (user.createdAt || user.deletedAt) { + if (user.createdAt) dbUser.createdAt = user.createdAt + if (user.deletedAt) dbUser.deletedAt = user.deletedAt + await dbUser.save() + } + + if (user.isAdmin) { + const admin = new ServerUser() + admin.username = dbUser.firstName + admin.password = 'please_refactor' + admin.email = dbUser.email + admin.role = 'admin' + admin.activated = 1 + admin.lastLogin = new Date() + admin.created = dbUser.createdAt + admin.modified = dbUser.createdAt + await admin.save() + } + } +} diff --git a/backend/src/seeds/graphql/enums.ts b/backend/src/seeds/graphql/enums.ts new file mode 100644 index 000000000..6e40dd19c --- /dev/null +++ b/backend/src/seeds/graphql/enums.ts @@ -0,0 +1,9 @@ +export const GdtEntryType = { + FORM: 'FORM', + CVS: 'CVS', + ELOPAGE: 'ELOPAGE', + ELOPAGE_PUBLISHER: 'ELOPAGE_PUBLISHER', + DIGISTORE: 'DIGISTORE', + CVS2: 'CVS2', + GLOBAL_MODIFICATOR: 'GLOBAL_MODIFICATOR', +} diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts new file mode 100644 index 000000000..298d56bdb --- /dev/null +++ b/backend/src/seeds/graphql/mutations.ts @@ -0,0 +1,102 @@ +import gql from 'graphql-tag' + +export const subscribeNewsletter = gql` + mutation ($email: String!, $language: String!) { + subscribeNewsletter(email: $email, language: $language) + } +` + +export const unsubscribeNewsletter = gql` + mutation ($email: String!) { + unsubscribeNewsletter(email: $email) + } +` + +export const setPassword = gql` + mutation ($code: String!, $password: String!) { + setPassword(code: $code, password: $password) + } +` + +export const updateUserInfos = gql` + mutation ( + $firstName: String + $lastName: String + $password: String + $passwordNew: String + $locale: String + $coinanimation: Boolean + ) { + updateUserInfos( + firstName: $firstName + lastName: $lastName + password: $password + passwordNew: $passwordNew + language: $locale + coinanimation: $coinanimation + ) + } +` + +export const createUser = gql` + mutation ( + $firstName: String! + $lastName: String! + $email: String! + $language: String! + $publisherId: Int + $redeemCode: String + ) { + createUser( + email: $email + firstName: $firstName + lastName: $lastName + language: $language + publisherId: $publisherId + redeemCode: $redeemCode + ) { + id + } + } +` + +export const sendCoins = gql` + mutation ($email: String!, $amount: Decimal!, $memo: String!) { + sendCoins(email: $email, amount: $amount, memo: $memo) + } +` + +export const createTransactionLink = gql` + mutation ($amount: Decimal!, $memo: String!) { + createTransactionLink(amount: $amount, memo: $memo) { + id + code + } + } +` + +// from admin interface + +export const createPendingCreation = gql` + mutation ( + $email: String! + $amount: Float! + $memo: String! + $creationDate: String! + $moderator: Int! + ) { + createPendingCreation( + email: $email + amount: $amount + memo: $memo + creationDate: $creationDate + moderator: $moderator + ) + } +` + +export const confirmPendingCreation = gql` + mutation ($id: Int!) { + confirmPendingCreation(id: $id) + } +` diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts new file mode 100644 index 000000000..04f849727 --- /dev/null +++ b/backend/src/seeds/graphql/queries.ts @@ -0,0 +1,145 @@ +import gql from 'graphql-tag' + +export const login = gql` + query ($email: String!, $password: String!, $publisherId: Int) { + login(email: $email, password: $password, publisherId: $publisherId) { + id + email + firstName + lastName + language + coinanimation + klickTipp { + newsletterState + } + hasElopage + publisherId + isAdmin + } + } +` + +export const verifyLogin = gql` + query { + verifyLogin { + email + firstName + lastName + language + coinanimation + klickTipp { + newsletterState + } + hasElopage + publisherId + isAdmin + } + } +` + +export const logout = gql` + query { + logout + } +` + +export const transactionsQuery = gql` + query ( + $currentPage: Int = 1 + $pageSize: Int = 25 + $order: Order = DESC + $onlyCreations: Boolean = false + ) { + transactionList( + currentPage: $currentPage + pageSize: $pageSize + order: $order + onlyCreations: $onlyCreations + ) { + balanceGDT + count + balance + decayStartBlock + transactions { + id + typeId + amount + balance + balanceDate + memo + linkedUser { + firstName + lastName + } + decay { + decay + start + end + duration + } + } + } + } +` + +export const sendResetPasswordEmail = gql` + query ($email: String!) { + sendResetPasswordEmail(email: $email) + } +` + +export const listGDTEntriesQuery = gql` + query ($currentPage: Int!, $pageSize: Int!) { + listGDTEntries(currentPage: $currentPage, pageSize: $pageSize) { + count + gdtEntries { + id + amount + date + comment + gdtEntryType + factor + gdt + } + gdtSum + } + } +` + +export const communityInfo = gql` + query { + getCommunityInfo { + name + description + registerUrl + url + } + } +` + +export const communities = gql` + query { + communities { + id + name + url + description + registerUrl + } + } +` + +export const queryTransactionLink = gql` + query ($code: String!) { + queryTransactionLink(code: $code) { + amount + memo + createdAt + validUntil + user { + firstName + publisherId + } + } + } +` diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts new file mode 100644 index 000000000..37c9992a7 --- /dev/null +++ b/backend/src/seeds/index.ts @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import createServer from '../server/createServer' +import { createTestClient } from 'apollo-server-testing' + +import { name, internet, random } from 'faker' + +import { users } from './users/index' +import { creations } from './creation/index' +import { transactionLinks } from './transactionLink/index' +import { userFactory } from './factory/user' +import { creationFactory } from './factory/creation' +import { transactionLinkFactory } from './factory/transactionLink' +import { entities } from '@entity/index' + +const context = { + token: '', + setHeaders: { + push: (value: { key: string; value: string }): void => { + context.token = value.value + }, + // eslint-disable-next-line @typescript-eslint/no-empty-function + forEach: (): void => {}, + }, +} + +export const cleanDB = async () => { + // this only works as lond we do not have foreign key constraints + for (let i = 0; i < entities.length; i++) { + await resetEntity(entities[i]) + } +} + +const resetEntity = async (entity: any) => { + const items = await entity.find({ withDeleted: true }) + if (items.length > 0) { + const ids = items.map((i: any) => i.id) + await entity.delete(ids) + } +} + +const run = async () => { + const server = await createServer(context) + const seedClient = createTestClient(server.apollo) + const { con } = server + await cleanDB() + + // seed the standard users + for (let i = 0; i < users.length; i++) { + await userFactory(seedClient, users[i]) + } + + // seed 100 random users + for (let i = 0; i < 100; i++) { + await userFactory(seedClient, { + firstName: name.firstName(), + lastName: name.lastName(), + email: internet.email(), + language: random.boolean() ? 'en' : 'de', + }) + } + + // create GDD + for (let i = 0; i < creations.length; i++) { + await creationFactory(seedClient, creations[i]) + } + + // create Transaction Links + for (let i = 0; i < transactionLinks.length; i++) { + await transactionLinkFactory(seedClient, transactionLinks[i]) + } + + await con.close() +} + +run() diff --git a/backend/src/seeds/transactionLink/TransactionLinkInterface.ts b/backend/src/seeds/transactionLink/TransactionLinkInterface.ts new file mode 100644 index 000000000..ddc835b33 --- /dev/null +++ b/backend/src/seeds/transactionLink/TransactionLinkInterface.ts @@ -0,0 +1,7 @@ +export interface TransactionLinkInterface { + email: string + amount: number + memo: string + createdAt?: Date + deletedAt?: boolean +} diff --git a/backend/src/seeds/transactionLink/index.ts b/backend/src/seeds/transactionLink/index.ts new file mode 100644 index 000000000..25755c0f2 --- /dev/null +++ b/backend/src/seeds/transactionLink/index.ts @@ -0,0 +1,52 @@ +import { TransactionLinkInterface } from './TransactionLinkInterface' + +export const transactionLinks: TransactionLinkInterface[] = [ + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: new Date(2022, 0, 1), + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: `Kein Trick, keine Zauberrei, +bei Gradidio sei dabei!`, + }, + { + email: 'bibi@bloxberg.de', + amount: 19.99, + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: true, + }, +] diff --git a/backend/src/seeds/users/UserInterface.ts b/backend/src/seeds/users/UserInterface.ts new file mode 100644 index 000000000..08aa5d19d --- /dev/null +++ b/backend/src/seeds/users/UserInterface.ts @@ -0,0 +1,12 @@ +export interface UserInterface { + email?: string + firstName?: string + lastName?: string + // description?: string + createdAt?: Date + emailChecked?: boolean + language?: string + deletedAt?: Date + publisherId?: number + isAdmin?: boolean +} diff --git a/backend/src/seeds/users/bibi-bloxberg.ts b/backend/src/seeds/users/bibi-bloxberg.ts new file mode 100644 index 000000000..7c372848e --- /dev/null +++ b/backend/src/seeds/users/bibi-bloxberg.ts @@ -0,0 +1,11 @@ +import { UserInterface } from './UserInterface' + +export const bibiBloxberg: UserInterface = { + email: 'bibi@bloxberg.de', + firstName: 'Bibi', + lastName: 'Bloxberg', + // description: 'Hex Hex', + emailChecked: true, + language: 'de', + publisherId: 1234, +} diff --git a/backend/src/seeds/users/bob-baumeister.ts b/backend/src/seeds/users/bob-baumeister.ts new file mode 100644 index 000000000..ac88611b8 --- /dev/null +++ b/backend/src/seeds/users/bob-baumeister.ts @@ -0,0 +1,10 @@ +import { UserInterface } from './UserInterface' + +export const bobBaumeister: UserInterface = { + email: 'bob@baumeister.de', + firstName: 'Bob', + lastName: 'der Baumeister', + // description: 'Können wir das schaffen? Ja, wir schaffen das!', + emailChecked: true, + language: 'de', +} diff --git a/backend/src/seeds/users/garrick-ollivander.ts b/backend/src/seeds/users/garrick-ollivander.ts new file mode 100644 index 000000000..264a866bd --- /dev/null +++ b/backend/src/seeds/users/garrick-ollivander.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const garrickOllivander: UserInterface = { + email: 'garrick@ollivander.com', + firstName: 'Garrick', + lastName: 'Ollivander', + // description: `Curious ... curious ... + // Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`, + createdAt: new Date('2022-01-10T10:23:17'), + emailChecked: false, + language: 'en', +} diff --git a/backend/src/seeds/users/index.ts b/backend/src/seeds/users/index.ts new file mode 100644 index 000000000..7a6dbe519 --- /dev/null +++ b/backend/src/seeds/users/index.ts @@ -0,0 +1,15 @@ +import { peterLustig } from './peter-lustig' +import { bibiBloxberg } from './bibi-bloxberg' +import { bobBaumeister } from './bob-baumeister' +import { raeuberHotzenplotz } from './raeuber-hotzenplotz' +import { stephenHawking } from './stephen-hawking' +import { garrickOllivander } from './garrick-ollivander' + +export const users = [ + peterLustig, + bibiBloxberg, + bobBaumeister, + raeuberHotzenplotz, + stephenHawking, + garrickOllivander, +] diff --git a/backend/src/seeds/users/peter-lustig.ts b/backend/src/seeds/users/peter-lustig.ts new file mode 100644 index 000000000..0cdc67829 --- /dev/null +++ b/backend/src/seeds/users/peter-lustig.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const peterLustig: UserInterface = { + email: 'peter@lustig.de', + firstName: 'Peter', + lastName: 'Lustig', + // description: 'Latzhose und Nickelbrille', + createdAt: new Date('2020-11-25T10:48:43'), + emailChecked: true, + language: 'de', + isAdmin: true, +} diff --git a/backend/src/seeds/users/raeuber-hotzenplotz.ts b/backend/src/seeds/users/raeuber-hotzenplotz.ts new file mode 100644 index 000000000..62601efff --- /dev/null +++ b/backend/src/seeds/users/raeuber-hotzenplotz.ts @@ -0,0 +1,10 @@ +import { UserInterface } from './UserInterface' + +export const raeuberHotzenplotz: UserInterface = { + email: 'raeuber@hotzenplotz.de', + firstName: 'Räuber', + lastName: 'Hotzenplotz', + // description: 'Pfefferpistole', + emailChecked: true, + language: 'de', +} diff --git a/backend/src/seeds/users/stephen-hawking.ts b/backend/src/seeds/users/stephen-hawking.ts new file mode 100644 index 000000000..a683b7579 --- /dev/null +++ b/backend/src/seeds/users/stephen-hawking.ts @@ -0,0 +1,12 @@ +import { UserInterface } from './UserInterface' + +export const stephenHawking: UserInterface = { + email: 'stephen@hawking.uk', + firstName: 'Stephen', + lastName: 'Hawking', + // description: 'A Brief History of Time', + emailChecked: true, + createdAt: new Date('1942-01-08T09:17:52'), + deletedAt: new Date('2018-03-14T09:17:52'), + language: 'en', +} diff --git a/backend/src/server/createServer.ts b/backend/src/server/createServer.ts index 4596eef4a..8315fda58 100644 --- a/backend/src/server/createServer.ts +++ b/backend/src/server/createServer.ts @@ -1,12 +1,11 @@ import 'reflect-metadata' -import 'module-alias/register' import { ApolloServer } from 'apollo-server-express' import express, { Express } from 'express' // database -import connection from '../typeorm/connection' -import { checkDBVersion } from '../typeorm/DBVersion' +import connection from '@/typeorm/connection' +import { checkDBVersion } from '@/typeorm/DBVersion' // server import cors from './cors' @@ -14,13 +13,13 @@ import serverContext from './context' import plugins from './plugins' // config -import CONFIG from '../config' +import CONFIG from '@/config' // graphql -import schema from '../graphql/schema' +import schema from '@/graphql/schema' // webhooks -import { elopageWebhook } from '../webhook/elopage' +import { elopageWebhook } from '@/webhook/elopage' import { Connection } from '@dbTools/typeorm' // TODO implement diff --git a/backend/src/typeorm/connection.ts b/backend/src/typeorm/connection.ts index bfd64f92f..745b2da94 100644 --- a/backend/src/typeorm/connection.ts +++ b/backend/src/typeorm/connection.ts @@ -1,7 +1,7 @@ // TODO This is super weird - since the entities are defined in another project they have their own globals. // We cannot use our connection here, but must use the external typeorm installation import { Connection, createConnection, FileLogger } from '@dbTools/typeorm' -import CONFIG from '../config' +import CONFIG from '@/config' import { entities } from '@entity/index' const connection = async (): Promise => { diff --git a/backend/src/typeorm/repository/Transaction.ts b/backend/src/typeorm/repository/Transaction.ts index e1af105f8..f84b57626 100644 --- a/backend/src/typeorm/repository/Transaction.ts +++ b/backend/src/typeorm/repository/Transaction.ts @@ -1,7 +1,7 @@ import { EntityRepository, Repository } from '@dbTools/typeorm' import { Transaction } from '@entity/Transaction' -import { Order } from '../../graphql/enum/Order' -import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId' +import { Order } from '@enum/Order' +import { TransactionTypeId } from '@enum/TransactionTypeId' @EntityRepository(Transaction) export class TransactionRepository extends Repository { diff --git a/backend/src/typeorm/repository/TransactionLink.ts b/backend/src/typeorm/repository/TransactionLink.ts new file mode 100644 index 000000000..46926673a --- /dev/null +++ b/backend/src/typeorm/repository/TransactionLink.ts @@ -0,0 +1,39 @@ +import { Repository, EntityRepository } from '@dbTools/typeorm' +import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' +import Decimal from 'decimal.js-light' + +@EntityRepository(dbTransactionLink) +export class TransactionLinkRepository extends Repository { + async summary( + userId: number, + date: Date, + ): Promise<{ + sumHoldAvailableAmount: Decimal + sumAmount: Decimal + lastDate: Date | null + firstDate: Date | null + transactionLinkcount: number + }> { + const { sumHoldAvailableAmount, sumAmount, lastDate, firstDate, count } = + await this.createQueryBuilder('transactionLinks') + .select('SUM(transactionLinks.holdAvailableAmount)', 'sumHoldAvailableAmount') + .addSelect('SUM(transactionLinks.amount)', 'sumAmount') + .addSelect('MAX(transactionLinks.validUntil)', 'lastDate') + .addSelect('MIN(transactionLinks.createdAt)', 'firstDate') + .addSelect('COUNT(*)', 'count') + .where('transactionLinks.userId = :userId', { userId }) + .andWhere('transactionLinks.redeemedAt is NULL') + .andWhere('transactionLinks.validUntil > :date', { date }) + .orderBy('transactionLinks.createdAt', 'DESC') + .getRawOne() + return { + sumHoldAvailableAmount: sumHoldAvailableAmount + ? new Decimal(sumHoldAvailableAmount) + : new Decimal(0), + sumAmount: sumAmount ? new Decimal(sumAmount) : new Decimal(0), + lastDate: lastDate || null, + firstDate: firstDate || null, + transactionLinkcount: count || 0, + } + } +} diff --git a/backend/src/typeorm/repository/UserSettingRepository.ts b/backend/src/typeorm/repository/UserSettingRepository.ts index 42171e04a..528090ff2 100644 --- a/backend/src/typeorm/repository/UserSettingRepository.ts +++ b/backend/src/typeorm/repository/UserSettingRepository.ts @@ -1,7 +1,7 @@ import { EntityRepository, Repository } from '@dbTools/typeorm' import { UserSetting } from '@entity/UserSetting' -import { Setting } from '../../graphql/enum/Setting' -import { isStringBoolean } from '../../util/validate' +import { Setting } from '@enum/Setting' +import { isStringBoolean } from '@/util/validate' @EntityRepository(UserSetting) export class UserSettingRepository extends Repository { diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index 5dfec9327..33ac2fad2 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -2,7 +2,7 @@ import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' import { User as dbUser } from '@entity/User' -import { User } from '../graphql/model/User' +import { User } from '@model/User' const communityDbUser: dbUser = { id: -1, diff --git a/backend/src/util/decay.ts b/backend/src/util/decay.ts index d26afbd33..36f83f23f 100644 --- a/backend/src/util/decay.ts +++ b/backend/src/util/decay.ts @@ -1,6 +1,6 @@ import Decimal from 'decimal.js-light' -import CONFIG from '../config' -import { Decay } from '../graphql/model/Decay' +import CONFIG from '@/config' +import { Decay } from '@model/Decay' // TODO: externalize all those definitions and functions into an external decay library diff --git a/backend/src/util/validate.ts b/backend/src/util/validate.ts index eba206b24..95e1bf699 100644 --- a/backend/src/util/validate.ts +++ b/backend/src/util/validate.ts @@ -1,7 +1,9 @@ import { calculateDecay } from './decay' import Decimal from 'decimal.js-light' import { Transaction } from '@entity/Transaction' -import { Decay } from '../graphql/model/Decay' +import { Decay } from '@model/Decay' +import { getCustomRepository } from '@dbTools/typeorm' +import { TransactionLinkRepository } from '@repository/TransactionLink' function isStringBoolean(value: string): boolean { const lowerValue = value.toLowerCase() @@ -24,9 +26,13 @@ async function calculateBalance( if (!lastTransaction) return null const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time) + // TODO why we have to use toString() here? const balance = decay.balance.add(amount.toString()) - if (balance.lessThan(0)) { + const transactionLinkRepository = getCustomRepository(TransactionLinkRepository) + const { sumHoldAvailableAmount } = await transactionLinkRepository.summary(userId, time) + + if (balance.minus(sumHoldAvailableAmount.toString()).lessThan(0)) { return null } return { balance, lastTransactionId: lastTransaction.id, decay } diff --git a/backend/src/util/virtualDecayTransaction.ts b/backend/src/util/virtualDecayTransaction.ts deleted file mode 100644 index df3482716..000000000 --- a/backend/src/util/virtualDecayTransaction.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import Decimal from 'decimal.js-light' -import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' -import { Transaction as dbTransaction } from '@entity/Transaction' -import { calculateDecay } from './decay' -import { TransactionTypeId } from '../graphql/enum/TransactionTypeId' -import { Transaction } from '../graphql/model/Transaction' -import { User } from '../graphql/model/User' - -const virtualDecayTransaction = ( - balance: Decimal, - balanceDate: Date, - time: Date = new Date(), - user: User, -): Transaction => { - const decay = calculateDecay(balance, balanceDate, time) - // const balance = decay.balance.minus(lastTransaction.balance) - const decayDbTransaction: dbTransaction = { - id: -1, - userId: -1, - previous: -1, - typeId: TransactionTypeId.DECAY, - amount: decay.decay ? decay.decay : new Decimal(0), // new Decimal(0), // this kinda is wrong, but helps with the frontend query - balance: decay.balance, - balanceDate: time, - decay: decay.decay ? decay.decay : new Decimal(0), - decayStart: decay.start, - memo: '', - creationDate: null, - hasId: function (): boolean { - throw new Error('Function not implemented.') - }, - save: function (options?: SaveOptions): Promise { - throw new Error('Function not implemented.') - }, - remove: function (options?: RemoveOptions): Promise { - throw new Error('Function not implemented.') - }, - softRemove: function (options?: SaveOptions): Promise { - throw new Error('Function not implemented.') - }, - recover: function (options?: SaveOptions): Promise { - throw new Error('Function not implemented.') - }, - reload: function (): Promise { - throw new Error('Function not implemented.') - }, - } - return new Transaction(decayDbTransaction, user) -} - -export { virtualDecayTransaction } diff --git a/backend/src/util/virtualTransactions.ts b/backend/src/util/virtualTransactions.ts new file mode 100644 index 000000000..e4b9eec1f --- /dev/null +++ b/backend/src/util/virtualTransactions.ts @@ -0,0 +1,82 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Transaction } from '@model/Transaction' +import { SaveOptions, RemoveOptions } from '@dbTools/typeorm' +import { Transaction as dbTransaction } from '@entity/Transaction' +import { TransactionTypeId } from '@enum/TransactionTypeId' +import { calculateDecay } from './decay' +import { User } from '@model/User' +import Decimal from 'decimal.js-light' + +const defaultModelFunctions = { + hasId: function (): boolean { + throw new Error('Function not implemented.') + }, + save: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + remove: function (options?: RemoveOptions): Promise { + throw new Error('Function not implemented.') + }, + softRemove: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + recover: function (options?: SaveOptions): Promise { + throw new Error('Function not implemented.') + }, + reload: function (): Promise { + throw new Error('Function not implemented.') + }, +} + +const virtualLinkTransaction = ( + balance: Decimal, + amount: Decimal, + holdAvailableAmount: Decimal, + decay: Decimal, + createdAt: Date, + validUntil: Date, + user: User, +): Transaction => { + const linkDbTransaction: dbTransaction = { + id: -2, + userId: -1, + previous: -1, + typeId: TransactionTypeId.LINK_SUMMARY, + amount: amount, + balance: balance, + balanceDate: validUntil, + decayStart: createdAt, + decay: decay, + memo: '', + creationDate: null, + ...defaultModelFunctions, + } + return new Transaction(linkDbTransaction, user) +} + +const virtualDecayTransaction = ( + balance: Decimal, + balanceDate: Date, + time: Date = new Date(), + user: User, +): Transaction => { + const decay = calculateDecay(balance, balanceDate, time) + // const balance = decay.balance.minus(lastTransaction.balance) + const decayDbTransaction: dbTransaction = { + id: -1, + userId: -1, + previous: -1, + typeId: TransactionTypeId.DECAY, + amount: decay.decay ? decay.decay : new Decimal(0), // new Decimal(0), // this kinda is wrong, but helps with the frontend query + balance: decay.balance, + balanceDate: time, + decay: decay.decay ? decay.decay : new Decimal(0), + decayStart: decay.start, + memo: '', + creationDate: null, + ...defaultModelFunctions, + } + return new Transaction(decayDbTransaction, user) +} + +export { virtualLinkTransaction, virtualDecayTransaction } diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index abd139552..d5eaef521 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -28,7 +28,7 @@ */ import { LoginElopageBuys } from '@entity/LoginElopageBuys' -import { UserResolver } from '../graphql/resolver/UserResolver' +import { UserResolver } from '@/graphql/resolver/UserResolver' import { User as dbUser } from '@entity/User' export const elopageWebhook = async (req: any, res: any): Promise => { diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts new file mode 100644 index 000000000..51610b07e --- /dev/null +++ b/backend/test/helpers.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { createTestClient } from 'apollo-server-testing' +import createServer from '../src/server/createServer' +import { initialize } from '@dbTools/helpers' +import { entities } from '@entity/index' + +export const headerPushMock = jest.fn((t) => { + context.token = t.value +}) + +const context = { + token: '', + setHeaders: { + push: headerPushMock, + forEach: jest.fn(), + }, +} + +export const cleanDB = async () => { + // this only works as lond we do not have foreign key constraints + for (let i = 0; i < entities.length; i++) { + await resetEntity(entities[i]) + } +} + +export const testEnvironment = async () => { + const server = await createServer(context) + const con = server.con + const testClient = createTestClient(server.apollo) + const mutate = testClient.mutate + const query = testClient.query + await initialize() + return { mutate, query, con } +} + +export const resetEntity = async (entity: any) => { + const items = await entity.find({ withDeleted: true }) + if (items.length > 0) { + const ids = items.map((i: any) => i.id) + await entity.delete(ids) + } +} + +export const resetToken = () => { + context.token = '' +} diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index ed2c5cf49..d42836626 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -4,3 +4,4 @@ // eslint-disable-next-line @typescript-eslint/no-empty-function console.info = () => {} +jest.setTimeout(1000000) diff --git a/backend/tsconfig.json b/backend/tsconfig.json index eabdc75b0..f81bf22d5 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -45,10 +45,17 @@ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - "@entity/*": ["../database/entity/*"], - "@dbTools/*": ["../database/src/*"] + "@/*": ["src/*"], + "@arg/*": ["src/graphql/arg/*"], + "@enum/*": ["src/graphql/enum/*"], + "@model/*": ["src/graphql/model/*"], + "@repository/*": ["src/typeorm/repository/*"], + "@test/*": ["test/*"], + /* external */ + "@dbTools/*": ["../database/src/*", "../../database/build/src/*"], + "@entity/*": ["../database/entity/*", "../../database/build/entity/*"] }, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ @@ -76,7 +83,7 @@ { "path": "../database/tsconfig.json", // add 'prepend' if you want to include the referenced project in your output file - // "prepend": true, + // "prepend": true } ] } diff --git a/backend/yarn.lock b/backend/yarn.lock index 330a6d590..4134e1d37 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -811,6 +811,11 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/faker@^5.5.9": + version "5.5.9" + resolved "https://registry.yarnpkg.com/@types/faker/-/faker-5.5.9.tgz#588ede92186dc557bff8341d294335d50d255f0c" + integrity sha512-uCx6mP3UY5SIO14XlspxsGjgaemrxpssJI0Ol+GfhxtcKpv9pgRZYsS4eeKeHVLje6Qtc8lGszuBI461+gVZBA== + "@types/fs-capacitor@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" @@ -2510,6 +2515,11 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +faker@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4092,11 +4102,6 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -module-alias@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" - integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -5232,6 +5237,16 @@ tsconfig-paths@^3.11.0: minimist "^1.2.0" strip-bom "^3.0.0" +tsconfig-paths@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz#4fcc48f9ccea8826c41b9ca093479de7f5018976" + integrity sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" diff --git a/database/.env.dist b/database/.env.dist index 689e4f509..58362a7b9 100644 --- a/database/.env.dist +++ b/database/.env.dist @@ -1,3 +1,5 @@ +CONFIG_VERSION=v1.2022-03-18 + DB_HOST=localhost DB_PORT=3306 DB_USER=root diff --git a/database/.env.template b/database/.env.template index 5b8554bcf..f2517a397 100644 --- a/database/.env.template +++ b/database/.env.template @@ -1,3 +1,5 @@ +CONFIG_VERSION=$DATABASE_CONFIG_VERSION + DB_HOST=localhost DB_PORT=3306 DB_USER=$DB_USER diff --git a/database/entity/0030-transaction_link/TransactionLink.ts b/database/entity/0030-transaction_link/TransactionLink.ts new file mode 100644 index 000000000..177f23561 --- /dev/null +++ b/database/entity/0030-transaction_link/TransactionLink.ts @@ -0,0 +1,68 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('transaction_links') +export class TransactionLink extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ unsigned: true, nullable: false }) + userId: number + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ + type: 'decimal', + name: 'hold_available_amount', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + holdAvailableAmount: Decimal + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) + code: string + + @Column({ + type: 'datetime', + nullable: false, + }) + createdAt: Date + + @DeleteDateColumn() + deletedAt: Date | null + + @Column({ + type: 'datetime', + nullable: false, + }) + validUntil: Date + + @Column({ + type: 'boolean', + default: () => false, + nullable: false, + }) + showEmail: boolean + + @Column({ + type: 'datetime', + nullable: true, + }) + redeemedAt: Date | null + + @Column({ type: 'int', unsigned: true, nullable: true }) + redeemedBy: number | null +} diff --git a/database/entity/0031-remove_sendEmail_from_transaction_link/TransactionLink.ts b/database/entity/0031-remove_sendEmail_from_transaction_link/TransactionLink.ts new file mode 100644 index 000000000..9cd42258a --- /dev/null +++ b/database/entity/0031-remove_sendEmail_from_transaction_link/TransactionLink.ts @@ -0,0 +1,61 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('transaction_links') +export class TransactionLink extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ unsigned: true, nullable: false }) + userId: number + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ + type: 'decimal', + name: 'hold_available_amount', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + holdAvailableAmount: Decimal + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) + code: string + + @Column({ + type: 'datetime', + nullable: false, + }) + createdAt: Date + + @DeleteDateColumn() + deletedAt: Date | null + + @Column({ + type: 'datetime', + nullable: false, + }) + validUntil: Date + + @Column({ + type: 'datetime', + nullable: true, + }) + redeemedAt: Date | null + + @Column({ type: 'int', unsigned: true, nullable: true }) + redeemedBy: number | null +} diff --git a/database/entity/0032-add-transaction-link-to-transaction/Transaction.ts b/database/entity/0032-add-transaction-link-to-transaction/Transaction.ts new file mode 100644 index 000000000..398fa03db --- /dev/null +++ b/database/entity/0032-add-transaction-link-to-transaction/Transaction.ts @@ -0,0 +1,94 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('transactions') +export class Transaction extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @Column({ type: 'int', unsigned: true, nullable: true, default: null }) + previous: number | null + + @Column({ name: 'type_id', unsigned: true, nullable: false }) + typeId: number + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + balance: Decimal + + @Column({ + name: 'balance_date', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP', + nullable: false, + }) + balanceDate: Date + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + decay: Decimal + + @Column({ + name: 'decay_start', + type: 'datetime', + nullable: true, + default: null, + }) + decayStart: Date | null + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ name: 'creation_date', type: 'datetime', nullable: true, default: null }) + creationDate: Date | null + + @Column({ + name: 'linked_user_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedUserId?: number | null + + @Column({ + name: 'linked_transaction_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + linkedTransactionId?: number | null + + @Column({ + name: 'transaction_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + transactionLinkId?: number | null +} diff --git a/database/entity/0033-add_referrer_id/User.ts b/database/entity/0033-add_referrer_id/User.ts new file mode 100644 index 000000000..48c804b63 --- /dev/null +++ b/database/entity/0033-add_referrer_id/User.ts @@ -0,0 +1,78 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + OneToMany, + DeleteDateColumn, +} from 'typeorm' +import { UserSetting } from '../UserSetting' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @DeleteDateColumn() + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ + type: 'text', + name: 'passphrase', + collation: 'utf8mb4_unicode_ci', + nullable: true, + default: null, + }) + passphrase: string + + @OneToMany(() => UserSetting, (userSetting) => userSetting.user) + settings: UserSetting[] +} diff --git a/database/entity/Transaction.ts b/database/entity/Transaction.ts index 56c77c557..3515202d0 100644 --- a/database/entity/Transaction.ts +++ b/database/entity/Transaction.ts @@ -1 +1 @@ -export { Transaction } from './0029-clean_transaction_table/Transaction' +export { Transaction } from './0032-add-transaction-link-to-transaction/Transaction' diff --git a/database/entity/TransactionLink.ts b/database/entity/TransactionLink.ts new file mode 100644 index 000000000..a483f0171 --- /dev/null +++ b/database/entity/TransactionLink.ts @@ -0,0 +1 @@ +export { TransactionLink } from './0031-remove_sendEmail_from_transaction_link/TransactionLink' diff --git a/database/entity/User.ts b/database/entity/User.ts index d2c201c54..35dfb7bbe 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0023-users_disabled_soft_delete/User' +export { User } from './0033-add_referrer_id/User' diff --git a/database/entity/index.ts b/database/entity/index.ts index bee4e2b77..cb6f56ab0 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -3,6 +3,7 @@ import { LoginEmailOptIn } from './LoginEmailOptIn' import { Migration } from './Migration' import { ServerUser } from './ServerUser' import { Transaction } from './Transaction' +import { TransactionLink } from './TransactionLink' import { User } from './User' import { UserSetting } from './UserSetting' import { AdminPendingCreation } from './AdminPendingCreation' @@ -14,6 +15,7 @@ export const entities = [ Migration, ServerUser, Transaction, + TransactionLink, User, UserSetting, ] diff --git a/database/migrations/0030-transaction_link.ts b/database/migrations/0030-transaction_link.ts new file mode 100644 index 000000000..ee76c980d --- /dev/null +++ b/database/migrations/0030-transaction_link.ts @@ -0,0 +1,28 @@ +/* MIGRATION TO CREATE TRANSACTION_LINK TABLE */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE \`transaction_links\` ( + \`id\` int UNSIGNED NOT NULL AUTO_INCREMENT, + \`userId\` int UNSIGNED NOT NULL, + \`amount\` DECIMAL(40,20) NOT NULL, + \`hold_available_amount\` DECIMAL(40,20) NOT NULL, + \`memo\` varchar(255) NOT NULL, + \`code\` varchar(24) NOT NULL, + \`createdAt\` datetime NOT NULL, + \`deletedAt\` datetime DEFAULT NULL, + \`validUntil\` datetime NOT NULL, + \`showEmail\` boolean NOT NULL DEFAULT false, + \`redeemedAt\` datetime, + \`redeemedBy\` int UNSIGNED, + PRIMARY KEY (\`id\`) + ) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + `) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(`DROP TABLE \`transaction_links\`;`) +} diff --git a/database/migrations/0031-remove_sendEmail_from_transaction_link.ts b/database/migrations/0031-remove_sendEmail_from_transaction_link.ts new file mode 100644 index 000000000..76b2ee742 --- /dev/null +++ b/database/migrations/0031-remove_sendEmail_from_transaction_link.ts @@ -0,0 +1,14 @@ +/* MIGRATION TO REMOVE sendEmail FIELD FROM TRANSACTION_LINK TABLE */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `transaction_links` DROP COLUMN `showEmail`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `transaction_links` ADD COLUMN `showEmail` boolean NOT NULL DEFAULT false AFTER `validUntil`;', + ) +} diff --git a/database/migrations/0032-add-transaction-link-to-transaction.ts b/database/migrations/0032-add-transaction-link-to-transaction.ts new file mode 100644 index 000000000..79cdf195a --- /dev/null +++ b/database/migrations/0032-add-transaction-link-to-transaction.ts @@ -0,0 +1,14 @@ +/* MIGRATION TO ADD transactionLinkId FIELDTO TRANSACTION */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `transactions` ADD COLUMN `transaction_link_id` int UNSIGNED DEFAULT NULL AFTER `linked_transaction_id`;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `transactions` DROP COLUMN `transaction_link_id`;') +} diff --git a/database/migrations/0033-add_referrer_id.ts b/database/migrations/0033-add_referrer_id.ts new file mode 100644 index 000000000..e45d1faf7 --- /dev/null +++ b/database/migrations/0033-add_referrer_id.ts @@ -0,0 +1,14 @@ +/* MIGRATION TO ADD referrer_id FIELD TO users */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `users` ADD COLUMN `referrer_id` int UNSIGNED DEFAULT NULL AFTER `language`;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `users` DROP COLUMN `referrer_id`;') +} diff --git a/database/src/config/index.ts b/database/src/config/index.ts index 2dde06c96..ba41f11d4 100644 --- a/database/src/config/index.ts +++ b/database/src/config/index.ts @@ -3,6 +3,14 @@ import dotenv from 'dotenv' dotenv.config() +const constants = { + CONFIG_VERSION: { + DEFAULT: 'DEFAULT', + EXPECTED: 'v1.2022-03-18', + CURRENT: '', + }, +} + const database = { DB_HOST: process.env.DB_HOST || 'localhost', DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, @@ -15,6 +23,18 @@ const migrations = { MIGRATIONS_TABLE: process.env.MIGRATIONS_TABLE || 'migrations', } -const CONFIG = { ...database, ...migrations } +// 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, ...database, ...migrations } export default CONFIG diff --git a/database/src/helpers.ts b/database/src/helpers.ts index 710094548..acd0e6ccd 100644 --- a/database/src/helpers.ts +++ b/database/src/helpers.ts @@ -1,5 +1,6 @@ import CONFIG from './config' import { createPool, PoolConfig } from 'mysql' +import { useSeeding, runSeeder } from 'typeorm-seeding' import { Migration } from 'ts-mysql-migrate' import path from 'path' @@ -31,4 +32,17 @@ const resetDB = async (closePool = false): Promise => { if (closePool) pool.end() } -export { resetDB, pool, migration, initialize } +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +const runSeeds = async (seeds: any[]): Promise => { + if (seeds.length > 0) { + await useSeeding({ + root: process.cwd(), + configName: 'ormconfig.js', + }) + for (let i = 0; i < seeds.length; i++) { + await runSeeder(seeds[i]) + } + } +} + +export { resetDB, pool, migration, initialize, runSeeds } diff --git a/database/src/index.ts b/database/src/index.ts index 6e99ac582..40c5913e4 100644 --- a/database/src/index.ts +++ b/database/src/index.ts @@ -1,7 +1,6 @@ import 'reflect-metadata' import prepare from './prepare' import connection from './typeorm/connection' -import { useSeeding, runSeeder } from 'typeorm-seeding' import { CreatePeterLustigSeed } from './seeds/users/peter-lustig.admin.seed' import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed' import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed' @@ -9,7 +8,7 @@ import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed' import { CreateStephenHawkingSeed } from './seeds/users/stephen-hawking.seed' import { CreateGarrickOllivanderSeed } from './seeds/users/garrick-ollivander.seed' import { CreateUserSeed } from './seeds/create-user.seed' -import { resetDB, pool, migration } from './helpers' +import { resetDB, pool, migration, runSeeds } from './helpers' const run = async (command: string) => { // Database actions not supported by our migration library @@ -37,20 +36,16 @@ const run = async (command: string) => { break case 'seed': // TODO protect from production - await useSeeding({ - root: process.cwd(), - configName: 'ormconfig.js', - }) - await runSeeder(CreatePeterLustigSeed) - await runSeeder(CreateBibiBloxbergSeed) - await runSeeder(CreateRaeuberHotzenplotzSeed) - await runSeeder(CreateBobBaumeisterSeed) - await runSeeder(CreateStephenHawkingSeed) - // eslint-disable-next-line prefer-spread - Array.apply(null, Array(96)).forEach(async () => { - await runSeeder(CreateUserSeed) - }) - await runSeeder(CreateGarrickOllivanderSeed) + // await runSeeder(CreatePeterLustigSeed) + await runSeeds([ + CreatePeterLustigSeed, + CreateBibiBloxbergSeed, + CreateRaeuberHotzenplotzSeed, + CreateBobBaumeisterSeed, + CreateStephenHawkingSeed, + CreateGarrickOllivanderSeed, + ...Array(96).fill(CreateUserSeed), + ]) break default: throw new Error(`Unsupported command ${command}`) diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 91e37b594..ee0ac3cc3 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -18,6 +18,8 @@ WEBHOOK_GITHUB_SECRET=secret WEBHOOK_GITHUB_BRANCH=master # backend +BACKEND_CONFIG_VERSION=v1.2022-03-18 + EMAIL=true EMAIL_USERNAME=peter@lustig.de EMAIL_SENDER=peter@lustig.de @@ -43,10 +45,22 @@ KLICKTIPP_PASSWORD= KLICKTIPP_APIKEY_DE= KLICKTIPP_APIKEY_EN= +# database +DATABASE_CONFIG_VERSION=v1.2022-03-18 + # frontend GRAPHQL_URI=https://stage1.gradido.net/graphql ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} +META_URL=http://localhost +META_TITLE_DE="Gradido – Dein Dankbarkeitskonto" +META_TITLE_EN="Gradido - Your gratitude account" +META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle." +META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." +META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" +META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" +META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" + # admin WALLET_AUTH_URL=https://stage1.gradido.net/authenticate?token={token} WALLET_URL=https://stage1.gradido.net/login \ No newline at end of file diff --git a/deployment/bare_metal/logrotate/gradido.conf.template b/deployment/bare_metal/logrotate/gradido.conf.template index c038f8e75..c543b54c2 100644 --- a/deployment/bare_metal/logrotate/gradido.conf.template +++ b/deployment/bare_metal/logrotate/gradido.conf.template @@ -1,4 +1,4 @@ -$GRADIDO_LOG_PATH/* { +$GRADIDO_LOG_PATH/*.log { weekly rotate 26 size 10M diff --git a/deployment/bare_metal/nginx/update-page/updating.html.template b/deployment/bare_metal/nginx/update-page/updating.html.template index a88a40b0f..cc6d7debb 100644 --- a/deployment/bare_metal/nginx/update-page/updating.html.template +++ b/deployment/bare_metal/nginx/update-page/updating.html.template @@ -1,3 +1,4 @@ -Gradido is currently updating...
-please stand by and try again in some minutes
-
\ No newline at end of file +
+Gradido is currently updating...
+please stand by and try again in some minutes
+
diff --git a/deployment/bare_metal/start.sh b/deployment/bare_metal/start.sh
index 616e4b8ab..250971419 100755
--- a/deployment/bare_metal/start.sh
+++ b/deployment/bare_metal/start.sh
@@ -42,30 +42,38 @@ if [ -f $LOCK_FILE ] ; then
 fi
 touch $LOCK_FILE
 
+# find today string
+TODAY=$(date +"%Y-%m-%d")
+
 # Create a new updating.html from the template
 \cp $SCRIPT_DIR/nginx/update-page/updating.html.template $UPDATE_HTML
 
+# redirect all output of the script to the UPDATE_HTML and also have things on console
+# TODO: this might pose a security risk
+exec > >(tee -a $UPDATE_HTML) 2>&1
+
 # configure nginx for the update-page
-echo 'Configuring nginx to serve the update-page
' >> $UPDATE_HTML +echo 'Configuring nginx to serve the update-page' >> $UPDATE_HTML rm /etc/nginx/sites-enabled/gradido.conf ln -s /etc/nginx/sites-available/update-page.conf /etc/nginx/sites-enabled/ sudo /etc/init.d/nginx restart # stop all services -echo 'Stopping all Gradido services
' >> $UPDATE_HTML +echo 'Stopping all Gradido services' >> $UPDATE_HTML pm2 stop all # git BRANCH=${1:-master} -echo "Starting with git pull - branch:$BRANCH
" >> $UPDATE_HTML +echo "Starting with git pull - branch:$BRANCH" >> $UPDATE_HTML cd $PROJECT_ROOT -git fetch origin $BRANCH +# TODO: this overfetches alot, but ensures we can use start.sh with tags +git fetch origin --all git checkout $BRANCH git pull export BUILD_COMMIT="$(git rev-parse HEAD)" # Generate gradido.conf from template -echo 'Generate new gradido nginx config
' >> $UPDATE_HTML +echo 'Generate new gradido nginx config' >> $UPDATE_HTML case "$NGINX_SSL" in true) TEMPLATE_FILE="gradido.conf.ssl.template" ;; *) TEMPLATE_FILE="gradido.conf.template" ;; @@ -73,7 +81,7 @@ esac envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/gradido.conf # Generate update-page.conf from template -echo 'Generate new update-page nginx config
' >> $UPDATE_HTML +echo 'Generate new update-page nginx config' >> $UPDATE_HTML case "$NGINX_SSL" in true) TEMPLATE_FILE="update-page.conf.ssl.template" ;; *) TEMPLATE_FILE="update-page.conf.template" ;; @@ -91,7 +99,7 @@ envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env # Install & build database -echo 'Updating database
' >> $UPDATE_HTML +echo 'Updating database' >> $UPDATE_HTML cd $PROJECT_ROOT/database yarn install yarn build @@ -104,7 +112,7 @@ else fi # Install & build backend -echo 'Updating backend
' >> $UPDATE_HTML +echo 'Updating backend' >> $UPDATE_HTML cd $PROJECT_ROOT/backend # TODO maybe handle this differently? unset NODE_ENV @@ -113,11 +121,11 @@ yarn build # TODO maybe handle this differently? export NODE_ENV=production pm2 delete gradido-backend -pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.log --log-date-format 'DD-MM HH:mm:ss.SSS' +pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS' pm2 save # Install & build frontend -echo 'Updating frontend
' >> $UPDATE_HTML +echo 'Updating frontend' >> $UPDATE_HTML cd $PROJECT_ROOT/frontend # TODO maybe handle this differently? unset NODE_ENV @@ -126,11 +134,11 @@ yarn build # TODO maybe handle this differently? export NODE_ENV=production pm2 delete gradido-frontend -pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.log --log-date-format 'DD-MM HH:mm:ss.SSS' +pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS' pm2 save # Install & build admin -echo 'Updating admin
' >> $UPDATE_HTML +echo 'Updating admin' >> $UPDATE_HTML cd $PROJECT_ROOT/admin # TODO maybe handle this differently? unset NODE_ENV @@ -139,14 +147,17 @@ yarn build # TODO maybe handle this differently? export NODE_ENV=production pm2 delete gradido-admin -pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.log --log-date-format 'DD-MM HH:mm:ss.SSS' +pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS' pm2 save # let nginx showing gradido -echo 'Configuring nginx to serve gradido again
' >> $UPDATE_HTML +echo 'Configuring nginx to serve gradido again' >> $UPDATE_HTML ln -s /etc/nginx/sites-available/gradido.conf /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/update-page.conf sudo /etc/init.d/nginx restart +# keep the update log +cat $UPDATE_HTML >> $GRADIDO_LOG_PATH/update.$TODAY.log + # release lock rm $LOCK_FILE \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 88249539f..1ddda051a 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,3 +1,4 @@ node_modules .git -.gitignore \ No newline at end of file +.gitignore +!.eslintignore \ No newline at end of file diff --git a/frontend/.env.dist b/frontend/.env.dist index f7bda826a..df3c7cd7e 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,3 +1,13 @@ +CONFIG_VERSION=v1.2022-03-18 + +META_URL=http://localhost +META_TITLE_DE="Gradido – Dein Dankbarkeitskonto" +META_TITLE_EN="Gradido - Your gratitude account" +META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Menschen entfalten ihr Potenzial und gestalten eine gute Zukunft für alle." +META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." +META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" +META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" +META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" GRAPHQL_URI=http://localhost/graphql DEFAULT_PUBLISHER_ID=2896 ADMIN_AUTH_URL=http://localhost/admin/authenticate?token={token} \ No newline at end of file diff --git a/frontend/.env.template b/frontend/.env.template index 8c9c053cc..4e4a86d08 100644 --- a/frontend/.env.template +++ b/frontend/.env.template @@ -1,3 +1,13 @@ +CONFIG_VERSION=$FRONTEND_CONFIG_VERSION + +META_URL=$META_URL +META_TITLE_DE=$META_TITLE_DE +META_TITLE_EN=$META_TITLE_EN +META_DESCRIPTION_DE=$META_DESCRIPTION_DE +META_DESCRIPTION_EN=$META_DESCRIPTION_EN +META_KEYWORDS_DE=$META_KEYWORDS_DE +META_KEYWORDS_EN=$META_KEYWORDS_EN +META_AUTHOR=$META_AUTHOR GRAPHQL_URI=$GRAPHQL_URI DEFAULT_PUBLISHER_ID=2896 ADMIN_AUTH_URL=$ADMIN_AUTH_URL \ No newline at end of file diff --git a/frontend/.eslintignore b/frontend/.eslintignore index e973d3709..94934f9e7 100644 --- a/frontend/.eslintignore +++ b/frontend/.eslintignore @@ -1,3 +1,3 @@ -node_modules -**/*.min.js -dist \ No newline at end of file +node_modules/ +dist/ +coverage/ \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 8c410feba..4e45ede62 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -8,14 +8,58 @@ 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'], + rules: { + // TODO: enable + '@intlify/vue-i18n/no-html-messages': 'off', + }, + }, + ], // 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'], + // TODO: remove ignores + ignores: [ + '/site.thx./', + '/form./', + '/time./', + '/decay.types./', + 'settings.password.resend_subtitle', + 'settings.password.reset-password.text', + 'settings.password.set', + 'settings.password.set-password.text', + 'settings.password.subtitle', + 'site.login.signin', + ], + enableFix: false, + }, + ], + '@intlify/vue-i18n/no-missing-keys-in-other-locales': 'error', 'prettier/prettier': [ 'error', { @@ -23,4 +67,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.22.4', + }, + }, } diff --git a/frontend/.gitignore b/frontend/.gitignore index b868f0a64..b19667d17 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -5,7 +5,6 @@ dist/ npm-debug.log* yarn-debug.log* yarn-error.log* -test/unit/coverage package-lock.json /.env diff --git a/frontend/.stylelintrc.js b/frontend/.stylelintrc.js new file mode 100644 index 000000000..061b87a79 --- /dev/null +++ b/frontend/.stylelintrc.js @@ -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"], + } + ] + +}; \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 879fdf4e0..a9d7572f2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,7 +1,7 @@ ################################################################################## # BASE ########################################################################### ################################################################################## -FROM node:12.19.0-alpine3.10 as base +FROM node:16-alpine3.14 as base # ENVs (available in production aswell, can be overwritten by commandline or env file) ## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame @@ -31,8 +31,8 @@ LABEL org.label-schema.schema-version="1.0" LABEL maintainer="support@ogradido.net" # Install Additional Software -## install: git -#RUN apk --no-cache add git +## install: node-gyp dependencies +RUN apk --no-cache add g++ make python3 # Settings ## Expose Container Port diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 5caae815c..2a52ec707 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -12,6 +12,7 @@ module.exports = { '\\.(css|less)$': 'identity-obj-proxy', '\\.(scss)$': '/src/assets/mocks/styleMock.js', '^@/(.*)$': '/src/$1', + '^@test/(.*)$': '/test/$1', }, transform: { '^.+\\.vue$': 'vue-jest', diff --git a/frontend/package.json b/frontend/package.json index 3600920e4..6ccf37d09 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,12 +6,12 @@ "start": "node run/server.js", "serve": "vue-cli-service serve --open", "build": "vue-cli-service build", - "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", - "lint": "eslint --max-warnings=0 --ext .js,.vue .", "dev": "yarn run serve", - "i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'", + "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/missing-keys.sh && scripts/sort.sh" + "locales": "scripts/sort.sh" }, "dependencies": { "@babel/core": "^7.13.13", @@ -52,7 +52,6 @@ "vee-validate": "^3.4.5", "vue": "2.6.12", "vue-apollo": "^3.0.7", - "vue-cli-plugin-i18n": "^1.0.1", "vue-flatpickr-component": "^8.1.2", "vue-focus": "^2.1.0", "vue-i18n": "^8.22.4", @@ -67,6 +66,7 @@ "vuex-persistedstate": "^4.0.0-beta.3" }, "devDependencies": { + "@intlify/eslint-plugin-vue-i18n": "^1.4.0", "@vue/cli-plugin-babel": "^3.7.0", "@vue/cli-plugin-eslint": "^3.7.0", "@vue/cli-service": "^3.7.0", @@ -75,9 +75,17 @@ "babel-plugin-component": "^1.1.0", "babel-plugin-transform-require-context": "^0.1.1", "dotenv-webpack": "^7.0.3", - "node-sass": "^6.0.1", + "postcss": "^8.4.8", + "postcss-html": "^1.3.0", + "postcss-scss": "^4.0.3", + "sass": "1.32.13", "sass-loader": "^10", "stats-webpack-plugin": "^0.7.0", + "stylelint": "^14.5.3", + "stylelint-config-recommended-vue": "^1.3.0", + "stylelint-config-standard-scss": "^3.0.0", + "vue-cli-plugin-i18n": "^1.0.1", + "vue-html-webpack-plugin": "^3.2.2", "vue-template-compiler": "^2.6.11", "webpack-bundle-analyzer": "^4.5.0" }, diff --git a/frontend/public/img/gradido-wallet-650-400.jpg b/frontend/public/img/gradido-wallet-650-400.jpg new file mode 100644 index 000000000..b851460a2 Binary files /dev/null and b/frontend/public/img/gradido-wallet-650-400.jpg differ diff --git a/frontend/public/index.html b/frontend/public/index.html index 4aad83f03..b9abe3876 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + -
- -
- diff --git a/frontend/public/json-example/admin_card_statistic.json b/frontend/public/json-example/admin_card_statistic.json deleted file mode 100644 index bacdccb5a..000000000 --- a/frontend/public/json-example/admin_card_statistic.json +++ /dev/null @@ -1,36 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "i_creation_sum": 29288278.4653, - "i_creation_mon": 68278.4653, - "i_creation_365d": 19288278.4653, - "i_creation_30d": 288278.4653, - "i_creation_14d": 88278.4653, - "i_creation_7d": 3278.4653 - }, - { - "i_transience_sum": 8278.4653, - "i_transience_mon": 8278.4653, - "i_transience_365d": 8278.4653, - "i_transience_30d": 178.4653, - "i_transience_14d": 78.4653, - "i_transience_7d": 8.4653 - }, - { - "i_exchange_sum": 23345.2324, - "i_exchange_mon": 2, - "i_exchange_365d": 2, - "i_exchange_30d": 2, - "i_exchange_14d": 2, - "i_exchange_7d": 2 - }, - { - "i_members_sum": 5398, - "i_members_mon": 234, - "i_members_365d": 2356, - "i_members_30d": 123, - "i_members_14d": 23, - "i_members_7d": 24 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_charts_statistic.json b/frontend/public/json-example/admin_charts_statistic.json deleted file mode 100644 index 8c7e6667b..000000000 --- a/frontend/public/json-example/admin_charts_statistic.json +++ /dev/null @@ -1,64 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "charts_creation_0_mon": 5635, - "charts_creation_1_mon": 5635, - "charts_creation_2_mon": 5635, - "charts_creation_3_mon": 5635, - "charts_creation_4_mon": 5635, - "charts_creation_5_mon": 5635, - "charts_creation_6_mon": 5635, - "charts_creation_7_mon": 5635, - "charts_creation_8_mon": 5635, - "charts_creation_9_mon": 5635, - "charts_creation_10_mon": 5635, - "charts_creation_11_mon": 5635, - "charts_creation_12_mon": 5635 - }, - { - "charts_transience_0_mon": 5635, - "charts_transience_1_mon": 5635, - "charts_transience_2_mon": 5635, - "charts_transience_3_mon": 5635, - "charts_transience_4_mon": 5635, - "charts_transience_5_mon": 5635, - "charts_transience_6_mon": 5635, - "charts_transience_7_mon": 5635, - "charts_transience_8_mon": 5635, - "charts_transience_9_mon": 5635, - "charts_transience_10_mon": 5635, - "charts_transience_11_mon": 5635, - "charts_transience_12_mon": 5635 - }, - { - "charts_exchange_0_mon": 5635, - "charts_exchange_1_mon": 5635, - "charts_exchange_2_mon": 5635, - "charts_exchange_3_mon": 5635, - "charts_exchange_4_mon": 5635, - "charts_exchange_5_mon": 5635, - "charts_exchange_6_mon": 5635, - "charts_exchange_7_mon": 5635, - "charts_exchange_8_mon": 5635, - "charts_exchange_9_mon": 5635, - "charts_exchange_10_mon": 5635, - "charts_exchange_11_mon": 5635, - "charts_exchange_12_mon": 5635 - }, - { - "charts_members_0_mon": 5635, - "charts_members_1_mon": 5635, - "charts_members_2_mon": 5635, - "charts_members_3_mon": 5635, - "charts_members_4_mon": 5635, - "charts_members_5_mon": 5635, - "charts_members_6_mon": 5635, - "charts_members_7_mon": 5635, - "charts_members_8_mon": 5635, - "charts_members_9_mon": 5635, - "charts_members_10_mon": 5635, - "charts_members_11_mon": 5635, - "charts_members_12_mon": 5635 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_community_statistic.json b/frontend/public/json-example/admin_community_statistic.json deleted file mode 100644 index f083991aa..000000000 --- a/frontend/public/json-example/admin_community_statistic.json +++ /dev/null @@ -1,19 +0,0 @@ -{"state":"success", "statisticdata": - [ - { - "community_entries1_mon": 5635, - "community_entries2_mon": 5635, - "community_entries3_mon": 5635, - "community_entries4_mon": 5635, - "community_entries5_mon": 5635, - "community_entries6_mon": 5635, - "community_entries7_mon": 5635, - "community_entries8_mon": 5635, - "community_entries9_mon": 5635, - "community_entries0_mon": 5635, - "community_entries10_mon": 5635, - "community_entries11_mon": 5635, - "community_entries12_mon": 5635 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_transaction_list.json b/frontend/public/json-example/admin_transaction_list.json deleted file mode 100644 index a1af47db8..000000000 --- a/frontend/public/json-example/admin_transaction_list.json +++ /dev/null @@ -1,61 +0,0 @@ -{"state":"success", "transactions": - [ - { - "name": "Jon Tester", - "email": "jon@example.de", - "type": "send", - "transaction_id": 12, - "date": "29-11-2020", - "balance": 7000, - "memo": "Reperatur Waschbecken", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 11, - "date": "1-11-2020", - "balance": 10000, - "memo": "Müll gesammelt im Wald", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Maria Tester", - "email": "maria@example.de", - "type": "receive", - "transaction_id": 7, - "date": "23-10-2020", - "balance": 5000, - "memo": "Spende an Alice ", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Alice Tester", - "email": "alice@example.de", - "type": "receive", - "transaction_id": 5, - "date": "2-8-2020", - "balance": 1000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 1, - "date": "11-7-2020", - "balance": 10000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - } - ], - "transactionExecutingCount": 8750, - "count": 5 -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_transience_list.json b/frontend/public/json-example/admin_transience_list.json deleted file mode 100644 index 0cbacfbd8..000000000 --- a/frontend/public/json-example/admin_transience_list.json +++ /dev/null @@ -1,49 +0,0 @@ -{"state":"success", "participation": - [ - { - "name": "", - "type": "submitted", - "participation_id": 412, - "date_submitted": "9-12-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "8-12-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "in progress", - "participation_id": 312, - "date_submitted": "2-11-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "2-11-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "confirmed", - "participation_id": 212, - "date_submitted": "20-10-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "20-10-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "name": "", - "type": "rejected", - "participation_id": 142, - "date_submitted": "17-9-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "17-9-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - } - ], - "count": 4 -} \ No newline at end of file diff --git a/frontend/public/json-example/admin_userlist.json b/frontend/public/json-example/admin_userlist.json deleted file mode 100644 index a8550716f..000000000 --- a/frontend/public/json-example/admin_userlist.json +++ /dev/null @@ -1,52 +0,0 @@ -{"state":"success", "admin0userlist": - [ - { - "created": 1578688666, - "disabled": false, - "email": "dervommond@gmail.com", - "email_checked": true, - "first_name": "Max", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Miau", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b", - "role": "admin", - "username": "", - "balance_gdd": 174500, - "balance_gdt": 4500, - "errorCount": 0 - }, - { - "created": 1578685678, - "disabled": false, - "email": "ttwer@gmail.com", - "email_checked": true, - "first_name": "John", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Doe", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71x", - "role": "user", - "username": "", - "balance_gdd": 144500, - "balance_gdt": 0, - "errorCount": 0 - }, - { - "created": 1578635671, - "disabled": false, - "email": "test@gmail.com", - "email_checked": true, - "first_name": "Alice", - "group_alias": "gdd1", - "ident_hash": 4928827813, - "last_name": "Seer", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71a", - "role": "user", - "username": "", - "balance_gdd": 444500, - "balance_gdt": 4500, - "errorCount": 0 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/example.json b/frontend/public/json-example/example.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/public/json-example/userdata.json b/frontend/public/json-example/userdata.json deleted file mode 100644 index 69f5a8041..000000000 --- a/frontend/public/json-example/userdata.json +++ /dev/null @@ -1,19 +0,0 @@ -{"state":"success", "userdata": - [ - { - "created": 1578688666, - "disabled": false, - "email": "dervommond@gmail.com", - "email_checked": true, - "first_name": "Max", - "group_alias": "gdd1", - "ident_hash": 2928827813, - "last_name": "Miau", - "public_hex": "2ed28a1cf5e116d83615406bc577152221c2f774a5656f66a0e7540f7576d71b", - "role": "admin", - "username": "", - "balance": 174500, - "errorCount": 0 - } - ] -} \ No newline at end of file diff --git a/frontend/public/json-example/userparticipation.json b/frontend/public/json-example/userparticipation.json deleted file mode 100644 index 4e29f117b..000000000 --- a/frontend/public/json-example/userparticipation.json +++ /dev/null @@ -1,45 +0,0 @@ -{"state":"success", "participation": - [ - { - "type": "submitted", - "participation_id": 412, - "date_submitted": "9-12-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "8-12-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "in progress", - "participation_id": 312, - "date_submitted": "2-11-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "2-11-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "confirmed", - "participation_id": 212, - "date_submitted": "20-10-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "20-10-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - }, - { - "type": "rejected", - "participation_id": 142, - "date_submitted": "17-9-2020", - "titel": "Lorem Ipsum panta lore es Tastina sero was. ", - "text": "Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. Lorem Ipsum panta lore es Tastina sero was. ", - "date_participation": "17-9-2020", - "plz_participation": "01099", - "pubkey": "abcdefghi123456789" - } - ], - "count": 4 -} \ No newline at end of file diff --git a/frontend/public/json-example/usertransactions.json b/frontend/public/json-example/usertransactions.json deleted file mode 100644 index a1af47db8..000000000 --- a/frontend/public/json-example/usertransactions.json +++ /dev/null @@ -1,61 +0,0 @@ -{"state":"success", "transactions": - [ - { - "name": "Jon Tester", - "email": "jon@example.de", - "type": "send", - "transaction_id": 12, - "date": "29-11-2020", - "balance": 7000, - "memo": "Reperatur Waschbecken", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 11, - "date": "1-11-2020", - "balance": 10000, - "memo": "Müll gesammelt im Wald", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Maria Tester", - "email": "maria@example.de", - "type": "receive", - "transaction_id": 7, - "date": "23-10-2020", - "balance": 5000, - "memo": "Spende an Alice ", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Alice Tester", - "email": "alice@example.de", - "type": "receive", - "transaction_id": 5, - "date": "2-8-2020", - "balance": 1000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - }, - { - "name": "Gradido Community", - "email": "gradido@example.de", - "type": "creation", - "transaction_id": 1, - "date": "11-7-2020", - "balance": 10000, - "memo": "Bob hat meinen Müll getrennt", - "pubkey": "abcdefghi123456789" - - } - ], - "transactionExecutingCount": 8750, - "count": 5 -} \ No newline at end of file diff --git a/frontend/scripts/missing-keys.sh b/frontend/scripts/missing-keys.sh deleted file mode 100755 index 6dbaee897..000000000 --- a/frontend/scripts/missing-keys.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -ROOT_DIR=$(dirname "$0")/.. - -sorting="jq -f $ROOT_DIR/scripts/sort_filter.jq" -english="$sorting $ROOT_DIR/src/locales/en.json" -german="$sorting $ROOT_DIR/src/locales/de.json" -listPaths="jq -c 'path(..)|[.[]|tostring]|join(\".\")'" -diffString="<( $english | $listPaths ) <( $german | $listPaths )" -if eval "diff -q $diffString"; -then - : # all good -else - eval "diff -y $diffString | grep '[|<>]'"; - printf "\nEnglish and German translation keys do not match, see diff above.\n" - exit 1 -fi diff --git a/admin/scripts/missing-keys.sh b/frontend/scripts/missing-keys.sh.old similarity index 100% rename from admin/scripts/missing-keys.sh rename to frontend/scripts/missing-keys.sh.old diff --git a/frontend/src/App.vue b/frontend/src/App.vue index b14f5c8a2..d7945ec69 100755 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -10,8 +10,8 @@ + diff --git a/frontend/src/assets/scss/custom/_variables.scss b/frontend/src/assets/scss/custom/_variables.scss index 8d698702c..a17320d1d 100644 --- a/frontend/src/assets/scss/custom/_variables.scss +++ b/frontend/src/assets/scss/custom/_variables.scss @@ -1,10 +1,8 @@ - -// // Variables -// // Global settings @import "gradido-custom/global"; + // $enable-caret: true !default; // $enable-rounded: true !default; // $enable-shadows: true !default; @@ -13,9 +11,9 @@ // $enable-grid-classes: true !default; // $enable-print-styles: true !default; - // Color system @import "gradido-custom/color"; + // $white: #fff !default; // $gray-100: #f6f9fc !default; // $gray-200: #e9ecef !default; @@ -27,7 +25,7 @@ // $gray-800: #32325d !default; // Line heading color // $gray-900: #212529 !default; // $black: #000 !default; -// + // $grays: () !default; // $grays: map-merge(( // "100": $gray-100, @@ -40,7 +38,7 @@ // "800": $gray-800, // "900": $gray-900 // ), $grays); -// + // $blue: #5e72e4 !default; // $indigo: #5603ad !default; // $purple: #8965e0 !default; @@ -51,7 +49,7 @@ // $green: #2dce89 !default; // $teal: #11cdef !default; // $cyan: #2bffc6 !default; -// + // $colors: () !default; // $colors: map-merge(( // "blue": $blue, @@ -70,7 +68,7 @@ // "lighter": $gray-200, // "gray-dark": $gray-800 // ), $colors); -// + // $default: #172b4d !default; // $primary: #5e72e4 !default; // $secondary: #f7fafc !default; @@ -81,7 +79,7 @@ // $light: $gray-500 !default; // $dark: $gray-900 !default; // $darker: darken($gray-900, 15%) !default; -// + // $facebook: #3b5999 !default; // $twitter: #1da1f2 !default; // $google-plus: #dd4b39 !default; @@ -91,7 +89,7 @@ // $slack: #3aaf85 !default; // $dribbble: #ea4c89 !default; // $github: #222222 !default; -// + // $theme-colors: () !default; // $theme-colors: map-merge(( // "default": $default, @@ -106,7 +104,7 @@ // "dark": $dark, // "darker": $darker // ), $theme-colors); -// + // $brand-colors: () !default; // $brand-colors: map-merge(( // "facebook": $facebook, @@ -119,7 +117,7 @@ // "dribbble": $dribbble, // "github": $github // ), $brand-colors); -// + // $shape-colors: () !default; // $shape-colors: map-merge(( // "default": #32325d, @@ -127,9 +125,9 @@ // "secondary": #24b47e, // "neutral": #e9ecef, // "blue-gray": #b2cbe1, -// + // ), $shape-colors); -// + // $shapes-primary-colors: () !default; // $shapes-primary-colors: map-merge(( // "step-1-gradient-bg": #281483, @@ -141,7 +139,7 @@ // "span-4-bg": #25ddf5, // "span-5-bg": #1fa2ff // ), $shapes-primary-colors); -// + // $shapes-default-colors: () !default; // $shapes-default-colors: map-merge(( // "step-1-gradient-bg": #7795f8, @@ -153,7 +151,7 @@ // "span-4-bg": #76eea7, // "span-5-bg": #6adaff // ), $shapes-default-colors); -// + // $shapes-light-colors: () !default; // $shapes-light-colors: map-merge(( // "step-1-gradient-bg": #b2cbe1, @@ -165,7 +163,7 @@ // "span-4-bg": #74e4a2, // "span-5-bg": #008169 // ), $shapes-light-colors); -// + // $shapes-dark-colors: () !default; // $shapes-dark-colors: map-merge(( // "step-1-gradient-bg": #32325d, @@ -177,30 +175,29 @@ // "span-4-bg": #d782d9, // "span-5-bg": #008169 // ), $shapes-dark-colors); -// -// + // // Translucent color variations // $translucent-color-opacity: .6 !default; -// + // // Set a specific jump point for requesting color jumps // $theme-color-interval: 8% !default; -// -// // The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255. + +// // The yiq lightness value that determines when the lightness of color changes +// from "dark" to "light". Acceptable values are between 0 and 255. // $yiq-contrasted-threshold: 200 !default; -// + // // Customize the light and dark text colors for use in our YIQ color contrast function. // $yiq-text-dark: $gray-900 !default; // $yiq-text-light: $white !default; -// + // // Action colors -// + // $star-rating-color: $gray-400 !default; // $star-rating-color-active: $yellow !default; -// + // $favorite-color: $yellow !default; // $like-color: $blue !default; // $love-color: $red !default; -// // Body @import "gradido-custom/body"; @@ -208,10 +205,9 @@ // $body-bg: #f8f9fe !default; // $body-color: $gray-700 !default; - - // Sections @import "gradido-custom/sections"; + // $section-colors: () !default; // $section-colors: map-merge(( // "primary": $body-bg, @@ -223,14 +219,15 @@ // Links @import "gradido-custom/links"; + // $link-color: $primary !default; // $link-decoration: none !default; // $link-hover-color: darken($link-color, 15%) !default; // $link-hover-decoration: none !default; - // Grid breakpoints @import "gradido-custom/grid-breakpoint"; + // $grid-breakpoints: ( // xs: 0, // sm: 576px, @@ -238,19 +235,17 @@ // lg: 992px, // xl: 1200px // ); -// + // @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); // @include _assert-starts-at-zero($grid-breakpoints); -// -// + // // Grid containers // // // // Define the maximum width of `.container` for different screen sizes. - - // Spacing @import "gradido-custom/spacing"; + // $spacer: 1rem !default; // $spacers: () !default; // $spacers: map-merge(( @@ -274,10 +269,9 @@ // 8: ($spacer * 8), // 9: ($spacer * 10) // ), $spacers); -// -// + // // This variable affects the `.h-*` and `.w-*` classes. -// + // $sizes: () !default; // $sizes: map-merge(( // 25: 25%, @@ -288,406 +282,406 @@ // Components @import "gradido-custom/components"; + // $shape-height-xl: 1.5 !default; // $shape-height-lg: 1.5 !default; // $shape-height-sm: 1.5 !default; -// + // $border-width: 1px !default; // $border-color: $gray-200 !default; -// + // $border-radius: .375rem !default; // $border-radius-xl: .5rem !default; // $border-radius-lg: .4375rem !default; // $border-radius-sm: .25rem !default; -// + // $box-shadow-sm: 0 0 .5rem rgba($gray-600, .075) !default; // $box-shadow: 0 0 2rem 0 rgba($gray-600, .15) !default; // $box-shadow-lg: 0 0 3rem rgba($gray-600, .175) !default; -// + // $component-active-color: $white !default; // $component-active-bg: theme-color("primary") !default; // $component-active-border-color: theme-color("primary") !default; -// + // $component-hover-color: $gray-300 !default; // $component-hover-bg: $gray-300 !default; // $component-hover-border-color: $gray-300 !default; -// + // $caret-width: .3em !default; // $transition-base: all .15s ease !default; // $transition-fade: opacity .15s linear !default; // $transition-collapse: height .35s ease !default; - // Fonts @import "gradido-custom/fonts"; + // $font-family-sans-serif: Open Sans, sans-serif !default; // $font-family-base: $font-family-sans-serif !default; -// + // $font-size-base: 1rem !default; // Assumes the browser default, typically `16px` // $font-size-xl: ($font-size-base * 1.5); // $font-size-lg: ($font-size-base * 1.25); // $font-size-sm: ($font-size-base * .875); // $font-size-xs: ($font-size-base * .75); -// + // $font-weight-light: 300 !default; // $font-weight-normal: 400 !default; // $font-weight-bold: 600 !default; // $font-weight-extra-bold: 700 !default; -// + // $font-weight-base: $font-weight-normal !default; // $line-height-base: 1.5 !default; // $shape-height-base: 1.5 !default; -// -// + // $h1-font-size: $font-size-base * 1.625 !default; // $h2-font-size: $font-size-base * 1.25 !default; // $h3-font-size: $font-size-base * 1.0625 !default; // $h4-font-size: $font-size-base * .9375 !default; // $h5-font-size: $font-size-base * .8125 !default; // $h6-font-size: $font-size-base * .625 !default; -// -// + // $headings-margin-bottom: ($spacer / 2); // $headings-font-family: inherit !default; // $headings-font-weight: $font-weight-bold !default; // $headings-line-height: 1.5 !default; // $headings-color: $gray-800 !default; -// + // $heading-letter-spacing: .025em !default; // $heading-font-size: .95rem !default; // $heading-text-transform: uppercase !default; // $heading-font-weight: $headings-font-weight !default; -// + // $heading-title-letter-spacing: .025em !default; // $heading-title-font-size: 1.375rem !default; // $heading-title-font-weight: $font-weight-bold !default; // $heading-title-text-transform: uppercase !default; -// + // $heading-section-letter-spacing: .025em !default; // $heading-section-font-size: 1.375rem !default; // $heading-section-font-weight: $font-weight-bold !default; // $heading-section-text-transform: uppercase !default; -// + // $display1-size: 3.3rem !default; // $display2-size: 2.75rem !default; // $display3-size: 2.1875rem !default; // $display4-size: 1.6275rem !default; -// + // $display1-weight: $font-weight-bold !default; // $display2-weight: $font-weight-bold !default; // $display3-weight: $font-weight-bold !default; // $display4-weight: $font-weight-bold !default; // $display-line-height: $headings-line-height !default; -// + // $paragraph-font-size: 1rem !default; // $paragraph-font-weight: 300 !default; // $paragraph-line-height: 1.7 !default; -// + // $lead-font-size: ($paragraph-font-size * 1.25) !default; // $lead-font-weight: 300 !default; -// + // $small-font-size: 80% !default; -// + // $text-muted: $gray-600 !default; -// + // $blockquote-small-color: $gray-600 !default; // $blockquote-font-size: ($font-size-base * 1.25) !default; -// + // $hr-border-color: rgba($black, .1); // $hr-border-width: $border-width !default; -// -// $mark-padding: .2em !default; -// -// $dt-font-weight: $font-weight-bold !default; -// -// $list-inline-padding: .5rem !default; -// -// $mark-bg: #fcf8e3 !default; -// -// $hr-margin-y: $spacer * 2 !default; +// $mark-padding: .2em !default; + +// $dt-font-weight: $font-weight-bold !default; + +// $list-inline-padding: .5rem !default; + +// $mark-bg: #fcf8e3 !default; + +// $hr-margin-y: $spacer * 2 !default; // Icons @import "gradido-custom/icons"; + // $icon-size: 3rem !default; // $icon-size-xl: 5rem !default; // $icon-size-lg: 4rem !default; // $icon-size-sm: 2rem !default; - // Tables @import "gradido-custom/tables"; + // $table-cell-padding: 1rem !default; // $table-cell-padding-sm: .5rem !default; -// + // $table-bg: transparent !default; // $table-accent-bg: rgba($black, .05) !default; // $table-hover-bg: $gray-100 !default; // $table-active-bg: $table-hover-bg !default; -// + // $table-border-width: $border-width !default; // $table-border-color: $gray-200 !default; -// + // $table-head-bg: $gray-100 !default; // $table-head-color: $gray-600 !default; // $table-head-spacer-y: .75rem !default; -// + // $table-head-font-size: .65rem !default; // $table-head-font-weight: $font-weight-bold !default; // $table-head-text-transform: uppercase !default; // $table-head-letter-spacing: 1px !default; -// + // $table-body-font-size: .8125rem !default; -// + // $table-dark-bg: theme-color("default") !default; // $table-dark-accent-bg: rgba($white, .05) !default; // $table-dark-hover-bg: rgba($white, .075) !default; // $table-dark-border-color: lighten(theme-color("default"), 7%) !default; // $table-dark-color: $body-bg !default; -// + // $table-dark-head-bg: lighten(theme-color("default"), 4%) !default; // $table-dark-head-color: lighten(theme-color("default"), 35%) !default; // Buttons + Forms @import "gradido-custom/buttons-forms"; + // $input-btn-padding-y: .625rem !default; // $input-btn-padding-x: .75rem !default; // $input-btn-line-height: $shape-height-base !default; -// + // $input-btn-focus-width: 0 !default; // $input-btn-focus-color: rgba($component-active-bg, 1) !default; // $input-btn-focus-box-shadow: none !default; -// + // $input-btn-padding-y-sm: .25rem !default; // $input-btn-padding-x-sm: .5rem !default; // $input-btn-line-height-sm: $shape-height-sm !default; -// + // $input-btn-padding-y-lg: .875rem !default; // $input-btn-padding-x-lg: 1rem !default; // $input-btn-line-height-lg: $shape-height-lg !default; -// + // $input-btn-border-width: 1px !default; -// + // $input-btn-font-size-sm: .75rem !default; // $input-btn-font-size: .875rem !default; // $input-btn-font-size-lg: .875rem !default; - // Buttons @import "gradido-custom/buttons"; + // $btn-padding-y: $input-btn-padding-y !default; // $btn-padding-x: $input-btn-padding-x + 0.5 !default; // $btn-line-height: $input-btn-line-height !default; -// + // $btn-padding-y-sm: $input-btn-padding-y-sm !default; // $btn-padding-x-sm: $input-btn-padding-x-sm !default; // $btn-line-height-sm: $input-btn-line-height-sm !default; -// + // $btn-padding-y-lg: $input-btn-padding-y-lg !default; // $btn-padding-x-lg: $input-btn-padding-x-lg !default; // $btn-line-height-lg: $input-btn-line-height-lg !default; -// + // $btn-border-width: $input-btn-border-width !default; -// + // $btn-font-weight: 600 !default; -// + // $btn-text-transform: none !default; -// -// + // $btn-letter-spacing: .025em !default; // $btn-box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08) !default; // $btn-hover-box-shadow: 0 7px 14px rgba(50,50,93,.1), 0 3px 6px rgba(0,0,0,.08) !default; // $btn-focus-box-shadow: $btn-hover-box-shadow !default; // $btn-focus-width: $input-btn-focus-width !default; // $btn-active-box-shadow: none !default; -// -// $btn-hover-translate-y: -1px !default; +// $btn-hover-translate-y: -1px !default; // Input @import "gradido-custom/input"; + // $input-padding-y: $input-btn-padding-y !default; // $input-padding-x: $input-btn-padding-x !default; // $input-line-height: $input-btn-line-height !default; -// + // $input-padding-y-sm: $input-btn-padding-y-sm !default; // $input-padding-x-sm: $input-btn-padding-x-sm !default; // $input-line-height-sm: $input-btn-line-height-sm !default; -// + // $input-padding-y-lg: $input-btn-padding-y-lg !default; // $input-padding-x-lg: $input-btn-padding-x-lg !default; // $input-line-height-lg: $input-btn-line-height-lg !default; -// + // $input-border-radius: $border-radius !default; // $input-border-radius-xl: $border-radius-xl !default; // $input-border-radius-lg: $border-radius-lg !default; // $input-border-radius-sm: $border-radius-sm !default; -// + // $input-bg: $white !default; // $input-disabled-bg: $gray-200 !default; -// + // $input-muted-bg: #F7FAFE !default; // $input-focus-muted-bg: lighten($input-muted-bg, 1%) !default; // $input-alternative-box-shadow: 0 1px 3px rgba(50,50,93,.15), 0 1px 0 rgba(0,0,0,.02) !default; // $input-focus-alternative-box-shadow: 0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08) !default; -// + // $input-color: $gray-600 !default; // $input-border-color: #cad1d7 !default; // $input-border-width: $input-btn-border-width !default; // $input-box-shadow: none !default; // 0 1px 3px 0 $gray-400 !default; -// + // $input-focus-bg: $white !default; // $input-focus-border-color: rgba(50,151,211,.25) !default; // $input-focus-color: $input-color !default; // $input-focus-width: 0 !default; // $input-focus-box-shadow: none !default; //0 1px 3px 0 $gray-500 !default; -// + // $input-placeholder-color: $gray-500 !default; // $input-focus-placeholder-color: $gray-500 !default; -// -// $input-height-border: $input-border-width * 2 !default; -// -// $input-transition: all .2s cubic-bezier(0.68, -0.55, 0.265, 1.55) !default; +// $input-height-border: $input-border-width * 2 !default; + +// $input-transition: all .2s cubic-bezier(0.68, -0.55, 0.265, 1.55) !default; // Input groups @import "gradido-custom/input-groups"; + // $input-group-addon-color: $input-placeholder-color !default; // $input-group-addon-bg: $input-bg !default; // $input-group-addon-border-color: $input-border-color !default; -// + // $input-group-addon-focus-color: $input-focus-color !default; // $input-group-addon-focus-bg: $input-focus-bg !default; // $input-group-addon-focus-border-color: $input-focus-border-color !default; - // Form group @import "gradido-custom/form-groups"; -// $form-group-margin-bottom: 1.5rem !default; +// $form-group-margin-bottom: 1.5rem !default; // Custom forms @import "gradido-custom/custom-forms"; + // custom-control-gutter: 1.75rem !default; // custom-control-spacer-x: 1rem !default; // custom-control-indicator-size: 1.25rem !default; -// + // custom-control-indicator-bg: $input-bg !default; // custom-control-indicator-border-width: 1px !default; // custom-control-indicator-border-color: $input-border-color !default; // custom-control-indicator-box-shadow: none !default; -// + // custom-control-indicator-focus-box-shadow: $custom-control-indicator-box-shadow !default; -// + // custom-control-indicator-hover-color: $component-hover-color !default; // custom-control-indicator-hover-bg: $component-hover-bg !default; // custom-control-indicator-hover-border-color: $component-hover-border-color !default; -// + // custom-control-indicator-active-color: $component-active-color !default; // custom-control-indicator-active-bg: $component-active-bg !default; // custom-control-indicator-active-border-color: $component-active-border-color !default; // custom-control-indicator-active-box-shadow: $custom-control-indicator-box-shadow !default; -// + // custom-control-indicator-checked-color: $component-active-color !default; // custom-control-indicator-checked-bg: $component-active-bg !default; // custom-control-indicator-checked-border-color: $component-active-border-color !default; // custom-control-indicator-checked-box-shadow: $custom-control-indicator-box-shadow !default; // custom-control-indicator-checked-disabled-bg: rgba(theme-color("primary"), .5) !default; -// + // custom-control-indicator-disabled-bg: $gray-200 !default; // custom-control-label-disabled-color: $gray-600 !default; -// + // custom-checkbox-indicator-border-radius: $border-radius-sm !default; // //$custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml !default;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default; // Custom toggle @import "gradido-custom/custom-toogle"; + // $custom-toggle-width: 50px !default; // $custom-toggle-slider-bg: $gray-200 !default; // $custom-toggle-checked-bg: theme-color("primary") !default; // Form validation @import "gradido-custom/form-validation"; + // $form-feedback-valid-bg: lighten($success, 15%) !default; // $form-feedback-valid-color: theme-color("success") !default; // $form-feedback-invalid-bg: lighten($warning, 15%) !default; // $form-feedback-invalid-color: theme-color("warning") !default; - // Allows for customizing button radius independently from global border radius -$btn-border-radius: $input-border-radius !default; -$btn-border-radius-xl: $input-border-radius-xl !default; -$btn-border-radius-lg: $input-border-radius-lg !default; -$btn-border-radius-sm: $input-border-radius !default; - +$btn-border-radius: $input-border-radius !default; +$btn-border-radius-xl: $input-border-radius-xl !default; +$btn-border-radius-lg: $input-border-radius-lg !default; +$btn-border-radius-sm: $input-border-radius !default; // Chart @import "gradido-custom/chart"; + // $chart-height: 350px !default; // $chart-height-sm: 230px !default; -// + // $chart-legend-margin-top: 2.5rem !default; // $chart-legend-font-size: $font-size-sm !default; // $chart-legend-color: $text-muted !default; // $chart-legend-height: $chart-legend-margin-top + $chart-legend-font-size * $line-height-base !default; - - - // Map @import "gradido-custom/map"; -// $map-height: 500px !default; +// $map-height: 500px !default; // No UI Slider @import "gradido-custom/nouislider"; + // $noui-target-bg: #eceeef !default; // $noui-target-thickness: 5px !default; // $noui-target-border-radius: 5px !default; // $noui-target-border-color: 0 !default; // $noui-target-box-shadow: inset 0 1px 2px rgba(90,97,105,.1) !default; -// + // $noui-slider-connect-bg: $primary !default; // $noui-slider-connect-disabled-bg: #b2b2b2 !default; -// + // $noui-handle-width: 15px !default; // $noui-handle-bg: theme-color("primary") !default; // $noui-handle-border: 0 !default; // $noui-handle-border-radius: 100% !default; -// -// $noui-origin-border-radius: 2px !default; +// $noui-origin-border-radius: 2px !default; // Dropdown @import "gradido-custom/dropdown"; + // $dropdown-bg: $white !default; // $dropdown-border-width: 0 !default; // $dropdown-border-color: rgba($black, .15) !default; // $dropdown-border-radius: $border-radius-lg !default; -// $dropdown-box-shadow: 0 50px 100px rgba(50, 50, 93, .1), 0 15px 35px rgba(50, 50, 93, .15), 0 5px 15px rgba(0, 0, 0, .1) !default; - +// $dropdown-box-shadow: 0 50px 100px rgba(50, 50, 93, .1), +// 0 15px 35px rgba(50, 50, 93, .15), +// 0 5px 15px rgba(0, 0, 0, .1) !default; // Navs @import "gradido-custom/navs"; + // $nav-link-padding-y: .25rem !default; // $nav-link-padding-x: .75rem !default; // $nav-link-color: $gray-700 !default; // $nav-link-hover-color: theme-color("primary") !default; // $nav-link-disabled-color: $gray-600 !default; -// + // $nav-pills-padding-y: .75rem !default; // $nav-pills-padding-x: 1rem !default; -// + // $nav-pills-space-x: 1rem !default; -// + // $nav-pills-bg: $white !default; // $nav-pills-border-width: 1px !default; // $nav-pills-border-color: theme-color("primary") !default; // $nav-pills-border-radius: $border-radius !default; -// + // $nav-pills-link-color: theme-color("primary") !default; // $nav-pills-link-hover-color: darken(theme-color("primary"), 5%) !default; // $nav-pills-link-active-color: color-yiq(theme-color("primary")) !default; @@ -696,25 +690,26 @@ $btn-border-radius-sm: $input-border-radius !default; // Navbar @import "gradido-custom/navbar"; + // $navbar-transition: all .15s linear !default; // $navbar-padding-y: 1rem !default; // $navbar-padding-x: 1rem !default; -// + // $navbar-nav-link-padding-x: 1rem !default; // $navbar-nav-link-padding-y: 1rem !default; -// + // $navbar-nav-link-font-size: .9rem !default; // $navbar-nav-link-font-weight: 400 !default; // $navbar-nav-link-text-transform: normal !default; // $navbar-nav-link-letter-spacing: 0 !default; // $navbar-nav-link-border-radius: $border-radius !default; -// + // $navbar-search-width: 270px !default; // $navbar-search-bg: transparent !default; -// + // $navbar-search-border-radius: 2rem !default; // $navbar-search-border-width: 2px !default; -// + // $navbar-dark-bg: transparent !default; // $navbar-dark-hover-bg: rgba(255, 255, 255, .1) !default; // $navbar-dark-active-bg: rgba(255, 255, 255, .1) !default; @@ -723,12 +718,12 @@ $btn-border-radius-sm: $input-border-radius !default; // $navbar-dark-active-color: rgba($white, .65) !default; // $navbar-dark-disabled-color: rgba($white, .25) !default; // $navbar-dark-toggler-border-color: transparent !default; -// + // $navbar-search-dark-border-color: rgba(255, 255, 255, .6) !default; // $navbar-search-dark-color: rgba(255, 255, 255, .6) !default; // $navbar-search-dark-focus-border-color: rgba(255, 255, 255, .9) !default; // $navbar-search-dark-focus-color: rgba(255, 255, 255, .9) !default; -// + // $navbar-light-bg: transparent !default; // $navbar-light-hover-bg: rgba(0, 0, 0, .1) !default; // $navbar-light-active-bg: rgba(0, 0, 0, .1) !default; @@ -738,77 +733,74 @@ $btn-border-radius-sm: $input-border-radius !default; // $navbar-light-active-color: rgba($black, .9) !default; // $navbar-light-disabled-color: rgba($black, .3) !default; // $navbar-light-toggler-border-color: transparent !default; -// + // $navbar-search-light-border-color: rgba(0, 0, 0, .6) !default; // $navbar-search-light-color: rgba(0, 0, 0, .6) !default; // $navbar-search-light-focus-border-color: rgba(0, 0, 0, .9) !default; // $navbar-search-light-focus-color: rgba(0, 0, 0, .9) !default; - - // Vertical navbar @import "gradido-custom/vertical-navbar"; + // $navbar-vertical-box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15) !important; // $navbar-vertical-width: 250px !default; // $navbar-vertical-padding-x: 1.5rem !default; // $navbar-vertical-nav-link-padding-x: 1.5rem !default; // $navbar-vertical-nav-link-padding-y: .65rem !default; -// + // $navbar-icon-min-width: 2.25rem !default; -// + // $navbar-breadcrumb-padding-y: $nav-link-padding-y !default; // $navbar-breadcrumb-padding-x: 0 !default; -// + // $navbar-light-bg: $white !default; // $navbar-light-border-color: $border-color !default; - - - // Main content @import "gradido-custom/main-content"; + // $main-content-padding-y: 40px !default; // $main-content-padding-x: 24px !default; // in px to combine with $grid-gutter-width - // Alerts @import "gradido-custom/alerts"; + // $alert-padding-y: 1rem !default; // $alert-padding-x: 1.5rem !default; // $alert-border-radius: $border-radius !default; -// + // $alert-bg-level: -2 !default; // $alert-border-level: -2 !default; // $alert-color-level: 0 !default; - // List group @import "gradido-custom/list-group"; + // $list-group-bg: $white !default; // $list-group-border-color: $border-color !default; //rgba($black, .125); // $list-group-border-width: $border-width !default; // $list-group-border-radius: $border-radius !default; -// + // $list-group-item-padding-y: 1rem !default; // $list-group-item-padding-x: 1rem !default; -// + // $list-group-hover-bg: $gray-100 !default; // $list-group-active-color: $component-active-color !default; // $list-group-active-bg: $component-active-bg !default; // $list-group-active-border-color: $list-group-active-bg !default; -// + // $list-group-disabled-color: $gray-600 !default; // $list-group-disabled-bg: $list-group-bg !default; -// + // $list-group-action-color: $gray-700 !default; // $list-group-action-hover-color: $list-group-action-color !default; -// + // $list-group-action-active-color: $list-group-action-color !default; // $list-group-action-active-bg: $gray-200 !default; - // Close @import "gradido-custom/close"; + // $close-font-size: $font-size-base * 1.5 !default; // $close-font-weight: $font-weight-bold !default; // $close-bg: transparent !default; @@ -817,9 +809,9 @@ $btn-border-radius-sm: $input-border-radius !default; // $close-hover-color: rgba(0, 0, 0, .9) !default; // $close-text-shadow: none !default; - // Popovers @import "gradido-custom/popovers"; + // $popover-font-size: $font-size-sm !default; // $popover-bg: $white !default; // $popover-max-width: 276px !default; @@ -827,39 +819,40 @@ $btn-border-radius-sm: $input-border-radius !default; // $popover-border-color: rgba($black, .05) !default; // $popover-border-radius: $border-radius-lg !default; // $popover-box-shadow: 0px .5rem 2rem 0px rgba($black, .2) !default; -// + // $popover-header-bg: $popover-bg !default; // $popover-header-color: $headings-color !default; // $popover-header-padding-y: .5rem !default; // $popover-header-padding-x: .95rem !default; -// + // $popover-body-color: $body-color !default; // $popover-body-padding-y: $popover-header-padding-y !default; // $popover-body-padding-x: $popover-header-padding-x !default; -// + // $popover-arrow-width: 1.5rem !default; // $popover-arrow-height: .75rem !default; // $popover-arrow-color: $popover-bg !default; -// -// $popover-arrow-outer-color: transparent !default; +// $popover-arrow-outer-color: transparent !default; // Badges @import "gradido-custom/badges"; + // $badge-font-size: 66% !default; // $badge-font-weight: $font-weight-bold !default; // $badge-padding-y: .35rem !default; // $badge-padding-x: .375rem !default; // $badge-border-radius: $border-radius !default; // $badge-text-transfom: uppercase; -// + // $badge-pill-padding-x: .875em !default; // $badge-pill-border-radius: 10rem !default; -// + // $badge-circle-size: 2rem !default; // Pagination @import "gradido-custom/pagination"; + // // $pagination-padding-y: .5rem !default; // // $pagination-padding-x: .75rem !default; // // $pagination-padding-y-sm: .25rem !default; @@ -867,30 +860,30 @@ $btn-border-radius-sm: $input-border-radius !default; // // $pagination-padding-y-lg: .75rem !default; // // $pagination-padding-x-lg: 1.5rem !default; // // $pagination-line-height: 1.25 !default; -// + // $pagination-color: $gray-600 !default; // $pagination-bg: $white !default; // $pagination-border-width: $border-width !default; // $pagination-border-color: $gray-300 !default; -// + // //$pagination-focus-box-shadow: $btn-hover-box-shadow !default; -// + // $pagination-hover-color: $gray-600 !default; // $pagination-hover-bg: $gray-300 !default; // $pagination-hover-border-color: $gray-300 !default; -// + // $pagination-active-color: $component-active-color !default; // $pagination-active-bg: $component-active-bg !default; // $pagination-active-border-color: $pagination-active-bg !default; // $pagination-active-box-shadow: $btn-hover-box-shadow !default; -// + // $pagination-disabled-color: $gray-600 !default; // $pagination-disabled-bg: $white !default; // $pagination-disabled-border-color: $gray-300 !default; - // Cards @import "gradido-custom/cards"; + // $card-spacer-y: 1.25rem !default; // $card-spacer-x: 1.5rem !default; // $card-border-width: $border-width !default; @@ -899,12 +892,12 @@ $btn-border-radius-sm: $input-border-radius !default; // $card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default; // $card-cap-bg: $white !default; // $card-bg: $white !default; -// + // $card-img-overlay-padding: 1.25rem !default; -// + // //$card-group-margin: ($grid-gutter-width / 2); // //$card-deck-margin: $card-group-margin !default; -// + // $card-columns-count: 3 !default; // $card-columns-gap: 1.25rem !default; // $card-columns-margin: $card-spacer-y !default; @@ -915,21 +908,22 @@ $btn-border-radius-sm: $input-border-radius !default; // Modals @import "gradido-custom/modals"; + // $modal-inner-padding: 1.5rem !default; -// + // $modal-lg: 800px !default; // $modal-md: 500px !default; // $modal-sm: 380px !default; -// + // $modal-title-line-height: 1.1; -// + // $modal-content-bg: $white !default; // $modal-content-border-color: rgba($black, .2) !default; // $modal-content-border-width: 0 !default; // $modal-content-border-radius: $border-radius-lg !default; // $modal-content-box-shadow-xs: 0 15px 35px rgba(50,50,93,.2), 0 5px 15px rgba(0,0,0,.17); // $modal-content-box-shadow-sm-up: 0 15px 35px rgba(50,50,93,.2), 0 5px 15px rgba(0,0,0,.17); -// + // $modal-backdrop-bg: $black !default; // $modal-backdrop-opacity: .16 !default; // $modal-header-border-color: $gray-200 !default; @@ -940,41 +934,40 @@ $btn-border-radius-sm: $input-border-radius !default; // Datepicker @import "gradido-custom/datepicker"; + // $datepicker-border-radius: $card-border-radius !default; // $datepicker-dropdown-padding: 20px 22px !default; -// + // $datepicker-cell-transition: $transition-base !default; // $datepicker-cell-hover-background: lighten($gray-400, 55%); // $datepicker-cell-border-radius: 50% !default; // $datepicker-cell-width: 36px !default; // $datepicker-cell-height: 36px !default; -// + // $datepicker-disabled-cell-color: $gray-300 !default; // $datepicker-disabled-old-new-color: $gray-500 !default; -// + // $datepicker-header-cell-border-radius: $border-radius !default; -// + // $datepicker-active-color: $white !default; // $datepicker-active-background: theme-color("primary") !default; // $datepicker-active-box-shadow: none !default; -// + // $datepicker-range-background: theme-color("primary") !default; // $datepicker-range-cell-focused-background: darken($datepicker-range-background, 5%); // $datepicker-range-color: $white !default; // $datepicker-range-highlighted-bg: $gray-200 !default; -// + // $datepicker-dropdown-border: lighten($gray-400, 40%); // $datepicker-dropdown-bg: $white !default; // $datepicker-highlighted-bg: $datepicker-active-background !default; - // Footer @import "gradido-custom/footer"; // $footer-padding-y: 2.5rem; // $footer-padding-x: 0; -// -// + // $footer-link-font-size: .85rem !default; // $footer-bg: theme-color("secondary") !default; // $footer-color: $gray-600 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_alerts.scss b/frontend/src/assets/scss/custom/gradido-custom/_alerts.scss index ce2659c33..a5d3824cf 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_alerts.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_alerts.scss @@ -3,8 +3,6 @@ $alert-padding-y: 1rem !default; $alert-padding-x: 1.5rem !default; $alert-border-radius: $border-radius !default; - $alert-bg-level: -2 !default; $alert-border-level: -2 !default; $alert-color-level: 0 !default; - diff --git a/frontend/src/assets/scss/custom/gradido-custom/_badges.scss b/frontend/src/assets/scss/custom/gradido-custom/_badges.scss index 6c2f64fb1..3796a34be 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_badges.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_badges.scss @@ -1,14 +1,11 @@ - // Badges -$badge-font-size: 66% !default; -$badge-font-weight: $font-weight-bold !default; -$badge-padding-y: .35rem !default; -$badge-padding-x: .375rem !default; -$badge-border-radius: $border-radius !default; -$badge-text-transfom: uppercase; - -$badge-pill-padding-x: .875em !default; -$badge-pill-border-radius: 10rem !default; - -$badge-circle-size: 2rem !default; +$badge-font-size: 66% !default; +$badge-font-weight: $font-weight-bold !default; +$badge-padding-y: 0.35rem !default; +$badge-padding-x: 0.375rem !default; +$badge-border-radius: $border-radius !default; +$badge-text-transfom: uppercase; +$badge-pill-padding-x: 0.875em !default; +$badge-pill-border-radius: 10rem !default; +$badge-circle-size: 2rem !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_body.scss b/frontend/src/assets/scss/custom/gradido-custom/_body.scss index 02f0f4e52..df8f91f5b 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_body.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_body.scss @@ -1,5 +1,4 @@ // Body - $body-bg: #f8f9fe !default; $body-color: $gray-700 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_buttons-forms.scss b/frontend/src/assets/scss/custom/gradido-custom/_buttons-forms.scss index 5b33bdbfb..92992c070 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_buttons-forms.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_buttons-forms.scss @@ -1,23 +1,18 @@ // Buttons + Forms -$input-btn-padding-y: .625rem !default; -$input-btn-padding-x: .75rem !default; -$input-btn-line-height: $shape-height-base !default; - -$input-btn-focus-width: 0 !default; -$input-btn-focus-color: rgba($component-active-bg, 1) !default; -$input-btn-focus-box-shadow: none !default; - -$input-btn-padding-y-sm: .25rem !default; -$input-btn-padding-x-sm: .5rem !default; -$input-btn-line-height-sm: $shape-height-sm !default; - -$input-btn-padding-y-lg: .875rem !default; -$input-btn-padding-x-lg: 1rem !default; -$input-btn-line-height-lg: $shape-height-lg !default; - -$input-btn-border-width: 1px !default; - -$input-btn-font-size-sm: .75rem !default; -$input-btn-font-size: .875rem !default; -$input-btn-font-size-lg: .875rem !default; +$input-btn-padding-y: 0.625rem !default; +$input-btn-padding-x: 0.75rem !default; +$input-btn-line-height: $shape-height-base !default; +$input-btn-focus-width: 0 !default; +$input-btn-focus-color: rgba($component-active-bg, 1) !default; +$input-btn-focus-box-shadow: none !default; +$input-btn-padding-y-sm: 0.25rem !default; +$input-btn-padding-x-sm: 0.5rem !default; +$input-btn-line-height-sm: $shape-height-sm !default; +$input-btn-padding-y-lg: 0.875rem !default; +$input-btn-padding-x-lg: 1rem !default; +$input-btn-line-height-lg: $shape-height-lg !default; +$input-btn-border-width: 1px !default; +$input-btn-font-size-sm: 0.75rem !default; +$input-btn-font-size: 0.875rem !default; +$input-btn-font-size-lg: 0.875rem !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_buttons.scss b/frontend/src/assets/scss/custom/gradido-custom/_buttons.scss index 5a2e49e3e..3d16e762d 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_buttons.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_buttons.scss @@ -1,35 +1,25 @@ // Buttons -$btn-padding-y: $input-btn-padding-y !default; -$btn-padding-x: $input-btn-padding-x + 0.5 !default; -$btn-line-height: $input-btn-line-height !default; - -$btn-padding-y-sm: $input-btn-padding-y-sm !default; -$btn-padding-x-sm: $input-btn-padding-x-sm !default; -$btn-line-height-sm: $input-btn-line-height-sm !default; - -$btn-padding-y-lg: $input-btn-padding-y-lg !default; -$btn-padding-x-lg: $input-btn-padding-x-lg !default; -$btn-line-height-lg: $input-btn-line-height-lg !default; - -$btn-border-width: $input-btn-border-width !default; - -$btn-font-weight: 600 !default; - -$btn-text-transform: none !default; - - -$btn-letter-spacing: .025em !default; -$btn-box-shadow: 0 4px 6px rgba(50,50,93,.11), 0 1px 3px rgba(0,0,0,.08) !default; -$btn-hover-box-shadow: 0 7px 14px rgba(50,50,93,.1), 0 3px 6px rgba(0,0,0,.08) !default; -$btn-focus-box-shadow: $btn-hover-box-shadow !default; -$btn-focus-width: $input-btn-focus-width !default; -$btn-active-box-shadow: none !default; - -$btn-hover-translate-y: -1px !default; - - +$btn-padding-y: $input-btn-padding-y !default; +$btn-padding-x: $input-btn-padding-x + 0.5 !default; +$btn-line-height: $input-btn-line-height !default; +$btn-padding-y-sm: $input-btn-padding-y-sm !default; +$btn-padding-x-sm: $input-btn-padding-x-sm !default; +$btn-line-height-sm: $input-btn-line-height-sm !default; +$btn-padding-y-lg: $input-btn-padding-y-lg !default; +$btn-padding-x-lg: $input-btn-padding-x-lg !default; +$btn-line-height-lg: $input-btn-line-height-lg !default; +$btn-border-width: $input-btn-border-width !default; +$btn-font-weight: 600 !default; +$btn-text-transform: none !default; +$btn-letter-spacing: 0.025em !default; +$btn-box-shadow: 0 4px 6px rgb(50 50 93 / 11%), 0 1px 3px rgb(0 0 0 / 8%) !default; +$btn-hover-box-shadow: 0 7px 14px rgb(50 50 93 / 10%), 0 3px 6px rgb(0 0 0 / 8%) !default; +$btn-focus-box-shadow: $btn-hover-box-shadow !default; +$btn-focus-width: $input-btn-focus-width !default; +$btn-active-box-shadow: none !default; +$btn-hover-translate-y: -1px !default; .btn-outline-secondary { - color: #4385b1 !important; - } + color: #4385b1 !important; +} diff --git a/frontend/src/assets/scss/custom/gradido-custom/_cards.scss b/frontend/src/assets/scss/custom/gradido-custom/_cards.scss index 566031a26..fe112f8b7 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_cards.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_cards.scss @@ -1,19 +1,18 @@ // Cards -$card-spacer-y: 1.25rem !default; -$card-spacer-x: 1.5rem !default; -$card-border-width: $border-width !default; -$card-border-radius: $border-radius !default; -$card-border-color: rgba($black, .05) !default; -$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default; -$card-cap-bg: $white !default; -$card-bg: $white !default; +$card-spacer-y: 1.25rem !default; +$card-spacer-x: 1.5rem !default; +$card-border-width: $border-width !default; +$card-border-radius: $border-radius !default; +$card-border-color: rgba($black, 0.05) !default; +$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width}) !default; +$card-cap-bg: $white !default; +$card-bg: $white !default; +$card-img-overlay-padding: 1.25rem !default; -$card-img-overlay-padding: 1.25rem !default; +// $card-group-margin: ($grid-gutter-width / 2); +// $card-deck-margin: $card-group-margin !default; -//$card-group-margin: ($grid-gutter-width / 2); -//$card-deck-margin: $card-group-margin !default; - -$card-columns-count: 3 !default; -$card-columns-gap: 1.25rem !default; -$card-columns-margin: $card-spacer-y !default; +$card-columns-count: 3 !default; +$card-columns-gap: 1.25rem !default; +$card-columns-margin: $card-spacer-y !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_chart.scss b/frontend/src/assets/scss/custom/gradido-custom/_chart.scss index 0b497e283..95e884d49 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_chart.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_chart.scss @@ -2,7 +2,6 @@ $chart-height: 350px !default; $chart-height-sm: 230px !default; - $chart-legend-margin-top: 2.5rem !default; $chart-legend-font-size: $font-size-sm !default; $chart-legend-color: $text-muted !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_close.scss b/frontend/src/assets/scss/custom/gradido-custom/_close.scss index 41870d798..346372aa7 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_close.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_close.scss @@ -1,9 +1,9 @@ // Close -$close-font-size: $font-size-base * 1.5 !default; -$close-font-weight: $font-weight-bold !default; -$close-bg: transparent !default; -$close-hover-bg: transparent !default; -$close-color: rgba(0, 0, 0, .6) !default !default; -$close-hover-color: rgba(0, 0, 0, .9) !default; -$close-text-shadow: none !default; \ No newline at end of file +$close-font-size: $font-size-base * 1.5 !default; +$close-font-weight: $font-weight-bold !default; +$close-bg: transparent !default; +$close-hover-bg: transparent !default; +$close-color: rgb(0 0 0 / 60%) !default !default; +$close-hover-color: rgb(0 0 0 / 90%) !default; +$close-text-shadow: none !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_color.scss b/frontend/src/assets/scss/custom/gradido-custom/_color.scss index b9749091a..20fcbefd6 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_color.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_color.scss @@ -1,8 +1,8 @@ - - // Color system +@use "sass:map"; +@use "sass:color"; -$white: #fff !default; +$white: #fff !default; $gray-100: #f6f9fc !default; $gray-200: #e9ecef !default; $gray-300: #dee2e6 !default; @@ -12,177 +12,190 @@ $gray-600: #8898aa !default; // Line footer color $gray-700: #525f7f !default; // Line p color $gray-800: #32325d !default; // Line heading color $gray-900: #212529 !default; -$black: #000 !default; - +$black: #000 !default; $grays: () !default; -$grays: map-merge(( - "100": $gray-100, - "200": $gray-200, - "300": $gray-300, - "400": $gray-400, - "500": $gray-500, - "600": $gray-600, - "700": $gray-700, - "800": $gray-800, - "900": $gray-900 -), $grays); - -$blue: #5e72e4 !default; -$indigo: #5603ad !default; -$purple: #8965e0 !default; -$pink: #f3a4b5 !default; -$red: #f5365c !default; -$orange: #fb6340 !default; -$yellow: #ffd600 !default; -$green: #2dce89 !default; -$teal: #11cdef !default; -$cyan: #2bffc6 !default; - +$grays: map.merge( + ( + "100": $gray-100, + "200": $gray-200, + "300": $gray-300, + "400": $gray-400, + "500": $gray-500, + "600": $gray-600, + "700": $gray-700, + "800": $gray-800, + "900": $gray-900 + ), + $grays +); +$blue: #5e72e4 !default; +$indigo: #5603ad !default; +$purple: #8965e0 !default; +$pink: #f3a4b5 !default; +$red: #f5365c !default; +$orange: #fb6340 !default; +$yellow: #ffd600 !default; +$green: #2dce89 !default; +$teal: #11cdef !default; +$cyan: #2bffc6 !default; $colors: () !default; -$colors: map-merge(( - "blue": $blue, - "indigo": $indigo, - "purple": $purple, - "pink": $pink, - "red": $red, - "orange": $orange, - "yellow": $yellow, - "green": $green, - "teal": $teal, - "cyan": $cyan, - "white": $white, - "gray": $gray-600, - "light": $gray-400, - "lighter": $gray-200, - "gray-dark": $gray-800 -), $colors); - -$default: #172b4d !default; -$primary: #5e72e4 !default; -$secondary: #f7fafc !default; -$success: $green !default; -$info: $teal !default; -$warning: $orange !default; -$danger: $red !default; -$light: $gray-500 !default; -$dark: $gray-900 !default; -$darker: darken($gray-900, 15%) !default; - -$facebook: #3b5999 !default; -$twitter: #1da1f2 !default; -$google-plus: #dd4b39 !default; -$instagram: #e4405f !default; -$pinterest: #bd081c !default; -$youtube: #cd201f !default; -$slack: #3aaf85 !default; -$dribbble: #ea4c89 !default; -$github: #222222 !default; - +$colors: map.merge( + ( + "blue": $blue, + "indigo": $indigo, + "purple": $purple, + "pink": $pink, + "red": $red, + "orange": $orange, + "yellow": $yellow, + "green": $green, + "teal": $teal, + "cyan": $cyan, + "white": $white, + "gray": $gray-600, + "light": $gray-400, + "lighter": $gray-200, + "gray-dark": $gray-800 + ), + $colors +); +$default: #172b4d !default; +$primary: #5e72e4 !default; +$secondary: #f7fafc !default; +$success: $green !default; +$info: $teal !default; +$warning: $orange !default; +$danger: $red !default; +$light: $gray-500 !default; +$dark: $gray-900 !default; +$darker: color.adjust($gray-900, $lightness: -15%) !default; +$facebook: #3b5999 !default; +$twitter: #1da1f2 !default; +$google-plus: #dd4b39 !default; +$instagram: #e4405f !default; +$pinterest: #bd081c !default; +$youtube: #cd201f !default; +$slack: #3aaf85 !default; +$dribbble: #ea4c89 !default; +$github: #222 !default; $theme-colors: () !default; -$theme-colors: map-merge(( - "default": $default, - "primary": $primary, - "secondary": $secondary, - "success": $success, - "info": $info, - "warning": $warning, - "danger": $danger, - "white": $white, - "neutral": $white, - "dark": $dark, - "darker": $darker -), $theme-colors); - +$theme-colors: map.merge( + ( + "default": $default, + "primary": $primary, + "secondary": $secondary, + "success": $success, + "info": $info, + "warning": $warning, + "danger": $danger, + "white": $white, + "neutral": $white, + "dark": $dark, + "darker": $darker + ), + $theme-colors +); $brand-colors: () !default; -$brand-colors: map-merge(( - "facebook": $facebook, - "twitter": $twitter, - "google-plus": $google-plus, - "instagram": $instagram, - "pinterest": $pinterest, - "youtube": $youtube, - "slack": $slack, - "dribbble": $dribbble, - "github": $github -), $brand-colors); - +$brand-colors: map.merge( + ( + "facebook": $facebook, + "twitter": $twitter, + "google-plus": $google-plus, + "instagram": $instagram, + "pinterest": $pinterest, + "youtube": $youtube, + "slack": $slack, + "dribbble": $dribbble, + "github": $github + ), + $brand-colors +); $shape-colors: () !default; -$shape-colors: map-merge(( - "default": #32325d, - "primary": #5533ff, - "secondary": #24b47e, - "neutral": #e9ecef, - "blue-gray": #b2cbe1, - -), $shape-colors); - +$shape-colors: map.merge( + ( + "default": #32325d, + "primary": #53f, + "secondary": #24b47e, + "neutral": #e9ecef, + "blue-gray": #b2cbe1, + ), + $shape-colors +); $shapes-primary-colors: () !default; -$shapes-primary-colors: map-merge(( - "step-1-gradient-bg": #281483, - "step-2-gradient-bg": #8f6ed5, - "step-3-gradient-bg": #d782d9, - "span-1-bg": #53f, - "span-2-bg": #4553ff, - "span-3-bg": #4f40ff, - "span-4-bg": #25ddf5, - "span-5-bg": #1fa2ff -), $shapes-primary-colors); - +$shapes-primary-colors: map.merge( + ( + "step-1-gradient-bg": #281483, + "step-2-gradient-bg": #8f6ed5, + "step-3-gradient-bg": #d782d9, + "span-1-bg": #53f, + "span-2-bg": #4553ff, + "span-3-bg": #4f40ff, + "span-4-bg": #25ddf5, + "span-5-bg": #1fa2ff + ), + $shapes-primary-colors +); $shapes-default-colors: () !default; -$shapes-default-colors: map-merge(( - "step-1-gradient-bg": #7795f8, - "step-2-gradient-bg": #6772e5, - "step-3-gradient-bg": #555abf, - "span-1-bg": #7795f8, - "span-2-bg": #7b9aff, - "span-3-bg": #6f8ff8, - "span-4-bg": #76eea7, - "span-5-bg": #6adaff -), $shapes-default-colors); - +$shapes-default-colors: map.merge( + ( + "step-1-gradient-bg": #7795f8, + "step-2-gradient-bg": #6772e5, + "step-3-gradient-bg": #555abf, + "span-1-bg": #7795f8, + "span-2-bg": #7b9aff, + "span-3-bg": #6f8ff8, + "span-4-bg": #76eea7, + "span-5-bg": #6adaff + ), + $shapes-default-colors +); $shapes-light-colors: () !default; -$shapes-light-colors: map-merge(( - "step-1-gradient-bg": #b2cbe1, - "step-2-gradient-bg": #f6f9fc, - "step-3-gradient-bg": #f6f9fc, - "span-1-bg": #b4cce1, - "span-2-bg": #c5dbef, - "span-3-bg": #b9d5ed, - "span-4-bg": #74e4a2, - "span-5-bg": #008169 -), $shapes-light-colors); - +$shapes-light-colors: map.merge( + ( + "step-1-gradient-bg": #b2cbe1, + "step-2-gradient-bg": #f6f9fc, + "step-3-gradient-bg": #f6f9fc, + "span-1-bg": #b4cce1, + "span-2-bg": #c5dbef, + "span-3-bg": #b9d5ed, + "span-4-bg": #74e4a2, + "span-5-bg": #008169 + ), + $shapes-light-colors +); $shapes-dark-colors: () !default; -$shapes-dark-colors: map-merge(( - "step-1-gradient-bg": #32325d, - "step-2-gradient-bg": #32325d, - "step-3-gradient-bg": #32325d, - "span-1-bg": #2e2e57, - "span-2-bg": #2b2b58, - "span-3-bg": #25254d, - "span-4-bg": #d782d9, - "span-5-bg": #008169 -), $shapes-dark-colors); - +$shapes-dark-colors: map.merge( + ( + "step-1-gradient-bg": #32325d, + "step-2-gradient-bg": #32325d, + "step-3-gradient-bg": #32325d, + "span-1-bg": #2e2e57, + "span-2-bg": #2b2b58, + "span-3-bg": #25254d, + "span-4-bg": #d782d9, + "span-5-bg": #008169 + ), + $shapes-dark-colors +); // Translucent color variations -$translucent-color-opacity: .6 !default; +$translucent-color-opacity: 0.6 !default; // Set a specific jump point for requesting color jumps -$theme-color-interval: 8% !default; +$theme-color-interval: 8% !default; -// The yiq lightness value that determines when the lightness of color changes from "dark" to "light". Acceptable values are between 0 and 255. -$yiq-contrasted-threshold: 200 !default; +// The yiq lightness value that determines when the lightness of color changes +// from "dark" to "light". Acceptable values are between 0 and 255. +$yiq-contrasted-threshold: 200 !default; // Customize the light and dark text colors for use in our YIQ color contrast function. -$yiq-text-dark: $gray-900 !default; -$yiq-text-light: $white !default; +$yiq-text-dark: $gray-900 !default; +$yiq-text-light: $white !default; // Action colors $star-rating-color: $gray-400 !default; $star-rating-color-active: $yellow !default; - $favorite-color: $yellow !default; $like-color: $blue !default; $love-color: $red !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_components.scss b/frontend/src/assets/scss/custom/gradido-custom/_components.scss index a670f3bcf..400fc0647 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_components.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_components.scss @@ -1,31 +1,24 @@ // Components -$shape-height-xl: 1.5 !default; -$shape-height-lg: 1.5 !default; -$shape-height-sm: 1.5 !default; - -$border-width: 1px !default; -$border-color: $gray-200 !default; - -$border-radius: .375rem !default; -$border-radius-xl: .5rem !default; -$border-radius-lg: .4375rem !default; -$border-radius-sm: .25rem !default; - -$box-shadow-sm: 0 0 .5rem rgba($gray-600, .075) !default; -$box-shadow: 0 0 2rem 0 rgba($gray-600, .15) !default; -$box-shadow-lg: 0 0 3rem rgba($gray-600, .175) !default; - -$component-active-color: $white !default; -$component-active-bg: theme-color("primary") !default; +$shape-height-xl: 1.5 !default; +$shape-height-lg: 1.5 !default; +$shape-height-sm: 1.5 !default; +$border-width: 1px !default; +$border-color: $gray-200 !default; +$border-radius: 0.375rem !default; +$border-radius-xl: 0.5rem !default; +$border-radius-lg: 0.4375rem !default; +$border-radius-sm: 0.25rem !default; +$box-shadow-sm: 0 0 0.5rem rgba($gray-600, 0.075) !default; +$box-shadow: 0 0 2rem 0 rgba($gray-600, 0.15) !default; +$box-shadow-lg: 0 0 3rem rgba($gray-600, 0.175) !default; +$component-active-color: $white !default; +$component-active-bg: theme-color("primary") !default; $component-active-border-color: theme-color("primary") !default; - -$component-hover-color: $gray-300 !default; -$component-hover-bg: $gray-300 !default; +$component-hover-color: $gray-300 !default; +$component-hover-bg: $gray-300 !default; $component-hover-border-color: $gray-300 !default; - -$caret-width: .3em !default; - -$transition-base: all .15s ease !default; -$transition-fade: opacity .15s linear !default; -$transition-collapse: height .35s ease !default; +$caret-width: 0.3em !default; +$transition-base: all 0.15s ease !default; +$transition-fade: opacity 0.15s linear !default; +$transition-collapse: height 0.35s ease !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_custom-forms.scss b/frontend/src/assets/scss/custom/gradido-custom/_custom-forms.scss index 4c9fb9ff5..4fa437b38 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_custom-forms.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_custom-forms.scss @@ -1,33 +1,29 @@ // Custom forms -$custom-control-gutter: 1.75rem !default; -$custom-control-spacer-x: 1rem !default; -$custom-control-indicator-size: 1.25rem !default; - -$custom-control-indicator-bg: $input-bg !default; -$custom-control-indicator-border-width: 1px !default; -$custom-control-indicator-border-color: $input-border-color !default; -$custom-control-indicator-box-shadow: none !default; - -$custom-control-indicator-focus-box-shadow: $custom-control-indicator-box-shadow !default; - -$custom-control-indicator-hover-color: $component-hover-color !default; -$custom-control-indicator-hover-bg: $component-hover-bg !default; -$custom-control-indicator-hover-border-color: $component-hover-border-color !default; - -$custom-control-indicator-active-color: $component-active-color !default; -$custom-control-indicator-active-bg: $component-active-bg !default; -$custom-control-indicator-active-border-color: $component-active-border-color !default; -$custom-control-indicator-active-box-shadow: $custom-control-indicator-box-shadow !default; - -$custom-control-indicator-checked-color: $component-active-color !default; -$custom-control-indicator-checked-bg: $component-active-bg !default; +$custom-control-gutter: 1.75rem !default; +$custom-control-spacer-x: 1rem !default; +$custom-control-indicator-size: 1.25rem !default; +$custom-control-indicator-bg: $input-bg !default; +$custom-control-indicator-border-width: 1px !default; +$custom-control-indicator-border-color: $input-border-color !default; +$custom-control-indicator-box-shadow: none !default; +$custom-control-indicator-focus-box-shadow: $custom-control-indicator-box-shadow !default; +$custom-control-indicator-hover-color: $component-hover-color !default; +$custom-control-indicator-hover-bg: $component-hover-bg !default; +$custom-control-indicator-hover-border-color: $component-hover-border-color !default; +$custom-control-indicator-active-color: $component-active-color !default; +$custom-control-indicator-active-bg: $component-active-bg !default; +$custom-control-indicator-active-border-color: $component-active-border-color !default; +$custom-control-indicator-active-box-shadow: $custom-control-indicator-box-shadow !default; +$custom-control-indicator-checked-color: $component-active-color !default; +$custom-control-indicator-checked-bg: $component-active-bg !default; $custom-control-indicator-checked-border-color: $component-active-border-color !default; -$custom-control-indicator-checked-box-shadow: $custom-control-indicator-box-shadow !default; -$custom-control-indicator-checked-disabled-bg: rgba(theme-color("primary"), .5) !default; +$custom-control-indicator-checked-box-shadow: $custom-control-indicator-box-shadow !default; -$custom-control-indicator-disabled-bg: $gray-200 !default; -$custom-control-label-disabled-color: $gray-600 !default; +// $custom-control-indicator-checked-disabled-bg: rgb(theme-color("primary") 0.5) !default; +$custom-control-indicator-checked-disabled-bg: theme-color("primary") !default; +$custom-control-indicator-disabled-bg: $gray-200 !default; +$custom-control-label-disabled-color: $gray-600 !default; +$custom-checkbox-indicator-border-radius: $border-radius-sm !default; -$custom-checkbox-indicator-border-radius: $border-radius-sm !default; -//$custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml !default;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default; +// $custom-checkbox-indicator-icon-checked: str-replace(url("data:image/svg+xml !default;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"), "#", "%23") !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_custom-toogle.scss b/frontend/src/assets/scss/custom/gradido-custom/_custom-toogle.scss index 578e2e4e0..ddf88c14c 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_custom-toogle.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_custom-toogle.scss @@ -1,5 +1,5 @@ // Custom toggle -$custom-toggle-width: 50px !default; -$custom-toggle-slider-bg: $gray-200 !default; -$custom-toggle-checked-bg: theme-color("primary") !default; +$custom-toggle-width: 50px !default; +$custom-toggle-slider-bg: $gray-200 !default; +$custom-toggle-checked-bg: theme-color("primary") !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_datepicker.scss b/frontend/src/assets/scss/custom/gradido-custom/_datepicker.scss index e4662636d..e745c22c5 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_datepicker.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_datepicker.scss @@ -1,28 +1,23 @@ // Datepicker +@use "sass:color"; $datepicker-border-radius: $card-border-radius !default; $datepicker-dropdown-padding: 20px 22px !default; - $datepicker-cell-transition: $transition-base !default; -$datepicker-cell-hover-background: lighten($gray-400, 55%); +$datepicker-cell-hover-background: color.adjust($gray-400, $lightness: 55%); $datepicker-cell-border-radius: 50% !default; $datepicker-cell-width: 36px !default; $datepicker-cell-height: 36px !default; - $datepicker-disabled-cell-color: $gray-300 !default; $datepicker-disabled-old-new-color: $gray-500 !default; - $datepicker-header-cell-border-radius: $border-radius !default; - $datepicker-active-color: $white !default; $datepicker-active-background: theme-color("primary") !default; -$datepicker-active-box-shadow: none !default; - +$datepicker-active-box-shadow: none !default; $datepicker-range-background: theme-color("primary") !default; -$datepicker-range-cell-focused-background: darken($datepicker-range-background, 5%); +$datepicker-range-cell-focused-background: color.adjust($datepicker-range-background, $lightness: -5%); $datepicker-range-color: $white !default; $datepicker-range-highlighted-bg: $gray-200 !default; - -$datepicker-dropdown-border: lighten($gray-400, 40%); +$datepicker-dropdown-border: color.adjust($gray-400, $lightness: 40%); $datepicker-dropdown-bg: $white !default; $datepicker-highlighted-bg: $datepicker-active-background !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_dropdown.scss b/frontend/src/assets/scss/custom/gradido-custom/_dropdown.scss index f26ac8c00..e699f20df 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_dropdown.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_dropdown.scss @@ -1,7 +1,9 @@ // Dropdown -$dropdown-bg: $white !default; -$dropdown-border-width: 0 !default; -$dropdown-border-color: rgba($black, .15) !default; -$dropdown-border-radius: $border-radius-lg !default; -$dropdown-box-shadow: 0 50px 100px rgba(50, 50, 93, .1), 0 15px 35px rgba(50, 50, 93, .15), 0 5px 15px rgba(0, 0, 0, .1) !default; +$dropdown-bg: $white !default; +$dropdown-border-width: 0 !default; +$dropdown-border-color: rgba($black, 0.15) !default; +$dropdown-border-radius: $border-radius-lg !default; +$dropdown-box-shadow: 0 50px 100px rgb(50 50 93 / 10%), + 0 15px 35px rgb(50 50 93 / 15%), + 0 5px 15px rgb(0 0 0 / 10%) !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_fonts.scss b/frontend/src/assets/scss/custom/gradido-custom/_fonts.scss index b9c2bf82a..be6fbe74a 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_fonts.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_fonts.scss @@ -1,88 +1,63 @@ - // Fonts - -$font-family-sans-serif: Open Sans, sans-serif !default; +$font-family-sans-serif: open sans, sans-serif !default; $font-family-base: $font-family-sans-serif !default; - -$font-size-base: 1rem !default; // Assumes the browser default, typically `16px` -$font-size-xl: ($font-size-base * 1.5); -$font-size-lg: ($font-size-base * 1.25); -$font-size-sm: ($font-size-base * .875); -$font-size-xs: ($font-size-base * .75); - -$font-weight-light: 300 !default; -$font-weight-normal: 400 !default; -$font-weight-bold: 600 !default; -$font-weight-extra-bold: 700 !default; - -$font-weight-base: $font-weight-normal !default; -$line-height-base: 1.5 !default; -$shape-height-base: 1.5 !default; - - -$h1-font-size: $font-size-base * 1.625 !default; -$h2-font-size: $font-size-base * 1.25 !default; -$h3-font-size: $font-size-base * 1.0625 !default; -$h4-font-size: $font-size-base * .9375 !default; -$h5-font-size: $font-size-base * .8125 !default; -$h6-font-size: $font-size-base * .625 !default; - - -$headings-margin-bottom: ($spacer / 2); -$headings-font-family: inherit !default; -$headings-font-weight: $font-weight-bold !default; -$headings-line-height: 1.5 !default; -$headings-color: $gray-800 !default; - -$heading-letter-spacing: .025em !default; -$heading-font-size: .95rem !default; -$heading-text-transform: uppercase !default; -$heading-font-weight: $headings-font-weight !default; - -$heading-title-letter-spacing: .025em !default; -$heading-title-font-size: 1.375rem !default; -$heading-title-font-weight: $font-weight-bold !default; -$heading-title-text-transform: uppercase !default; - -$heading-section-letter-spacing: .025em !default; -$heading-section-font-size: 1.375rem !default; -$heading-section-font-weight: $font-weight-bold !default; -$heading-section-text-transform: uppercase !default; - -$display1-size: 3.3rem !default; -$display2-size: 2.75rem !default; -$display3-size: 2.1875rem !default; -$display4-size: 1.6275rem !default; - -$display1-weight: $font-weight-bold !default; -$display2-weight: $font-weight-bold !default; -$display3-weight: $font-weight-bold !default; -$display4-weight: $font-weight-bold !default; -$display-line-height: $headings-line-height !default; - -$paragraph-font-size: 1rem !default; -$paragraph-font-weight: 300 !default; -$paragraph-line-height: 1.7 !default; - -$lead-font-size: ($paragraph-font-size * 1.25) !default; -$lead-font-weight: 300 !default; - -$small-font-size: 80% !default; - -$text-muted: $gray-600 !default; - -$blockquote-small-color: $gray-600 !default; -$blockquote-font-size: ($font-size-base * 1.25) !default; - -$hr-border-color: rgba($black, .1); -$hr-border-width: $border-width !default; - -$mark-padding: .2em !default; - -$dt-font-weight: $font-weight-bold !default; - -$list-inline-padding: .5rem !default; - -$mark-bg: #fcf8e3 !default; - -$hr-margin-y: $spacer * 2 !default; +$font-size-base: 1rem !default; // Assumes the browser default, typically `16px` +$font-size-xl: ($font-size-base * 1.5); +$font-size-lg: ($font-size-base * 1.25); +$font-size-sm: ($font-size-base * 0.875); +$font-size-xs: ($font-size-base * 0.75); +$font-weight-light: 300 !default; +$font-weight-normal: 400 !default; +$font-weight-bold: 600 !default; +$font-weight-extra-bold: 700 !default; +$font-weight-base: $font-weight-normal !default; +$line-height-base: 1.5 !default; +$shape-height-base: 1.5 !default; +$h1-font-size: $font-size-base * 1.625 !default; +$h2-font-size: $font-size-base * 1.25 !default; +$h3-font-size: $font-size-base * 1.0625 !default; +$h4-font-size: $font-size-base * 0.9375 !default; +$h5-font-size: $font-size-base * 0.8125 !default; +$h6-font-size: $font-size-base * 0.625 !default; +$headings-margin-bottom: ($spacer / 2); +$headings-font-family: inherit !default; +$headings-font-weight: $font-weight-bold !default; +$headings-line-height: 1.5 !default; +$headings-color: $gray-800 !default; +$heading-letter-spacing: 0.025em !default; +$heading-font-size: 0.95rem !default; +$heading-text-transform: uppercase !default; +$heading-font-weight: $headings-font-weight !default; +$heading-title-letter-spacing: 0.025em !default; +$heading-title-font-size: 1.375rem !default; +$heading-title-font-weight: $font-weight-bold !default; +$heading-title-text-transform: uppercase !default; +$heading-section-letter-spacing: 0.025em !default; +$heading-section-font-size: 1.375rem !default; +$heading-section-font-weight: $font-weight-bold !default; +$heading-section-text-transform: uppercase !default; +$display1-size: 3.3rem !default; +$display2-size: 2.75rem !default; +$display3-size: 2.1875rem !default; +$display4-size: 1.6275rem !default; +$display1-weight: $font-weight-bold !default; +$display2-weight: $font-weight-bold !default; +$display3-weight: $font-weight-bold !default; +$display4-weight: $font-weight-bold !default; +$display-line-height: $headings-line-height !default; +$paragraph-font-size: 1rem !default; +$paragraph-font-weight: 300 !default; +$paragraph-line-height: 1.7 !default; +$lead-font-size: ($paragraph-font-size * 1.25) !default; +$lead-font-weight: 300 !default; +$small-font-size: 80% !default; +$text-muted: $gray-600 !default; +$blockquote-small-color: $gray-600 !default; +$blockquote-font-size: ($font-size-base * 1.25) !default; +$hr-border-color: rgba($black, 0.1); +$hr-border-width: $border-width !default; +$mark-padding: 0.2em !default; +$dt-font-weight: $font-weight-bold !default; +$list-inline-padding: 0.5rem !default; +$mark-bg: #fcf8e3 !default; +$hr-margin-y: $spacer * 2 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_footer.scss b/frontend/src/assets/scss/custom/gradido-custom/_footer.scss index 50bc8fecb..8cc541f2e 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_footer.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_footer.scss @@ -1,13 +1,11 @@ // Footer -$footer-padding-y: 2.5rem; -$footer-padding-x: 0; - - -$footer-link-font-size: .85rem !default; +$footer-padding-y: 2.5rem; +$footer-padding-x: 0; +$footer-link-font-size: 0.85rem !default; $footer-bg: theme-color("secondary") !default; $footer-color: $gray-600 !default; $footer-link-color: $gray-600 !default; $footer-link-hover-color: $gray-700 !default; $footer-heading-color: $gray-600 !default; -$footer-heading-font-size: $font-size-sm !default; \ No newline at end of file +$footer-heading-font-size: $font-size-sm !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_form-groups.scss b/frontend/src/assets/scss/custom/gradido-custom/_form-groups.scss index 88bbfef4a..e367b97a5 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_form-groups.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_form-groups.scss @@ -1,3 +1,3 @@ // Form group -$form-group-margin-bottom: 1.5rem !default; +$form-group-margin-bottom: 1.5rem !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_form-validation.scss b/frontend/src/assets/scss/custom/gradido-custom/_form-validation.scss index 644b49b9f..0d9243e08 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_form-validation.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_form-validation.scss @@ -1,6 +1,7 @@ // Form validation +@use "sass:color"; -$form-feedback-valid-bg: lighten($success, 15%) !default; -$form-feedback-valid-color: theme-color("success") !default; -$form-feedback-invalid-bg: lighten($warning, 15%) !default; -$form-feedback-invalid-color: theme-color("warning") !default; +$form-feedback-valid-bg: color.adjust($success, $lightness: 15%) !default; +$form-feedback-valid-color: theme-color("success") !default; +$form-feedback-invalid-bg: color.adjust($warning, $lightness: 15%) !default; +$form-feedback-invalid-color: theme-color("warning") !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_forms.scss b/frontend/src/assets/scss/custom/gradido-custom/_forms.scss index e69de29bb..8b1a39374 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_forms.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_forms.scss @@ -0,0 +1 @@ +// empty diff --git a/frontend/src/assets/scss/custom/gradido-custom/_global.scss b/frontend/src/assets/scss/custom/gradido-custom/_global.scss index 14961b56a..f4f787be6 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_global.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_global.scss @@ -1,11 +1,9 @@ - // Global settings -$enable-caret: true !default; -$enable-rounded: true !default; -$enable-shadows: true !default; -$enable-gradients: false !default; -$enable-transitions: true !default; -$enable-grid-classes: true !default; -$enable-print-styles: true !default; - +$enable-caret: true !default; +$enable-rounded: true !default; +$enable-shadows: true !default; +$enable-gradients: false !default; +$enable-transitions: true !default; +$enable-grid-classes: true !default; +$enable-print-styles: true !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_grid-breakpoint.scss b/frontend/src/assets/scss/custom/gradido-custom/_grid-breakpoint.scss index 499e672a5..af4296cf6 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_grid-breakpoint.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_grid-breakpoint.scss @@ -11,7 +11,6 @@ $grid-breakpoints: ( @include _assert-ascending($grid-breakpoints, "$grid-breakpoints"); @include _assert-starts-at-zero($grid-breakpoints); - // Grid containers -// + // Define the maximum width of `.container` for different screen sizes. diff --git a/frontend/src/assets/scss/custom/gradido-custom/_icons.scss b/frontend/src/assets/scss/custom/gradido-custom/_icons.scss index b4b520996..f20490d59 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_icons.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_icons.scss @@ -1,7 +1,6 @@ - // Icons $icon-size: 3rem !default; $icon-size-xl: 5rem !default; $icon-size-lg: 4rem !default; -$icon-size-sm: 2rem !default; \ No newline at end of file +$icon-size-sm: 2rem !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_input-groups.scss b/frontend/src/assets/scss/custom/gradido-custom/_input-groups.scss index 7824bae28..66add56f5 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_input-groups.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_input-groups.scss @@ -1,9 +1,8 @@ // Input groups -$input-group-addon-color: $input-placeholder-color !default; -$input-group-addon-bg: $input-bg !default; -$input-group-addon-border-color: $input-border-color !default; - -$input-group-addon-focus-color: $input-focus-color !default; -$input-group-addon-focus-bg: $input-focus-bg !default; -$input-group-addon-focus-border-color: $input-focus-border-color !default; +$input-group-addon-color: $input-placeholder-color !default; +$input-group-addon-bg: $input-bg !default; +$input-group-addon-border-color: $input-border-color !default; +$input-group-addon-focus-color: $input-focus-color !default; +$input-group-addon-focus-bg: $input-focus-bg !default; +$input-group-addon-focus-border-color: $input-focus-border-color !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_input.scss b/frontend/src/assets/scss/custom/gradido-custom/_input.scss index c3320ccc7..12ecf12e5 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_input.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_input.scss @@ -1,45 +1,37 @@ // Input +@use "sass:color"; -$input-padding-y: $input-btn-padding-y !default; -$input-padding-x: $input-btn-padding-x !default; -$input-line-height: $input-btn-line-height !default; +$input-padding-y: $input-btn-padding-y !default; +$input-padding-x: $input-btn-padding-x !default; +$input-line-height: $input-btn-line-height !default; +$input-padding-y-sm: $input-btn-padding-y-sm !default; +$input-padding-x-sm: $input-btn-padding-x-sm !default; +$input-line-height-sm: $input-btn-line-height-sm !default; +$input-padding-y-lg: $input-btn-padding-y-lg !default; +$input-padding-x-lg: $input-btn-padding-x-lg !default; +$input-line-height-lg: $input-btn-line-height-lg !default; +$input-border-radius: $border-radius !default; +$input-border-radius-xl: $border-radius-xl !default; +$input-border-radius-lg: $border-radius-lg !default; +$input-border-radius-sm: $border-radius-sm !default; +$input-bg: $white !default; +$input-disabled-bg: $gray-200 !default; +$input-muted-bg: #f7fafe !default; +$input-focus-muted-bg: color.adjust($input-muted-bg, $lightness: 1%) !default; +$input-alternative-box-shadow: 0 1px 3px rgb(50 50 93 / 15%), 0 1px 0 rgb(0 0 0 / 2%) !default; +$input-focus-alternative-box-shadow: 0 4px 6px rgb(50 50 93 / 11%),0 1px 3px rgb(0 0 0 / 8%) !default; +$input-color: $gray-600 !default; +$input-border-color: #cad1d7 !default; +$input-border-width: $input-btn-border-width !default; +$input-box-shadow: none !default; // 0 1px 3px 0 $gray-400 !default; -$input-padding-y-sm: $input-btn-padding-y-sm !default; -$input-padding-x-sm: $input-btn-padding-x-sm !default; -$input-line-height-sm: $input-btn-line-height-sm !default; +$input-focus-bg: $white !default; +$input-focus-border-color: rgb(50 151 211 / 25%) !default; +$input-focus-color: $input-color !default; +$input-focus-width: 0 !default; +$input-focus-box-shadow: none !default; // 0 1px 3px 0 $gray-500 !default; -$input-padding-y-lg: $input-btn-padding-y-lg !default; -$input-padding-x-lg: $input-btn-padding-x-lg !default; -$input-line-height-lg: $input-btn-line-height-lg !default; - -$input-border-radius: $border-radius !default; -$input-border-radius-xl: $border-radius-xl !default; -$input-border-radius-lg: $border-radius-lg !default; -$input-border-radius-sm: $border-radius-sm !default; - -$input-bg: $white !default; -$input-disabled-bg: $gray-200 !default; - -$input-muted-bg: #F7FAFE !default; -$input-focus-muted-bg: lighten($input-muted-bg, 1%) !default; - -$input-alternative-box-shadow: 0 1px 3px rgba(50,50,93,.15), 0 1px 0 rgba(0,0,0,.02) !default; -$input-focus-alternative-box-shadow: 0 4px 6px rgba(50,50,93,.11),0 1px 3px rgba(0,0,0,.08) !default; - -$input-color: $gray-600 !default; -$input-border-color: #cad1d7 !default; -$input-border-width: $input-btn-border-width !default; -$input-box-shadow: none !default; // 0 1px 3px 0 $gray-400 !default; - -$input-focus-bg: $white !default; -$input-focus-border-color: rgba(50,151,211,.25) !default; -$input-focus-color: $input-color !default; -$input-focus-width: 0 !default; -$input-focus-box-shadow: none !default; //0 1px 3px 0 $gray-500 !default; - -$input-placeholder-color: $gray-500 !default; -$input-focus-placeholder-color: $gray-500 !default; - -$input-height-border: $input-border-width * 2 !default; - -$input-transition: all .2s cubic-bezier(0.68, -0.55, 0.265, 1.55) !default; +$input-placeholder-color: $gray-500 !default; +$input-focus-placeholder-color: $gray-500 !default; +$input-height-border: $input-border-width * 2 !default; +$input-transition: all 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55) !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_links.scss b/frontend/src/assets/scss/custom/gradido-custom/_links.scss index 3cdd10dad..bf06c470f 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_links.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_links.scss @@ -1,6 +1,7 @@ // Links +@use "sass:color"; -$link-color: $primary !default; -$link-decoration: none !default; -$link-hover-color: darken($link-color, 15%) !default; -$link-hover-decoration: none !default; \ No newline at end of file +$link-color: $primary !default; +$link-decoration: none !default; +$link-hover-color: color.adjust($link-color, $lightness: -15%) !default; +$link-hover-decoration: none !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_list-group.scss b/frontend/src/assets/scss/custom/gradido-custom/_list-group.scss index 0109d4644..647570cb1 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_list-group.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_list-group.scss @@ -1,23 +1,18 @@ // List group -$list-group-bg: $white !default; -$list-group-border-color: $border-color !default; //rgba($black, .125); -$list-group-border-width: $border-width !default; -$list-group-border-radius: $border-radius !default; - -$list-group-item-padding-y: 1rem !default; -$list-group-item-padding-x: 1rem !default; - -$list-group-hover-bg: $gray-100 !default; -$list-group-active-color: $component-active-color !default; -$list-group-active-bg: $component-active-bg !default; -$list-group-active-border-color: $list-group-active-bg !default; - -$list-group-disabled-color: $gray-600 !default; -$list-group-disabled-bg: $list-group-bg !default; - -$list-group-action-color: $gray-700 !default; -$list-group-action-hover-color: $list-group-action-color !default; - -$list-group-action-active-color: $list-group-action-color !default; -$list-group-action-active-bg: $gray-200 !default; +$list-group-bg: $white !default; +$list-group-border-color: $border-color !default; // rgba($black, .125); +$list-group-border-width: $border-width !default; +$list-group-border-radius: $border-radius !default; +$list-group-item-padding-y: 1rem !default; +$list-group-item-padding-x: 1rem !default; +$list-group-hover-bg: $gray-100 !default; +$list-group-active-color: $component-active-color !default; +$list-group-active-bg: $component-active-bg !default; +$list-group-active-border-color: $list-group-active-bg !default; +$list-group-disabled-color: $gray-600 !default; +$list-group-disabled-bg: $list-group-bg !default; +$list-group-action-color: $gray-700 !default; +$list-group-action-hover-color: $list-group-action-color !default; +$list-group-action-active-color: $list-group-action-color !default; +$list-group-action-active-bg: $gray-200 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_modals.scss b/frontend/src/assets/scss/custom/gradido-custom/_modals.scss index 3ef72f5bc..a372d586d 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_modals.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_modals.scss @@ -1,24 +1,20 @@ // Modals -$modal-inner-padding: 1.5rem !default; - -$modal-lg: 800px !default; -$modal-md: 500px !default; -$modal-sm: 380px !default; - -$modal-title-line-height: 1.1; - -$modal-content-bg: $white !default; -$modal-content-border-color: rgba($black, .2) !default; -$modal-content-border-width: 0 !default; -$modal-content-border-radius: $border-radius-lg !default; -$modal-content-box-shadow-xs: 0 15px 35px rgba(50,50,93,.2), 0 5px 15px rgba(0,0,0,.17); -$modal-content-box-shadow-sm-up: 0 15px 35px rgba(50,50,93,.2), 0 5px 15px rgba(0,0,0,.17); - -$modal-backdrop-bg: $black !default; -$modal-backdrop-opacity: .16 !default; -$modal-header-border-color: $gray-200 !default; -$modal-footer-border-color: $modal-header-border-color !default; -$modal-header-border-width: $modal-content-border-width !default; -$modal-footer-border-width: $modal-header-border-width !default; -$modal-header-padding: 1.25rem !default; \ No newline at end of file +$modal-inner-padding: 1.5rem !default; +$modal-lg: 800px !default; +$modal-md: 500px !default; +$modal-sm: 380px !default; +$modal-title-line-height: 1.1; +$modal-content-bg: $white !default; +$modal-content-border-color: rgba($black, 0.2) !default; +$modal-content-border-width: 0 !default; +$modal-content-border-radius: $border-radius-lg !default; +$modal-content-box-shadow-xs: 0 15px 35px rgb(50 50 93 / 20%), 0 5px 15px rgb(0 0 0 / 17%); +$modal-content-box-shadow-sm-up: 0 15px 35px rgb(50 50 93 / 20%), 0 5px 15px rgb(0 0 0 / 17%); +$modal-backdrop-bg: $black !default; +$modal-backdrop-opacity: 0.16 !default; +$modal-header-border-color: $gray-200 !default; +$modal-footer-border-color: $modal-header-border-color !default; +$modal-header-border-width: $modal-content-border-width !default; +$modal-footer-border-width: $modal-header-border-width !default; +$modal-header-padding: 1.25rem !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_navbar.scss b/frontend/src/assets/scss/custom/gradido-custom/_navbar.scss index 39bd31dd7..1f4abaf45 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_navbar.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_navbar.scss @@ -1,49 +1,41 @@ // Navbar -$navbar-transition: all .15s linear !default; -$navbar-padding-y: 1rem !default; -$navbar-padding-x: 1rem !default; - -$navbar-nav-link-padding-x: 1rem !default; -$navbar-nav-link-padding-y: 1rem !default; - -$navbar-nav-link-font-size: .9rem !default; -$navbar-nav-link-font-weight: 400 !default; -$navbar-nav-link-text-transform: normal !default; -$navbar-nav-link-letter-spacing: 0 !default; -$navbar-nav-link-border-radius: $border-radius !default; - -$navbar-search-width: 270px !default; -$navbar-search-bg: transparent !default; - -$navbar-search-border-radius: 2rem !default; -$navbar-search-border-width: 2px !default; - -$navbar-dark-bg: transparent !default; -$navbar-dark-hover-bg: rgba(255, 255, 255, .1) !default; -$navbar-dark-active-bg: rgba(255, 255, 255, .1) !default; -$navbar-dark-color: rgba($white, .95) !default; -$navbar-dark-hover-color: rgba($white, .65) !default; -$navbar-dark-active-color: rgba($white, .65) !default; -$navbar-dark-disabled-color: rgba($white, .25) !default; -$navbar-dark-toggler-border-color: transparent !default; - -$navbar-search-dark-border-color: rgba(255, 255, 255, .6) !default; -$navbar-search-dark-color: rgba(255, 255, 255, .6) !default; -$navbar-search-dark-focus-border-color: rgba(255, 255, 255, .9) !default; -$navbar-search-dark-focus-color: rgba(255, 255, 255, .9) !default; - -$navbar-light-bg: transparent !default; -$navbar-light-hover-bg: rgba(0, 0, 0, .1) !default; -$navbar-light-active-bg: rgba(0, 0, 0, .1) !default; -$navbar-light-border-color: $gray-100 !default; -$navbar-light-color: rgba($black, .5) !default; -$navbar-light-hover-color: rgba($black, .7) !default; -$navbar-light-active-color: rgba($black, .9) !default; -$navbar-light-disabled-color: rgba($black, .3) !default; +$navbar-transition: all 0.15s linear !default; +$navbar-padding-y: 1rem !default; +$navbar-padding-x: 1rem !default; +$navbar-nav-link-padding-x: 1rem !default; +$navbar-nav-link-padding-y: 1rem !default; +$navbar-nav-link-font-size: 0.9rem !default; +$navbar-nav-link-font-weight: 400 !default; +$navbar-nav-link-text-transform: normal !default; +$navbar-nav-link-letter-spacing: 0 !default; +$navbar-nav-link-border-radius: $border-radius !default; +$navbar-search-width: 270px !default; +$navbar-search-bg: transparent !default; +$navbar-search-border-radius: 2rem !default; +$navbar-search-border-width: 2px !default; +$navbar-dark-bg: transparent !default; +$navbar-dark-hover-bg: rgb(255 255 255 / 10%) !default; +$navbar-dark-active-bg: rgb(255 255 255 / 10%) !default; +$navbar-dark-color: rgba($white, 0.95) !default; +$navbar-dark-hover-color: rgba($white, 0.65) !default; +$navbar-dark-active-color: rgba($white, 0.65) !default; +$navbar-dark-disabled-color: rgba($white, 0.25) !default; +$navbar-dark-toggler-border-color: transparent !default; +$navbar-search-dark-border-color: rgb(255 255 255 / 60%) !default; +$navbar-search-dark-color: rgb(255 255 255 / 60%) !default; +$navbar-search-dark-focus-border-color: rgb(255 255 255 / 90%) !default; +$navbar-search-dark-focus-color: rgb(255 255 255 / 90%) !default; +$navbar-light-bg: transparent !default; +$navbar-light-hover-bg: rgb(0 0 0 / 10%) !default; +$navbar-light-active-bg: rgb(0 0 0 / 10%) !default; +$navbar-light-border-color: $gray-100 !default; +$navbar-light-color: rgba($black, 0.5) !default; +$navbar-light-hover-color: rgba($black, 0.7) !default; +$navbar-light-active-color: rgba($black, 0.9) !default; +$navbar-light-disabled-color: rgba($black, 0.3) !default; $navbar-light-toggler-border-color: transparent !default; - -$navbar-search-light-border-color: rgba(0, 0, 0, .6) !default; -$navbar-search-light-color: rgba(0, 0, 0, .6) !default; -$navbar-search-light-focus-border-color: rgba(0, 0, 0, .9) !default; -$navbar-search-light-focus-color: rgba(0, 0, 0, .9) !default; +$navbar-search-light-border-color: rgb(0 0 0 / 60%) !default; +$navbar-search-light-color: rgb(0 0 0 / 60%) !default; +$navbar-search-light-focus-border-color: rgb(0 0 0 / 90%) !default; +$navbar-search-light-focus-color: rgb(0 0 0 / 90%) !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_navs.scss b/frontend/src/assets/scss/custom/gradido-custom/_navs.scss index ba3083b02..106c58c4a 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_navs.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_navs.scss @@ -1,23 +1,20 @@ // Navs +@use "sass:color"; -$nav-link-padding-y: .25rem !default; -$nav-link-padding-x: .75rem !default; -$nav-link-color: $gray-700 !default; -$nav-link-hover-color: theme-color("primary") !default; -$nav-link-disabled-color: $gray-600 !default; - -$nav-pills-padding-y: .75rem !default; -$nav-pills-padding-x: 1rem !default; - -$nav-pills-space-x: 1rem !default; - -$nav-pills-bg: $white !default; -$nav-pills-border-width: 1px !default; -$nav-pills-border-color: theme-color("primary") !default; -$nav-pills-border-radius: $border-radius !default; - -$nav-pills-link-color: theme-color("primary") !default; -$nav-pills-link-hover-color: darken(theme-color("primary"), 5%) !default; -$nav-pills-link-active-color: color-yiq(theme-color("primary")) !default; -$nav-pills-link-active-bg: theme-color("primary") !default; -$nav-pills-box-shadow: $btn-box-shadow !default; +$nav-link-padding-y: 0.25rem !default; +$nav-link-padding-x: 0.75rem !default; +$nav-link-color: $gray-700 !default; +$nav-link-hover-color: theme-color("primary") !default; +$nav-link-disabled-color: $gray-600 !default; +$nav-pills-padding-y: 0.75rem !default; +$nav-pills-padding-x: 1rem !default; +$nav-pills-space-x: 1rem !default; +$nav-pills-bg: $white !default; +$nav-pills-border-width: 1px !default; +$nav-pills-border-color: theme-color("primary") !default; +$nav-pills-border-radius: $border-radius !default; +$nav-pills-link-color: theme-color("primary") !default; +$nav-pills-link-hover-color: color.adjust(theme-color("primary"), $lightness: -5%) !default; +$nav-pills-link-active-color: color-yiq(theme-color("primary")) !default; +$nav-pills-link-active-bg: theme-color("primary") !default; +$nav-pills-box-shadow: $btn-box-shadow !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_nouislider.scss b/frontend/src/assets/scss/custom/gradido-custom/_nouislider.scss index 9ce346e35..9d4bb94ca 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_nouislider.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_nouislider.scss @@ -1,17 +1,14 @@ // No UI Slider -$noui-target-bg: #eceeef !default; -$noui-target-thickness: 5px !default; -$noui-target-border-radius: 5px !default; -$noui-target-border-color: 0 !default; -$noui-target-box-shadow: inset 0 1px 2px rgba(90,97,105,.1) !default; - -$noui-slider-connect-bg: $primary !default; +$noui-target-bg: #eceeef !default; +$noui-target-thickness: 5px !default; +$noui-target-border-radius: 5px !default; +$noui-target-border-color: 0 !default; +$noui-target-box-shadow: inset 0 1px 2px rgb(90 97 105 / 10%) !default; +$noui-slider-connect-bg: $primary !default; $noui-slider-connect-disabled-bg: #b2b2b2 !default; - -$noui-handle-width: 15px !default; -$noui-handle-bg: theme-color("primary") !default; -$noui-handle-border: 0 !default; -$noui-handle-border-radius: 100% !default; - -$noui-origin-border-radius: 2px !default; +$noui-handle-width: 15px !default; +$noui-handle-bg: theme-color("primary") !default; +$noui-handle-border: 0 !default; +$noui-handle-border-radius: 100% !default; +$noui-origin-border-radius: 2px !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_pagination.scss b/frontend/src/assets/scss/custom/gradido-custom/_pagination.scss index 5ec0f81f9..f98151d0b 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_pagination.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_pagination.scss @@ -8,22 +8,20 @@ // $pagination-padding-x-lg: 1.5rem !default; // $pagination-line-height: 1.25 !default; -$pagination-color: $gray-600 !default; -$pagination-bg: $white !default; -$pagination-border-width: $border-width !default; -$pagination-border-color: $gray-300 !default; +$pagination-color: $gray-600 !default; +$pagination-bg: $white !default; +$pagination-border-width: $border-width !default; +$pagination-border-color: $gray-300 !default; -//$pagination-focus-box-shadow: $btn-hover-box-shadow !default; +// $pagination-focus-box-shadow: $btn-hover-box-shadow !default; -$pagination-hover-color: $gray-600 !default; -$pagination-hover-bg: $gray-300 !default; -$pagination-hover-border-color: $gray-300 !default; - -$pagination-active-color: $component-active-color !default; -$pagination-active-bg: $component-active-bg !default; -$pagination-active-border-color: $pagination-active-bg !default; -$pagination-active-box-shadow: $btn-hover-box-shadow !default; - -$pagination-disabled-color: $gray-600 !default; -$pagination-disabled-bg: $white !default; -$pagination-disabled-border-color: $gray-300 !default; +$pagination-hover-color: $gray-600 !default; +$pagination-hover-bg: $gray-300 !default; +$pagination-hover-border-color: $gray-300 !default; +$pagination-active-color: $component-active-color !default; +$pagination-active-bg: $component-active-bg !default; +$pagination-active-border-color: $pagination-active-bg !default; +$pagination-active-box-shadow: $btn-hover-box-shadow !default; +$pagination-disabled-color: $gray-600 !default; +$pagination-disabled-bg: $white !default; +$pagination-disabled-border-color: $gray-300 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_popovers.scss b/frontend/src/assets/scss/custom/gradido-custom/_popovers.scss index b667a294b..b5395f1d7 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_popovers.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_popovers.scss @@ -1,24 +1,20 @@ // Popovers -$popover-font-size: $font-size-sm !default; -$popover-bg: $white !default; -$popover-max-width: 276px !default; -$popover-border-width: 1px !default; -$popover-border-color: rgba($black, .05) !default; -$popover-border-radius: $border-radius-lg !default; -$popover-box-shadow: 0px .5rem 2rem 0px rgba($black, .2) !default; - -$popover-header-bg: $popover-bg !default; -$popover-header-color: $headings-color !default; -$popover-header-padding-y: .5rem !default; -$popover-header-padding-x: .95rem !default; - -$popover-body-color: $body-color !default; -$popover-body-padding-y: $popover-header-padding-y !default; -$popover-body-padding-x: $popover-header-padding-x !default; - -$popover-arrow-width: 1.5rem !default; -$popover-arrow-height: .75rem !default; -$popover-arrow-color: $popover-bg !default; - -$popover-arrow-outer-color: transparent !default; +$popover-font-size: $font-size-sm !default; +$popover-bg: $white !default; +$popover-max-width: 276px !default; +$popover-border-width: 1px !default; +$popover-border-color: rgba($black, 0.05) !default; +$popover-border-radius: $border-radius-lg !default; +$popover-box-shadow: 0 0.5rem 2rem 0 rgba($black, 0.2) !default; +$popover-header-bg: $popover-bg !default; +$popover-header-color: $headings-color !default; +$popover-header-padding-y: 0.5rem !default; +$popover-header-padding-x: 0.95rem !default; +$popover-body-color: $body-color !default; +$popover-body-padding-y: $popover-header-padding-y !default; +$popover-body-padding-x: $popover-header-padding-x !default; +$popover-arrow-width: 1.5rem !default; +$popover-arrow-height: 0.75rem !default; +$popover-arrow-color: $popover-bg !default; +$popover-arrow-outer-color: transparent !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_sections.scss b/frontend/src/assets/scss/custom/gradido-custom/_sections.scss index 02f0f4e52..df8f91f5b 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_sections.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_sections.scss @@ -1,5 +1,4 @@ // Body - $body-bg: #f8f9fe !default; $body-color: $gray-700 !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_spacing.scss b/frontend/src/assets/scss/custom/gradido-custom/_spacing.scss index 8b4cf5019..2569048a8 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_spacing.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_spacing.scss @@ -1,37 +1,42 @@ - // Spacing +@use "sass:map"; $spacer: 1rem !default; $spacers: () !default; -$spacers: map-merge(( - -9: -($spacer * 10), - -8: -($spacer * 8), - -7: -($spacer * 6), - -6: -($spacer * 4.5), - -5: -($spacer * 3), - -4: -($spacer * 1.5), - -3: -$spacer, - -2: -($spacer * .5), - -1: -($spacer * .25), - 0: 0, - 1: ($spacer * .25), - 2: ($spacer * .5), - 3: $spacer, - 4: ($spacer * 1.5), - 5: ($spacer * 3), - 6: ($spacer * 4.5), - 7: ($spacer * 6), - 8: ($spacer * 8), - 9: ($spacer * 10) -), $spacers); - +$spacers: map.merge( + ( + -9: - ($spacer * 10), + -8: - ($spacer * 8), + -7: - ($spacer * 6), + -6: - ($spacer * 4.5), + -5: - ($spacer * 3), + -4: - ($spacer * 1.5), + -3: - $spacer, + -2: - ($spacer * 0.5), + -1: - ($spacer * 0.25), + 0: 0, + 1: ($spacer * 0.25), + 2: ($spacer * 0.5), + 3: $spacer, + 4: ($spacer * 1.5), + 5: ($spacer * 3), + 6: ($spacer * 4.5), + 7: ($spacer * 6), + 8: ($spacer * 8), + 9: ($spacer * 10) + ), + $spacers +); // This variable affects the `.h-*` and `.w-*` classes. $sizes: () !default; -$sizes: map-merge(( - 25: 25%, - 50: 50%, - 75: 75%, - 100: 100% -), $sizes); \ No newline at end of file +$sizes: map.merge( + ( + 25: 25%, + 50: 50%, + 75: 75%, + 100: 100% + ), + $sizes +); diff --git a/frontend/src/assets/scss/custom/gradido-custom/_tables.scss b/frontend/src/assets/scss/custom/gradido-custom/_tables.scss index 64eb6056a..7648d9cc3 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_tables.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_tables.scss @@ -1,32 +1,26 @@ // Tables +@use "sass:color"; -$table-cell-padding: 1rem !default; -$table-cell-padding-sm: .5rem !default; - -$table-bg: transparent !default; -$table-accent-bg: rgba($black, .05) !default; -$table-hover-bg: $gray-100 !default; -$table-active-bg: $table-hover-bg !default; - -$table-border-width: $border-width !default; -$table-border-color: $gray-200 !default; - -$table-head-bg: $gray-100 !default; -$table-head-color: $gray-600 !default; -$table-head-spacer-y: .75rem !default; - -$table-head-font-size: .65rem !default; -$table-head-font-weight: $font-weight-bold !default; -$table-head-text-transform: uppercase !default; -$table-head-letter-spacing: 1px !default; - -$table-body-font-size: .8125rem !default; - -$table-dark-bg: theme-color("default") !default; -$table-dark-accent-bg: rgba($white, .05) !default; -$table-dark-hover-bg: rgba($white, .075) !default; -$table-dark-border-color: lighten(theme-color("default"), 7%) !default; -$table-dark-color: $body-bg !default; - -$table-dark-head-bg: lighten(theme-color("default"), 4%) !default; -$table-dark-head-color: lighten(theme-color("default"), 35%) !default; \ No newline at end of file +$table-cell-padding: 1rem !default; +$table-cell-padding-sm: 0.5rem !default; +$table-bg: transparent !default; +$table-accent-bg: rgba($black, 0.05) !default; +$table-hover-bg: $gray-100 !default; +$table-active-bg: $table-hover-bg !default; +$table-border-width: $border-width !default; +$table-border-color: $gray-200 !default; +$table-head-bg: $gray-100 !default; +$table-head-color: $gray-600 !default; +$table-head-spacer-y: 0.75rem !default; +$table-head-font-size: 0.65rem !default; +$table-head-font-weight: $font-weight-bold !default; +$table-head-text-transform: uppercase !default; +$table-head-letter-spacing: 1px !default; +$table-body-font-size: 0.8125rem !default; +$table-dark-bg: theme-color("default") !default; +$table-dark-accent-bg: rgba($white, 0.05) !default; +$table-dark-hover-bg: rgba($white, 0.075) !default; +$table-dark-border-color: color.adjust(theme-color("default"), $lightness: 7%) !default; +$table-dark-color: $body-bg !default; +$table-dark-head-bg: color.adjust(theme-color("default"), $lightness: 4%) !default; +$table-dark-head-color: color.adjust(theme-color("default"), $lightness: 35%) !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_tooltips.scss b/frontend/src/assets/scss/custom/gradido-custom/_tooltips.scss index ea7868fef..63f9ea078 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_tooltips.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_tooltips.scss @@ -1,3 +1,3 @@ // Tooltips -$tooltip-font-size: $font-size-sm !default; +$tooltip-font-size: $font-size-sm !default; diff --git a/frontend/src/assets/scss/custom/gradido-custom/_vertical-navbar.scss b/frontend/src/assets/scss/custom/gradido-custom/_vertical-navbar.scss index 6df40a4d6..4b05084e8 100644 --- a/frontend/src/assets/scss/custom/gradido-custom/_vertical-navbar.scss +++ b/frontend/src/assets/scss/custom/gradido-custom/_vertical-navbar.scss @@ -1,15 +1,12 @@ // Vertical navbar -$navbar-vertical-box-shadow: 0 0 2rem 0 rgba(136, 152, 170, .15) !important; +$navbar-vertical-box-shadow: 0 0 2rem 0 rgb(136 152 170 / 15%) !important; $navbar-vertical-width: 250px !default; $navbar-vertical-padding-x: 1.5rem !default; -$navbar-vertical-nav-link-padding-x: 1.5rem !default; -$navbar-vertical-nav-link-padding-y: .65rem !default; - +$navbar-vertical-nav-link-padding-x: 1.5rem !default; +$navbar-vertical-nav-link-padding-y: 0.65rem !default; $navbar-icon-min-width: 2.25rem !default; - $navbar-breadcrumb-padding-y: $nav-link-padding-y !default; $navbar-breadcrumb-padding-x: 0 !default; - $navbar-light-bg: $white !default; $navbar-light-border-color: $border-color !default; diff --git a/frontend/src/assets/scss/gradido.scss b/frontend/src/assets/scss/gradido.scss index 9bc3231c4..53a296713 100644 --- a/frontend/src/assets/scss/gradido.scss +++ b/frontend/src/assets/scss/gradido.scss @@ -1,14 +1,13 @@ - // Bootstrap (4.5.3) functions @import "~bootstrap/scss/functions"; - @import "custom/variables"; -// @import "~bootstrap/scss/variables"; wird am + +// @import "~bootstrap/scss/variables"; wird am // ende der custom/variables angehangen - + // Bootstrap (4.5.3) mixins -@import "~bootstrap/scss/mixins"; +@import "~bootstrap/scss/mixins"; // Bootstrap (4.5.3) components @@ -22,13 +21,16 @@ @import "~bootstrap/scss/close"; @import "~bootstrap/scss/code"; @import "~bootstrap/scss/custom-forms"; -@import "~bootstrap/scss/dropdown"; + +// @import "~bootstrap/scss/dropdown"; @import "~bootstrap/scss/forms"; -@import "~bootstrap/scss/functions"; + +// @import "~bootstrap/scss/functions"; @import "~bootstrap/scss/grid"; @import "~bootstrap/scss/input-group"; @import "~bootstrap/scss/list-group"; -@import "~bootstrap/scss/mixins"; + +// @import "~bootstrap/scss/mixins"; @import "~bootstrap/scss/modal"; @import "~bootstrap/scss/nav"; @import "~bootstrap/scss/navbar"; @@ -48,94 +50,162 @@ @import "~bootstrap/scss/bootstrap"; // Bootstrap-vue (2.21.1) scss -@import '~bootstrap-vue/src/index.scss'; - +@import "~bootstrap-vue/src/index"; .alert-success { - color: #155724; - background-color: #d4edda; - border-color: #c3e6cb; -} -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; } +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} .b-toast-danger .toast .toast-header { - color: #721c24; - background-color: rgba(248,215,218,.85); - border-bottom-color: rgba(245,198,203,.85); + color: #721c24; + background-color: rgb(248 215 218 / 85%); + border-bottom-color: rgb(245 198 203 / 85%); } -.b-toast-danger .toast .toast-body{ - background-color: rgba(252,237,238,.85); - border-color: rgba(245,198,203,.85); - color: #721c24; + +.b-toast-danger .toast .toast-body { + background-color: rgb(252 237 238 / 85%); + border-color: rgb(245 198 203 / 85%); + color: #721c24; } .b-toast-success .toast .toast-header { - color: #155724; - background-color: rgba(212,237,218,.58); - border-bottom-color: rgba(195,230,203,.85); -} -.b-toast-success .toast .toast-body{ - color: #155724; - background-color: rgba(212,237,218,.85); - border-bottom-color: rgba(195,230,203,.85); + color: #155724; + background-color: rgb(212 237 218 / 58%); + border-bottom-color: rgb(195 230 203 / 85%); } +.b-toast-success .toast .toast-body { + color: #155724; + background-color: rgb(212 237 218 / 85%); + border-bottom-color: rgb(195 230 203 / 85%); +} - .btn-primary pim { - background-color: #5a7b02; - border-color: #5e72e4; - } - a, - .copyright { - color: #5a7b02; - } - .font1_2em { - font-size: 1.2em; - } - .font2em { - font-size: 1.5em; - } - .gradido-global-color-text { - color: #3d443b; - } - .gradido-global-color-accent { - color: #047006; - } - .gradido-global-color-6e0a9c9e { - color: #000; - } - .gradido-global-color-2d0fb154 { - color: #047006; - } - .gradido-global-color-16efe88c { - color: #7ebc55; - } - .gradido-global-color-1939326 { - color: #f6fff6; - } - .gradido-global-color-9d79fc1 { - color: #047006; - } - .gradido-global-color-6347f4d { - color: #5a7b02; - } - .gradido-global-color-4fbc19a { - color: #014034; - } - .gradido-global-color-d341874 { - color: #b6d939; - } - .gradido-global-color-619d338 { - color: #8ebfb1; - } - .gradido-global-color-44819a9 { - color: #026873; - } - .gradido-global-color-gray { - color: #858383; - } \ No newline at end of file +// .btn-primary pim { +.btn-primary { + background-color: #5a7b02; + border-color: #5e72e4; +} + +.gradido-font-large { + font-size: large; + height: auto !important; +} + +a, +.copyright { + color: #5a7b02; +} + +.font12em { + font-size: 1.2em; +} + +.font2em { + font-size: 1.5em; +} + +.gradido-global-color-text { + color: #3d443b; +} + +.gradido-global-color-accent { + color: #047006; +} + +.gradido-global-color-6e0a9c9e { + color: #000; +} + +.gradido-global-color-2d0fb154 { + color: #047006; +} + +.gradido-global-color-16efe88c { + color: #7ebc55; +} + +.gradido-global-color-1939326 { + color: #f6fff6; +} + +.gradido-global-color-9d79fc1 { + color: #047006; +} + +.gradido-global-color-6347f4d { + color: #5a7b02; +} + +.gradido-global-color-4fbc19a { + color: #014034; +} + +.gradido-global-color-d341874 { + color: #b6d939; +} + +.gradido-global-color-619d338 { + color: #8ebfb1; +} + +.gradido-global-color-44819a9 { + color: #026873; +} + +.gradido-global-color-gray { + color: #858383; +} + +.gradido-custom-background { + background-color: #ebebeba3 !important; +} + +.gradido-max-width { + width: 100%; +} + +.gradido-width-300 { + width: 300px; +} + +.gradido-absolute { + position: absolute; +} + +.gradido-width-95-absolute { + width: 95%; + position: absolute; +} + +.gradido-width-96-absolute { + width: 96%; + position: absolute; +} + +.gradido-no-border-radius { + border-radius: 0; +} + +.gradido-no-border { + border: 0; +} + +.gradido-background-f1 { + background-color: #f1f1f1; +} + +.gradido-background-white { + background-color: #fff; +} + +.gradido-font-15rem { + font-size: 1.5rem; +} diff --git a/frontend/src/components/ClipboardCopy.vue b/frontend/src/components/ClipboardCopy.vue new file mode 100644 index 000000000..b952dbd8c --- /dev/null +++ b/frontend/src/components/ClipboardCopy.vue @@ -0,0 +1,37 @@ + + diff --git a/frontend/src/views/Layout/ContentFooter.spec.js b/frontend/src/components/ContentFooter.spec.js similarity index 92% rename from frontend/src/views/Layout/ContentFooter.spec.js rename to frontend/src/components/ContentFooter.spec.js index 4b360d581..f67560e44 100644 --- a/frontend/src/views/Layout/ContentFooter.spec.js +++ b/frontend/src/components/ContentFooter.spec.js @@ -11,7 +11,7 @@ describe('ContentFooter', () => { $i18n: { locale: 'en', }, - $t: jest.fn((t) => t), + $t: jest.fn((t, options) => (options ? [t, options] : t)), } const Wrapper = () => { @@ -33,11 +33,11 @@ describe('ContentFooter', () => { }) it('renders the copyright year', () => { - expect(wrapper.find('div.copyright').text()).toMatch(/©\s*2[0-9]{3,3}\s+/) + expect(mocks.$t).toBeCalledWith('footer.copyright.year', { year: 2022 }) }) it('renders a link to Gradido-Akademie', () => { - expect(wrapper.find('div.copyright').find('a').text()).toEqual('Gradido-Akademie') + expect(wrapper.find('div.copyright').find('a').text()).toEqual('footer.copyright.link') }) it('links to the login page when clicked on copyright', () => { @@ -51,7 +51,7 @@ describe('ContentFooter', () => { it('shows the current version', async () => { wrapper.setData({ version: 1.23 }) await wrapper.vm.$nextTick() - expect(wrapper.find('div.copyright').findAll('a').at(1).text()).toEqual('App version 1.23') + expect(mocks.$t).toBeCalledWith('footer.app_version', { version: 1.23 }) }) it('links to latest release on GitHub', () => { @@ -64,7 +64,7 @@ describe('ContentFooter', () => { wrapper.setData({ shortHash: 'ACCEDED' }) wrapper.setData({ hash: 'ACCEDEDC001D00DC001D00DC001D00DC001CAFA' }) await wrapper.vm.$nextTick() - expect(wrapper.find('div.copyright').findAll('a').at(2).text()).toEqual('(ACCEDED)') + expect(mocks.$t).toBeCalledWith('footer.short_hash', { shortHash: 'ACCEDED' }) }) it('links to last release commit', async () => { @@ -78,7 +78,7 @@ describe('ContentFooter', () => { describe('links to gradido.net', () => { it('has a link to the legal notice', () => { - expect(wrapper.findAll('a.nav-link').at(0).text()).toEqual('imprint') + expect(wrapper.findAll('a.nav-link').at(0).text()).toEqual('footer.imprint') }) it('links to the https://gradido.net/en/impressum when locale is en', () => { @@ -88,7 +88,7 @@ describe('ContentFooter', () => { }) it('has a link to the privacy policy', () => { - expect(wrapper.findAll('a.nav-link').at(1).text()).toEqual('privacy_policy') + expect(wrapper.findAll('a.nav-link').at(1).text()).toEqual('footer.privacy_policy') }) it('links to the https://gradido.net/en/datenschutz when locale is en', () => { @@ -98,7 +98,7 @@ describe('ContentFooter', () => { }) it('has a link to the members area', () => { - expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('members_area') + expect(wrapper.findAll('a.nav-link').at(2).text()).toEqual('navigation.members_area') }) it('links to the elopage', () => { diff --git a/frontend/src/views/Layout/ContentFooter.vue b/frontend/src/components/ContentFooter.vue similarity index 80% rename from frontend/src/views/Layout/ContentFooter.vue rename to frontend/src/components/ContentFooter.vue index dedfc4765..bdcb5b1a9 100755 --- a/frontend/src/views/Layout/ContentFooter.vue +++ b/frontend/src/components/ContentFooter.vue @@ -3,24 +3,24 @@ @@ -29,16 +29,16 @@ - {{ $t('imprint') }} + {{ $t('footer.imprint') }} - {{ $t('privacy_policy') }} + {{ $t('footer.privacy_policy') }} - {{ $t('members_area') }} + {{ $t('navigation.members_area') }} - {{ $t('whitepaper') }} + {{ $t('footer.whitepaper') }} - {{ $t('site.navbar.support') }} + {{ $t('navigation.support') }} @@ -59,7 +59,7 @@ - diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.spec.js b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js new file mode 100644 index 000000000..b533c6aa1 --- /dev/null +++ b/frontend/src/components/DecayInformations/CollapseLinksList.spec.js @@ -0,0 +1,126 @@ +import { mount } from '@vue/test-utils' +import CollapseLinksList from './CollapseLinksList' + +const localVue = global.localVue + +const mocks = { + $i18n: { + locale: 'en', + }, + $tc: jest.fn((tc) => tc), + $t: jest.fn((t) => t), +} + +const propsData = { + transactionLinks: [ + { + amount: '5', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 87, + memo: 'Eene meene Siegerpreis, vor mir steht ein Schokoeis. Hex-hex!', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + { + amount: '6', + code: 'ce28664b5308c17f931c0367', + createdAt: '2022-03-16T14:22:40.000Z', + holdAvailableAmount: '5.13109484759482747111', + id: 86, + memo: 'Eene meene buntes Laub, auf dem Schrank da liegt kein Staub.', + redeemedAt: null, + validUntil: '2022-03-30T14:22:40.000Z', + }, + ], + transactionLinkCount: 3, + value: 1, + pending: false, + pageSize: 5, +} + +describe('CollapseLinksList', () => { + let wrapper + + const Wrapper = () => { + return mount(CollapseLinksList, { localVue, mocks, propsData }) + } + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the component div.collapse-links-list', () => { + expect(wrapper.find('div.collapse-links-list').exists()).toBeTruthy() + }) + + describe('load more links', () => { + beforeEach(async () => { + await wrapper.find('button.test-button-load-more').trigger('click') + }) + + it('emits input', () => { + expect(wrapper.emitted('input')).toEqual([[2]]) + }) + }) + + describe('reset transaction link list', () => { + beforeEach(async () => { + await wrapper + .findComponent({ name: 'TransactionLink' }) + .vm.$emit('reset-transaction-link-list') + }) + + it('emits input ', () => { + expect(wrapper.emitted('input')).toEqual([[0]]) + }) + }) + + describe('button text', () => { + describe('one more link to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + }) + }) + + it('renders text in singular', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 0) + }) + }) + + describe('less than pageSize links to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + transactionLinkCount: 6, + }) + }) + + it('renders text in plural and shows the correct count of links', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 1, { n: 4 }) + }) + }) + + describe('more than pageSize links to load', () => { + beforeEach(async () => { + await wrapper.setProps({ + value: 1, + pending: false, + pageSize: 5, + transactionLinkCount: 16, + }) + }) + + it('renders text in plural with page size links to load', () => { + expect(mocks.$tc).toBeCalledWith('link-load', 2, { n: 5 }) + }) + }) + }) + }) +}) diff --git a/frontend/src/components/DecayInformations/CollapseLinksList.vue b/frontend/src/components/DecayInformations/CollapseLinksList.vue new file mode 100644 index 000000000..0c1db5f0a --- /dev/null +++ b/frontend/src/components/DecayInformations/CollapseLinksList.vue @@ -0,0 +1,66 @@ + + diff --git a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue index 0dc00a79a..6abacd2b8 100644 --- a/frontend/src/components/DecayInformations/DecayInformation-Decay.vue +++ b/frontend/src/components/DecayInformations/DecayInformation-Decay.vue @@ -1,7 +1,7 @@ diff --git a/frontend/src/views/Pages/ResetPassword.spec.js b/frontend/src/pages/ResetPassword.spec.js similarity index 74% rename from frontend/src/views/Pages/ResetPassword.spec.js rename to frontend/src/pages/ResetPassword.spec.js index 5ee2742f2..c43f71932 100644 --- a/frontend/src/views/Pages/ResetPassword.spec.js +++ b/frontend/src/pages/ResetPassword.spec.js @@ -2,13 +2,14 @@ import { mount, RouterLinkStub } from '@vue/test-utils' import ResetPassword from './ResetPassword' import flushPromises from 'flush-promises' -import { toastErrorSpy } from '../../../test/testSetup' +import { toastErrorSpy } from '@test/testSetup' -// validation is tested in src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js +// validation is tested in src/components/UserSettings/UserPassword.spec.js const localVue = global.localVue const apolloMutationMock = jest.fn() +const apolloQueryMock = jest.fn().mockResolvedValue() const routerPushMock = jest.fn() @@ -40,6 +41,7 @@ const mocks = { }, $apollo: { mutate: apolloMutationMock, + query: apolloQueryMock, }, } @@ -52,25 +54,27 @@ describe('ResetPassword', () => { describe('mount', () => { beforeEach(() => { + jest.clearAllMocks() wrapper = Wrapper() }) - describe('No valid optin', () => { - it.skip('does not render the Reset Password form when not authenticated', () => { - expect(wrapper.find('form').exists()).toBeFalsy() + describe('no valid optin', () => { + beforeEach(() => { + jest.clearAllMocks() + apolloQueryMock.mockRejectedValue({ message: 'Your time is up!' }) + wrapper = Wrapper() }) - it.skip('toasts an error when no valid optin is given', () => { - expect(toastErrorSpy).toHaveBeenCalledWith('error') + it('toasts an error when no valid optin is given', () => { + expect(toastErrorSpy).toHaveBeenCalledWith('Your time is up!') }) - it.skip('has a message suggesting to contact the support', () => { - expect(wrapper.find('div.header').text()).toContain('settings.password.reset') - expect(wrapper.find('div.header').text()).toContain('settings.password.not-authenticated') + it('redirects to /forgot-password/resetPassword', () => { + expect(routerPushMock).toBeCalledWith('/forgot-password/resetPassword') }) }) - describe('is authenticated', () => { + describe('valid optin', () => { it('renders the Reset Password form when authenticated', () => { expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy() }) @@ -78,7 +82,7 @@ describe('ResetPassword', () => { describe('Register header', () => { describe('from reset', () => { beforeEach(() => { - mocks.$route.path.mock = 'reset' + mocks.$route.path.mock = 'reset-password' wrapper = Wrapper() }) @@ -146,7 +150,6 @@ describe('ResetPassword', () => { describe('server response with error code > 10min', () => { beforeEach(async () => { - jest.clearAllMocks() apolloMutationMock.mockRejectedValue({ message: '...Code is older than 10 minutes' }) await wrapper.find('form').trigger('submit') await flushPromises() @@ -156,12 +159,12 @@ describe('ResetPassword', () => { expect(toastErrorSpy).toHaveBeenCalledWith('...Code is older than 10 minutes') }) - it('router pushes to /password/reset', () => { - expect(routerPushMock).toHaveBeenCalledWith('/password/reset') + it('router pushes to /forgot-password/resetPassword', () => { + expect(routerPushMock).toHaveBeenCalledWith('/forgot-password/resetPassword') }) }) - describe('server response with error code > 10min', () => { + describe('server response with error', () => { beforeEach(async () => { jest.clearAllMocks() apolloMutationMock.mockRejectedValueOnce({ message: 'Error' }) @@ -176,6 +179,7 @@ describe('ResetPassword', () => { describe('server response with success on /checkEmail', () => { beforeEach(async () => { + jest.clearAllMocks() mocks.$route.path.mock = 'checkEmail' apolloMutationMock.mockResolvedValue({ data: { @@ -202,11 +206,33 @@ describe('ResetPassword', () => { it('redirects to "/thx/checkEmail"', () => { expect(routerPushMock).toHaveBeenCalledWith('/thx/checkEmail') }) + + describe('with param code', () => { + beforeEach(async () => { + mocks.$route.params.code = 'the-most-secret-code-ever' + apolloMutationMock.mockResolvedValue({ + data: { + resetPassword: 'success', + }, + }) + wrapper = Wrapper() + await wrapper.findAll('input').at(0).setValue('Aa123456_') + await wrapper.findAll('input').at(1).setValue('Aa123456_') + await wrapper.find('form').trigger('submit') + await flushPromises() + }) + + it('redirects to "/thx/checkEmail/the-most-secret-code-ever"', () => { + expect(routerPushMock).toHaveBeenCalledWith( + '/thx/checkEmail/the-most-secret-code-ever', + ) + }) + }) }) - describe('server response with success on /reset', () => { + describe('server response with success on /reset-password', () => { beforeEach(async () => { - mocks.$route.path.mock = 'reset' + mocks.$route.path.mock = 'reset-password' wrapper = Wrapper() apolloMutationMock.mockResolvedValue({ data: { @@ -219,8 +245,8 @@ describe('ResetPassword', () => { await flushPromises() }) - it('redirects to "/thx/reset"', () => { - expect(routerPushMock).toHaveBeenCalledWith('/thx/reset') + it('redirects to "/thx/resetPassword"', () => { + expect(routerPushMock).toHaveBeenCalledWith('/thx/resetPassword') }) }) }) diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/pages/ResetPassword.vue similarity index 64% rename from frontend/src/views/Pages/ResetPassword.vue rename to frontend/src/pages/ResetPassword.vue index e4adeda9f..7771be5f6 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/pages/ResetPassword.vue @@ -5,10 +5,12 @@
-

{{ $t(displaySetup.authenticated) }}

+ +

{{ $t(displaySetup.title) }}

- {{ $t(displaySetup.notAuthenticated) }} + + {{ $t(displaySetup.text) }}
@@ -19,13 +21,14 @@ - +
+ {{ $t(displaySetup.button) }}
@@ -44,19 +47,20 @@
- diff --git a/frontend/src/views/Pages/RegisterSelectCommunity.spec.js b/frontend/src/pages/SelectCommunity.spec.js similarity index 94% rename from frontend/src/views/Pages/RegisterSelectCommunity.spec.js rename to frontend/src/pages/SelectCommunity.spec.js index d2a13fb4e..f8896ae15 100644 --- a/frontend/src/views/Pages/RegisterSelectCommunity.spec.js +++ b/frontend/src/pages/SelectCommunity.spec.js @@ -1,8 +1,8 @@ import { mount, RouterLinkStub } from '@vue/test-utils' -import { communities, communityInfo } from '../../graphql/queries' -import RegisterSelectCommunity from './RegisterSelectCommunity' +import { communities, communityInfo } from '@/graphql/queries' +import SelectCommunity from './SelectCommunity' -import { toastErrorSpy } from '../../../test/testSetup' +import { toastErrorSpy } from '@test/testSetup' const localVue = global.localVue @@ -56,7 +56,7 @@ const apolloQueryMock = jest const mockStoreCommit = jest.fn() -describe('RegisterSelectCommunity', () => { +describe('SelectCommunity', () => { let wrapper const mocks = { @@ -86,7 +86,7 @@ describe('RegisterSelectCommunity', () => { } const Wrapper = () => { - return mount(RegisterSelectCommunity, { localVue, mocks, stubs }) + return mount(SelectCommunity, { localVue, mocks, stubs }) } describe('mount', () => { diff --git a/frontend/src/views/Pages/RegisterSelectCommunity.vue b/frontend/src/pages/SelectCommunity.vue similarity index 94% rename from frontend/src/views/Pages/RegisterSelectCommunity.vue rename to frontend/src/pages/SelectCommunity.vue index 28b89e95f..0535e4fe7 100644 --- a/frontend/src/views/Pages/RegisterSelectCommunity.vue +++ b/frontend/src/pages/SelectCommunity.vue @@ -48,11 +48,11 @@ diff --git a/frontend/src/pages/TransactionLink.spec.js b/frontend/src/pages/TransactionLink.spec.js new file mode 100644 index 000000000..8c41d0a92 --- /dev/null +++ b/frontend/src/pages/TransactionLink.spec.js @@ -0,0 +1,360 @@ +import { mount } from '@vue/test-utils' +import TransactionLink from './TransactionLink' +import { queryTransactionLink } from '@/graphql/queries' +import { redeemTransactionLink } from '@/graphql/mutations' +import { toastSuccessSpy, toastErrorSpy } from '@test/testSetup' + +const localVue = global.localVue + +const apolloQueryMock = jest.fn() +const apolloMutateMock = jest.fn() +const routerPushMock = jest.fn() + +const now = new Date().toISOString() + +const transactionLinkValidExpireDate = () => { + const validUntil = new Date() + return new Date(validUntil.setDate(new Date().getDate() + 14)).toISOString() +} + +apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: '2022-03-18T10:08:43.000Z', + deletedAt: null, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, +}) + +const mocks = { + $t: jest.fn((t, obj = null) => (obj ? [t, obj.date].join('; ') : t)), + $store: { + state: { + token: null, + email: 'bibi@bloxberg.de', + }, + }, + $apollo: { + query: apolloQueryMock, + mutate: apolloMutateMock, + }, + $route: { + params: { + code: 'some-code', + }, + }, + $router: { + push: routerPushMock, + }, +} + +describe('TransactionLink', () => { + let wrapper + + const Wrapper = () => { + return mount(TransactionLink, { localVue, mocks }) + } + + describe('mount', () => { + beforeEach(() => { + jest.clearAllMocks() + wrapper = Wrapper() + }) + + it('renders the component', () => { + expect(wrapper.find('div.show-transaction-link-informations').exists()).toBe(true) + }) + + it('calls the queryTransactionLink query', () => { + expect(apolloQueryMock).toBeCalledWith({ + query: queryTransactionLink, + variables: { + code: 'some-code', + }, + }) + }) + + describe('deleted link', () => { + beforeEach(() => { + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: '2022-03-18T10:08:43.000Z', + deletedAt: now, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a component RedeemedTextBox', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).exists()).toBe(true) + }) + + it('has a link deleted text in text box', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).text()).toContain( + 'gdd_per_link.link-deleted; ' + now, + ) + }) + }) + + describe('expired link', () => { + beforeEach(() => { + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: '2020-03-18T10:08:43.000Z', + redeemedAt: '2022-03-18T10:08:43.000Z', + deletedAt: null, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a component RedeemedTextBox', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).exists()).toBe(true) + }) + + it('has a link deleted text in text box', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).text()).toContain( + 'gdd_per_link.link-expired; 2020-03-18T10:08:43.000Z', + ) + }) + }) + + describe('redeemed link', () => { + beforeEach(() => { + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: '2022-03-18T10:08:43.000Z', + deletedAt: null, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a component RedeemedTextBox', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).exists()).toBe(true) + }) + + it('has a link deleted text in text box', () => { + expect(wrapper.findComponent({ name: 'RedeemedTextBox' }).text()).toContain( + 'gdd_per_link.redeemed-at; 2022-03-18T10:08:43.000Z', + ) + }) + }) + + describe('no token in store', () => { + beforeEach(() => { + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: null, + deletedAt: null, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a RedeemLoggedOut component', () => { + expect(wrapper.findComponent({ name: 'RedeemLoggedOut' }).exists()).toBe(true) + }) + + it('has a link to register with code', () => { + expect(wrapper.find('a[href="/register/some-code"]').exists()).toBe(true) + }) + + it('has a link to login with code', () => { + expect(wrapper.find('a[href="/login/some-code"]').exists()).toBe(true) + }) + }) + + describe('token in store and own link', () => { + beforeEach(() => { + mocks.$store.state.token = 'token' + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: null, + deletedAt: null, + user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a RedeemSelfCreator component', () => { + expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true) + }) + + it('has a no redeem text', () => { + expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).text()).toContain( + 'gdd_per_link.no-redeem', + ) + }) + + it('has a link to transaction page', () => { + expect(wrapper.find('a[to="/transactions"]').exists()).toBe(true) + }) + }) + + describe('valid link', () => { + beforeEach(() => { + mocks.$store.state.token = 'token' + apolloQueryMock.mockResolvedValue({ + data: { + queryTransactionLink: { + id: 92, + amount: '22', + memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ', + createdAt: '2022-03-17T16:10:28.000Z', + validUntil: transactionLinkValidExpireDate(), + redeemedAt: null, + deletedAt: null, + user: { firstName: 'Peter', publisherId: 0, email: 'peter@listig.de' }, + }, + }, + }) + wrapper = Wrapper() + }) + + it('has a RedeemValid component', () => { + expect(wrapper.findComponent({ name: 'RedeemValid' }).exists()).toBe(true) + }) + + it('has a button with redeem text', () => { + expect(wrapper.findComponent({ name: 'RedeemValid' }).find('button').text()).toBe( + 'gdd_per_link.redeem', + ) + }) + + describe('redeem link with success', () => { + let spy + + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockResolvedValue() + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click') + }) + + it('opens the modal', () => { + expect(spy).toBeCalledWith('gdd_per_link.redeem-text') + }) + + it('calls the API', () => { + expect(apolloMutateMock).toBeCalledWith( + expect.objectContaining({ + mutation: redeemTransactionLink, + variables: { + code: 'some-code', + }, + }), + ) + }) + + it('toasts a success message', () => { + expect(mocks.$t).toBeCalledWith('gdd_per_link.redeemed', { n: '22' }) + expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ') + }) + + it('pushes the route to overview', () => { + expect(routerPushMock).toBeCalledWith('/overview') + }) + }) + + describe('cancel redeem link', () => { + let spy + + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockResolvedValue() + spy.mockImplementation(() => Promise.resolve(false)) + await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click') + }) + + it('does not call the API', () => { + expect(apolloMutateMock).not.toBeCalled() + }) + + it('does not toasts a success message', () => { + expect(mocks.$t).not.toBeCalledWith('gdd_per_link.redeemed', { n: '22' }) + expect(toastSuccessSpy).not.toBeCalled() + }) + + it('does not push the route', () => { + expect(routerPushMock).not.toBeCalled() + }) + }) + + describe('redeem link with error', () => { + let spy + + beforeEach(async () => { + spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm') + apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' }) + spy.mockImplementation(() => Promise.resolve(true)) + await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click') + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Oh Noo!') + }) + + it('pushes the route to overview', () => { + expect(routerPushMock).toBeCalledWith('/overview') + }) + }) + }) + + describe('error on transaction link query', () => { + beforeEach(() => { + apolloQueryMock.mockRejectedValue({ message: 'Ouchh!' }) + wrapper = Wrapper() + }) + + it('toasts an error message', () => { + expect(toastErrorSpy).toBeCalledWith('Ouchh!') + }) + }) + }) +}) diff --git a/frontend/src/pages/TransactionLink.vue b/frontend/src/pages/TransactionLink.vue new file mode 100644 index 000000000..def8e2e7c --- /dev/null +++ b/frontend/src/pages/TransactionLink.vue @@ -0,0 +1,148 @@ + + diff --git a/frontend/src/views/Pages/UserProfileTransactionList.spec.js b/frontend/src/pages/Transactions.spec.js similarity index 93% rename from frontend/src/views/Pages/UserProfileTransactionList.spec.js rename to frontend/src/pages/Transactions.spec.js index a9ab48a19..25bc05985 100644 --- a/frontend/src/views/Pages/UserProfileTransactionList.spec.js +++ b/frontend/src/pages/Transactions.spec.js @@ -1,11 +1,11 @@ import { mount } from '@vue/test-utils' -import UserProfileTransactionList from './UserProfileTransactionList' +import Transactions from './Transactions' const localVue = global.localVue window.scrollTo = jest.fn() -describe('UserProfileTransactionList', () => { +describe('Transactions', () => { let wrapper const mocks = { @@ -22,7 +22,7 @@ describe('UserProfileTransactionList', () => { } const Wrapper = () => { - return mount(UserProfileTransactionList, { localVue, mocks, stubs }) + return mount(Transactions, { localVue, mocks, stubs }) } describe('mount', () => { diff --git a/frontend/src/views/Pages/UserProfileTransactionList.vue b/frontend/src/pages/Transactions.vue similarity index 86% rename from frontend/src/views/Pages/UserProfileTransactionList.vue rename to frontend/src/pages/Transactions.vue index 4eac3ebba..6fc588b10 100644 --- a/frontend/src/views/Pages/UserProfileTransactionList.vue +++ b/frontend/src/pages/Transactions.vue @@ -7,6 +7,7 @@ diff --git a/frontend/src/views/Pages/SendOverview/GddSend.vue b/frontend/src/views/Pages/SendOverview/GddSend.vue deleted file mode 100644 index cfab0899a..000000000 --- a/frontend/src/views/Pages/SendOverview/GddSend.vue +++ /dev/null @@ -1,18 +0,0 @@ - - diff --git a/frontend/src/views/Pages/SendOverview/GddSend/TransactionForm.spec.js b/frontend/src/views/Pages/SendOverview/GddSend/TransactionForm.spec.js deleted file mode 100644 index 25683e6df..000000000 --- a/frontend/src/views/Pages/SendOverview/GddSend/TransactionForm.spec.js +++ /dev/null @@ -1,235 +0,0 @@ -import { mount } from '@vue/test-utils' -import TransactionForm from './TransactionForm' -import flushPromises from 'flush-promises' - -const localVue = global.localVue - -describe('GddSend', () => { - let wrapper - - const mocks = { - $t: jest.fn((t) => t), - $i18n: { - locale: jest.fn(() => 'en'), - }, - $n: jest.fn((n) => String(n)), - $store: { - state: { - email: 'user@example.org', - }, - }, - } - - const propsData = { - balance: 0.0, - } - - const Wrapper = () => { - return mount(TransactionForm, { localVue, mocks, propsData }) - } - - describe('mount', () => { - beforeEach(() => { - wrapper = Wrapper() - }) - - it('renders the component', () => { - expect(wrapper.find('div.transaction-form').exists()).toBeTruthy() - }) - - describe('transaction form disable because balance 0,0 GDD', () => { - it('has a disabled input field of type email', () => { - expect(wrapper.find('#input-group-1').find('input').attributes('disabled')).toBe('disabled') - }) - it('has a disabled input field for amount', () => { - expect(wrapper.find('#input-2').find('input').attributes('disabled')).toBe('disabled') - }) - it('has a disabled textarea field ', () => { - expect(wrapper.find('#input-3').find('textarea').attributes('disabled')).toBe('disabled') - }) - it('has a message indicating that there are no GDDs to send ', () => { - expect(wrapper.find('.text-danger').text()).toBe('form.no_gdd_available') - }) - it('has no reset button and no submit button ', () => { - expect(wrapper.find('.test-buttons').exists()).toBeFalsy() - }) - }) - - describe('transaction form', () => { - beforeEach(() => { - wrapper.setProps({ balance: 100.0 }) - }) - describe('transaction form show because balance 100,0 GDD', () => { - it('has no warning message ', () => { - expect(wrapper.find('.text-danger').exists()).toBeFalsy() - }) - it('has a reset button', () => { - expect(wrapper.find('.test-buttons').findAll('button').at(0).attributes('type')).toBe( - 'reset', - ) - }) - it('has a submit button', () => { - expect(wrapper.find('.test-buttons').findAll('button').at(1).attributes('type')).toBe( - 'submit', - ) - }) - }) - - describe('email field', () => { - it('has an input field of type email', () => { - expect(wrapper.find('#input-group-1').find('input').attributes('type')).toBe('email') - }) - - it('has an envelope icon', () => { - expect(wrapper.find('#input-group-1').find('svg').attributes('aria-label')).toBe( - 'envelope', - ) - }) - - it('has a label form.receiver', () => { - expect(wrapper.find('label.input-1').text()).toBe('form.recipient') - }) - - it('has a placeholder "E-Mail"', () => { - expect(wrapper.find('#input-group-1').find('input').attributes('placeholder')).toBe( - 'E-Mail', - ) - }) - - it('flushes an error message when no valid email is given', async () => { - await wrapper.find('#input-group-1').find('input').setValue('a') - await flushPromises() - expect(wrapper.find('span.errors').text()).toBe('validations.messages.email') - }) - - it('trims the email after blur', async () => { - await wrapper.find('#input-group-1').find('input').setValue(' valid@email.com ') - await wrapper.find('#input-group-1').find('input').trigger('blur') - await flushPromises() - expect(wrapper.vm.form.email).toBe('valid@email.com') - }) - }) - - describe('amount field', () => { - it('has an input field of type text', () => { - expect(wrapper.find('#input-group-2').find('input').attributes('type')).toBe('text') - }) - - it('has an GDD text icon', () => { - expect(wrapper.find('#input-group-2').find('div.m-1').text()).toBe('GDD') - }) - - it('has a label form.amount', () => { - expect(wrapper.find('label.input-2').text()).toBe('form.amount') - }) - - it('has a placeholder "0.01"', () => { - expect(wrapper.find('#input-group-2').find('input').attributes('placeholder')).toBe( - '0.01', - ) - }) - - it('does not update form amount when invalid', async () => { - await wrapper.find('#input-group-2').find('input').setValue('invalid') - await wrapper.find('#input-group-2').find('input').trigger('blur') - await flushPromises() - expect(wrapper.vm.form.amountValue).toBe(0) - }) - - it('flushes an error message when no valid amount is given', async () => { - await wrapper.find('#input-group-2').find('input').setValue('a') - await flushPromises() - expect(wrapper.find('span.errors').text()).toBe('form.validation.gddSendAmount') - }) - - it('flushes an error message when amount is too high', async () => { - await wrapper.find('#input-group-2').find('input').setValue('123.34') - await flushPromises() - expect(wrapper.find('span.errors').text()).toBe('form.validation.gddSendAmount') - }) - - it('flushes no errors when amount is valid', async () => { - await wrapper.find('#input-group-2').find('input').setValue('87.34') - await flushPromises() - expect(wrapper.find('span.errors').exists()).toBeFalsy() - }) - }) - - describe('message text box', () => { - it('has an textarea field', () => { - expect(wrapper.find('#input-group-3').find('textarea').exists()).toBeTruthy() - }) - - it('has an chat-right-text icon', () => { - expect(wrapper.find('#input-group-3').find('svg').attributes('aria-label')).toBe( - 'chat right text', - ) - }) - - it('has a label form.message', () => { - expect(wrapper.find('label.input-3').text()).toBe('form.message') - }) - - it('flushes an error message when memo is less than 5 characters', async () => { - await wrapper.find('#input-group-3').find('textarea').setValue('a') - await flushPromises() - expect(wrapper.find('span.errors').text()).toBe('validations.messages.min') - }) - - it('flushes no error message when memo is valid', async () => { - await wrapper.find('#input-group-3').find('textarea').setValue('Long enough') - await flushPromises() - expect(wrapper.find('span.errors').exists()).toBeFalsy() - }) - }) - - describe('cancel button', () => { - it('has a cancel button', () => { - expect(wrapper.find('button[type="reset"]').exists()).toBeTruthy() - }) - - it('has the text "form.cancel"', () => { - expect(wrapper.find('button[type="reset"]').text()).toBe('form.reset') - }) - - it('clears all fields on click', async () => { - await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv') - await wrapper.find('#input-group-2').find('input').setValue('87.23') - await wrapper.find('#input-group-3').find('textarea').setValue('Long enough') - await flushPromises() - expect(wrapper.vm.form.email).toBe('someone@watches.tv') - expect(wrapper.vm.form.amount).toBe('87.23') - expect(wrapper.vm.form.memo).toBe('Long enough') - await wrapper.find('button[type="reset"]').trigger('click') - await flushPromises() - expect(wrapper.vm.form.email).toBe('') - expect(wrapper.vm.form.amount).toBe('') - expect(wrapper.vm.form.memo).toBe('') - }) - }) - - describe('submit', () => { - beforeEach(async () => { - await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv') - await wrapper.find('#input-group-2').find('input').setValue('87.23') - await wrapper.find('#input-group-3').find('textarea').setValue('Long enough') - await wrapper.find('form').trigger('submit') - await flushPromises() - }) - - it('emits set-transaction', async () => { - expect(wrapper.emitted('set-transaction')).toBeTruthy() - expect(wrapper.emitted('set-transaction')).toEqual([ - [ - { - email: 'someone@watches.tv', - amount: 87.23, - memo: 'Long enough', - }, - ], - ]) - }) - }) - }) - }) -}) diff --git a/frontend/src/views/Pages/UserProfileOverview.spec.js b/frontend/src/views/Pages/UserProfileOverview.spec.js deleted file mode 100644 index 7b75b0dd2..000000000 --- a/frontend/src/views/Pages/UserProfileOverview.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -import { shallowMount } from '@vue/test-utils' -import UserProfileOverview from './UserProfileOverview' - -const localVue = global.localVue - -describe('UserProfileOverview', () => { - let wrapper - - const mocks = { - $t: jest.fn((t) => t), - } - - const Wrapper = () => { - return shallowMount(UserProfileOverview, { localVue, mocks }) - } - - describe('shallow Mount', () => { - beforeEach(() => { - wrapper = Wrapper() - }) - - it('has a user card', () => { - expect(wrapper.findComponent({ name: 'UserCard' }).exists()).toBeTruthy() - }) - - it('has a user first and last name form', () => { - expect(wrapper.findComponent({ name: 'FormUserData' }).exists()).toBeTruthy() - }) - - it('has a user change password form', () => { - expect(wrapper.findComponent({ name: 'FormUserPasswort' }).exists()).toBeTruthy() - }) - - it('has a user change language form', () => { - expect(wrapper.findComponent({ name: 'FormUserLanguage' }).exists()).toBeTruthy() - }) - }) -}) diff --git a/frontend/test/testSetup.js b/frontend/test/testSetup.js index 2e26db998..0ceb5affd 100644 --- a/frontend/test/testSetup.js +++ b/frontend/test/testSetup.js @@ -17,7 +17,9 @@ import { focus } from 'vue-focus' import { loadAllRules } from '../src/validation-rules' -import { toasters } from '../src/mixins/toaster' +import { loadFilters } from '@/filters/amount' + +import { toasters } from '@/mixins/toaster' export const toastErrorSpy = jest.spyOn(toasters.methods, 'toastError') export const toastSuccessSpy = jest.spyOn(toasters.methods, 'toastSuccess') @@ -53,6 +55,10 @@ global.localVue.directive('focus', focus) global.localVue.mixin(toasters) +const filters = loadFilters(i18nMock) +global.localVue.filter('amount', filters.amount) +global.localVue.filter('GDD', filters.GDD) + // Filter the warnings for portal vue // https://github.com/BeniRupp/bug_portal-vue-target-already-exists const consoleWarn = global.console.warn diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 47b016aea..573f2a70b 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -2,11 +2,13 @@ const path = require('path') const webpack = require('webpack') const Dotenv = require('dotenv-webpack') const StatsPlugin = require('stats-webpack-plugin') +const HtmlWebpackPlugin = require('vue-html-webpack-plugin') +const CONFIG = require('./src/config') // vue.config.js module.exports = { devServer: { - port: process.env.PORT || 3000, + port: CONFIG.PORT, }, pluginOptions: { i18n: { @@ -14,6 +16,7 @@ module.exports = { fallbackLocale: 'de', localeDir: 'locales', enableInSFC: false, + enableLegacy: false, }, }, lintOnSave: true, @@ -33,11 +36,25 @@ 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 new StatsPlugin('webpack.stats.json'), + new HtmlWebpackPlugin({ + vue: true, + template: 'public/index.html', + meta: { + title_de: CONFIG.META_TITLE_DE, + title_en: CONFIG.META_TITLE_EN, + description_de: CONFIG.META_DESCRIPTION_DE, + description_en: CONFIG.META_DESCRIPTION_EN, + keywords_de: CONFIG.META_KEYWORDS_DE, + keywords_en: CONFIG.META_KEYWORDS_EN, + author: CONFIG.META_AUTHOR, + url: CONFIG.META_URL, + }, + }), ], infrastructureLogging: { level: 'warn', // 'none' | 'error' | 'warn' | 'info' | 'log' | 'verbose' @@ -45,7 +62,7 @@ module.exports = { }, css: { // Enable CSS source maps. - sourceMap: process.env.NODE_ENV !== 'production', + sourceMap: CONFIG.NODE_ENV !== 'production', }, outputDir: path.resolve(__dirname, './dist'), } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 296e0f01b..cd833a3df 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1726,6 +1726,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.0": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" + integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@7", "@babel/template@^7.12.13", "@babel/template@^7.16.0", "@babel/template@^7.3.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" @@ -1875,6 +1882,21 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" + integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -1916,6 +1938,83 @@ cssnano-preset-default "^4.0.0" postcss "^7.0.0" +"@intlify/core-base@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.1.9.tgz#e4e8c951010728e4af3a0d13d74cf3f9e7add7f6" + integrity sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw== + dependencies: + "@intlify/devtools-if" "9.1.9" + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@intlify/vue-devtools" "9.1.9" + +"@intlify/devtools-if@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.1.9.tgz#a30e1dd1256ff2c5c98d8d75d075384fba898e5d" + integrity sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ== + dependencies: + "@intlify/shared" "9.1.9" + +"@intlify/eslint-plugin-vue-i18n@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@intlify/eslint-plugin-vue-i18n/-/eslint-plugin-vue-i18n-1.4.0.tgz#f8fe791892c2dce7d189a364b6a908c87e1c3ac9" + integrity sha512-anB1eBf6rpxpWyW883gi6O1hozQy4Q02VyzyodOUnohOqT07GATVSxnr2J9/qQSV47xWukV+9LiRErJcU7d/uA== + dependencies: + "@eslint/eslintrc" "^1.2.0" + "@intlify/core-base" "^9.1.9" + "@intlify/message-compiler" "^9.1.9" + debug "^4.3.1" + glob "^7.1.3" + ignore "^5.0.5" + is-language-code "^3.1.0" + js-yaml "^4.0.0" + json5 "^2.1.3" + jsonc-eslint-parser "^2.0.0" + lodash "^4.17.11" + parse5 "^6.0.0" + semver "^7.3.4" + vue-eslint-parser "^8.0.0" + yaml-eslint-parser "^0.5.0" + +"@intlify/message-compiler@9.1.9", "@intlify/message-compiler@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.1.9.tgz#1193cbd224a71c2fb981455b8534a3c766d2948d" + integrity sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + source-map "0.6.1" + +"@intlify/message-resolver@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/message-resolver/-/message-resolver-9.1.9.tgz#3155ccd2f5e6d0dc16cad8b7f1d8e97fcda05bfc" + integrity sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA== + +"@intlify/runtime@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/runtime/-/runtime-9.1.9.tgz#2c12ce29518a075629efed0a8ed293ee740cb285" + integrity sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg== + dependencies: + "@intlify/message-compiler" "9.1.9" + "@intlify/message-resolver" "9.1.9" + "@intlify/shared" "9.1.9" + +"@intlify/shared@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.1.9.tgz#0baaf96128b85560666bec784ffb01f6623cc17a" + integrity sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw== + +"@intlify/vue-devtools@9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz#2be8f4dbe7f7ed4115676eb32348141d411e426b" + integrity sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og== + dependencies: + "@intlify/message-resolver" "9.1.9" + "@intlify/runtime" "9.1.9" + "@intlify/shared" "9.1.9" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2521,6 +2620,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "@types/prettier@^2.0.0": version "2.2.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.3.tgz#ef65165aea2924c9359205bf748865b8881753c0" @@ -3187,6 +3291,11 @@ acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== +acorn@^8.5.0, acorn@^8.7.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + address@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -3249,11 +3358,6 @@ alphanum-sort@^1.0.0: resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -3301,6 +3405,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -3333,7 +3442,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.3: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -3444,7 +3553,7 @@ apollo-utilities@1.3.4, apollo-utilities@^1.3.0, apollo-utilities@^1.3.4: ts-invariant "^0.4.0" tslib "^1.10.0" -aproba@^1.0.3, aproba@^1.1.1: +aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== @@ -3454,14 +3563,6 @@ arch@^2.1.1: resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3469,6 +3570,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3596,11 +3702,6 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -4014,6 +4115,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" + integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== + base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -4565,7 +4671,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -4624,6 +4730,21 @@ check-types@^8.0.3: resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -4663,11 +4784,6 @@ chownr@^1.0.1, chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - chrome-trace-event@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" @@ -4809,6 +4925,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" + clone@2.x: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" @@ -4833,11 +4956,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -4891,6 +5009,11 @@ color@^3.0.0: color-convert "^1.9.1" color-string "^1.5.4" +colord@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" + integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== + colorette@^1.2.1: version "1.4.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" @@ -5010,11 +5133,6 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - consolidate@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" @@ -5145,6 +5263,17 @@ cosmiconfig@^5.0.0: js-yaml "^3.13.1" parse-json "^4.0.0" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -5196,7 +5325,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5235,6 +5364,11 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" +css-functions-list@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.0.1.tgz#1460df7fb584d1692c30b105151dbb988c8094f9" + integrity sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw== + css-loader@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-1.0.1.tgz#6885bb5233b35ec47b006057da01cc640b6b79fe" @@ -5479,7 +5613,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0: +debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -5619,11 +5753,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -5757,6 +5886,15 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -5772,6 +5910,11 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -5793,6 +5936,13 @@ domhandler@^2.3.0: dependencies: domelementtype "1" +domhandler@^4.2.0, domhandler@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== + dependencies: + domelementtype "^2.2.0" + domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -5801,6 +5951,15 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" +domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + dot-object@^1.7.1: version "1.9.0" resolved "https://registry.yarnpkg.com/dot-object/-/dot-object-1.9.0.tgz#6e3d6d8379f794c5174599ddf05528f5990f076e" @@ -5979,10 +6138,10 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== errno@^0.1.3, errno@~0.1.7: version "0.1.8" @@ -6359,6 +6518,14 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^2.0.0, eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -6376,6 +6543,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + eslint@^4.19.1: version "4.19.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" @@ -6494,6 +6666,15 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" +espree@^9.0.0, espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== + dependencies: + acorn "^8.7.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^3.3.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -6633,6 +6814,13 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== + dependencies: + clone-regexp "^2.1.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -6817,6 +7005,17 @@ fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" +fast-glob@^3.2.11, fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -6827,6 +7026,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + fastparse@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" @@ -7131,13 +7335,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -7161,7 +7358,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1: +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -7176,27 +7373,6 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gaze@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -7221,16 +7397,16 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -7278,7 +7454,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -7297,7 +7473,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -7321,6 +7497,22 @@ glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^11.0.1, globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -7340,6 +7532,13 @@ globals@^13.6.0: dependencies: type-fest "^0.20.2" +globals@^13.9.0: + version "13.12.1" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb" + integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw== + dependencies: + type-fest "^0.20.2" + globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -7357,6 +7556,18 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -7394,21 +7605,17 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -globule@^1.0.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4" - integrity sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== -graceful-fs@^4.2.3, graceful-fs@^4.2.4: +graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -7512,11 +7719,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -7692,6 +7894,11 @@ html-tags@^2.0.0: resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + html-webpack-plugin@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" @@ -7717,6 +7924,16 @@ htmlparser2@^3.10.1: inherits "^2.0.1" readable-stream "^3.1.1" +htmlparser2@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" + integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.2" + domutils "^2.8.0" + entities "^3.0.1" + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -7860,6 +8077,11 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.0.5, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + ignore@^5.1.1, ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" @@ -7895,6 +8117,11 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -7954,7 +8181,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: +ini@^1.3.4, ini@^1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8211,13 +8438,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -8247,6 +8467,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-language-code@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-language-code/-/is-language-code-3.1.0.tgz#b2386b49227e7010636f16d0c2c681ca40136ab5" + integrity sha512-zJdQ3QTeLye+iphMeK3wks+vXSRFKh68/Pnlw7aOfApFSEIOhYa8P9vwwa6QrImNNBMJTiL1PpYF0f4BxDuEgA== + dependencies: + "@babel/runtime" "^7.14.0" + is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -8305,6 +8532,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-potential-custom-element-name@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" @@ -8331,6 +8563,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== + is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -9413,11 +9650,6 @@ jest@^26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" -js-base64@^2.1.8: - version "2.6.4" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" - integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== - js-beautify@^1.6.12, js-beautify@^1.6.14: version "1.13.5" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.13.5.tgz#a08a97890cae55daf1d758d3f6577bd4a64d7014" @@ -9464,6 +9696,13 @@ js-yaml@^3.13.1, js-yaml@^3.9.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.0.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -9653,7 +9892,7 @@ json3@^3.3.3: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.2: +json5@2.x, json5@^2.1.2, json5@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== @@ -9672,6 +9911,16 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-eslint-parser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.1.0.tgz#4c126b530aa583d85308d0b3041ff81ce402bbb2" + integrity sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g== + dependencies: + acorn "^8.5.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + semver "^7.3.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -9728,6 +9977,11 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== +known-css-properties@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.24.0.tgz#19aefd85003ae5698a5560d2b55135bf5432155c" + integrity sha512-RTSoaUAfLvpR357vWzAz/50Q/BmHfmE6ETSWfutT0AJiw10e6CmcdYRQJlLRd95B53D0Y2aD1jSxD3V3ySF+PA== + launch-editor-middleware@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/launch-editor-middleware/-/launch-editor-middleware-2.2.1.tgz#e14b07e6c7154b0a4b86a0fd345784e45804c157" @@ -9908,7 +10162,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0, lodash@~4.17.10: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10022,6 +10276,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -10097,7 +10356,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3, merge2@^1.3.0: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -10199,7 +10458,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -10220,21 +10479,6 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^3.0.0: - version "3.1.5" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732" - integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw== - dependencies: - yallist "^4.0.0" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - mississippi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" @@ -10282,7 +10526,7 @@ mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -10358,11 +10602,16 @@ mz@^2.4.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.12.1, nan@^2.13.2: +nan@^2.12.1: version "2.14.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nanoid@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -10445,22 +10694,6 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-gyp@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.3" - nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" - rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" - which "^2.0.2" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -10542,27 +10775,6 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -node-sass@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-6.0.1.tgz#cad1ccd0ce63e35c7181f545d8b986f3a9a887fe" - integrity sha512-f+Rbqt92Ful9gX0cGtdYwjTrWAaGURgaK5rZCWOgCNyGWusFYHhbqCCBoFBeat+HKETOU02AyTxNhJV0YZf2jQ== - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^7.0.3" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - lodash "^4.17.15" - meow "^9.0.0" - nan "^2.13.2" - node-gyp "^7.1.0" - npmlog "^4.0.0" - request "^2.88.0" - sass-graph "2.2.5" - stdout-stream "^1.4.0" - "true-case-path" "^1.0.2" - nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -10612,6 +10824,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= + normalize-url@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" @@ -10641,16 +10858,6 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.0, npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -10663,11 +10870,6 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - nwsapi@^2.0.7, nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -11064,7 +11266,7 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parse5@6.0.1, parse5@^6.0.1: +parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -11374,6 +11576,15 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" +postcss-html@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-1.3.0.tgz#5b28b44a9cc8351cdba6ae1f26017c2303bd7642" + integrity sha512-ewbwd7OGW4dLsErtvZH9HpVMEcXnlhYSzKsr7MepGlOT8imHTIZ/+pdfEruLS+hTYapLTQAWDnoQcJpsYU4uRw== + dependencies: + htmlparser2 "^7.1.2" + postcss "^8.4.0" + postcss-safe-parser "^6.0.0" + postcss-load-config@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" @@ -11392,6 +11603,11 @@ postcss-loader@^3.0.0: postcss-load-config "^2.0.0" schema-utils "^1.0.0" +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= + postcss-merge-longhand@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" @@ -11595,6 +11811,21 @@ postcss-reduce-transforms@^4.0.2: postcss "^7.0.0" postcss-value-parser "^3.0.0" +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss-scss@^4.0.2, postcss-scss@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.3.tgz#36c23c19a804274e722e83a54d20b838ab4767ac" + integrity sha512-j4KxzWovfdHsyxwl1BxkUal/O4uirvHgdzMKS1aWJBAV0qh2qj5qAZqpeBfVUYGWv+4iK9Az7SPyZ4fyNju1uA== + postcss-selector-parser@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" @@ -11614,6 +11845,14 @@ postcss-selector-parser@^6.0.2: uniq "^1.0.1" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" + integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" @@ -11643,6 +11882,11 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + postcss@^6.0.1, postcss@^6.0.23: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" @@ -11661,6 +11905,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.3 source-map "^0.6.1" supports-color "^6.1.0" +postcss@^8.4.0, postcss@^8.4.6, postcss@^8.4.8: + version "8.4.8" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032" + integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -12005,7 +12258,7 @@ read-pkg@^5.0.0, read-pkg@^5.1.1, read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -12043,6 +12296,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -12446,16 +12706,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sass-graph@2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.5.tgz#a981c87446b8319d96dce0671e487879bd24c2e8" - integrity sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag== - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - scss-tokenizer "^0.2.3" - yargs "^13.3.2" - sass-loader@^10: version "10.2.0" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.2.0.tgz#3d64c1590f911013b3fa48a0b22a83d5e1494716" @@ -12467,6 +12717,13 @@ sass-loader@^10: schema-utils "^3.0.0" semver "^7.3.2" +sass@1.32.13: + version "1.32.13" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.13.tgz#8d29c849e625a415bce71609c7cf95e15f74ed00" + integrity sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA== + dependencies: + chokidar ">=3.0.0 <4.0.0" + sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -12513,14 +12770,6 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -scss-tokenizer@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" - integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= - dependencies: - js-base64 "^2.1.8" - source-map "^0.4.2" - sdp@^2.12.0, sdp@^2.6.0: version "2.12.0" resolved "https://registry.yarnpkg.com/sdp/-/sdp-2.12.0.tgz#338a106af7560c86e4523f858349680350d53b22" @@ -12553,7 +12802,7 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semve resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.4: +semver@^7.2.1, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -12621,7 +12870,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -12724,6 +12973,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -12839,6 +13093,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -12871,23 +13130,16 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -12942,6 +13194,11 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +specificity@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" + integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -13027,13 +13284,6 @@ stats-webpack-plugin@^0.7.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -stdout-stream@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" - integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== - dependencies: - readable-stream "^2.0.1" - stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -13100,16 +13350,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -13135,6 +13376,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.padend@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311" @@ -13211,6 +13461,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -13253,6 +13510,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI= + stylehacks@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" @@ -13262,6 +13524,112 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" +stylelint-config-html@>=1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-html/-/stylelint-config-html-1.0.0.tgz#172acb996ee4c854e8c670fcff77ff0d98d3bec2" + integrity sha512-rKQUUWDpaYC7ybsS6tLxddjn6DxhjSIXybElSmcTyVQj3ExhmU3q+l41ktrlwHRyY0M5SkTkZiwngvYPYmsgSQ== + +stylelint-config-recommended-scss@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-5.0.2.tgz#193f483861c76a36ece24c52eb6baca4838f4a48" + integrity sha512-b14BSZjcwW0hqbzm9b0S/ScN2+3CO3O4vcMNOw2KGf8lfVSwJ4p5TbNEXKwKl1+0FMtgRXZj6DqVUe/7nGnuBg== + dependencies: + postcss-scss "^4.0.2" + stylelint-config-recommended "^6.0.0" + stylelint-scss "^4.0.0" + +stylelint-config-recommended-vue@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.3.0.tgz#a40f1efa5bff8bd3e2c1c58c3faacbe73b5b69ec" + integrity sha512-UEY/MZlBO2yiQdZoJm33trtqJ1MEDzBADhIAM0pTvfYbyXe5KwdbTtmALHETG1cXfDxuuUbJTvL9uENMy2Rjug== + dependencies: + semver "^7.3.5" + stylelint-config-html ">=1.0.0" + stylelint-config-recommended ">=6.0.0" + +stylelint-config-recommended@>=6.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz#7497372ae83ab7a6fffc18d7d7b424c6480ae15e" + integrity sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q== + +stylelint-config-recommended@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz#fd2523a322836005ad9bf473d3e5534719c09f9d" + integrity sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw== + +stylelint-config-standard-scss@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-3.0.0.tgz#dafc4fa5538d0ed833bf0a7d391e075683ffd96c" + integrity sha512-zt3ZbzIbllN1iCmc94e4pDxqpkzeR6CJo5DDXzltshuXr+82B8ylHyMMARNnUYrZH80B7wgY7UkKTYCFM0UUyw== + dependencies: + stylelint-config-recommended-scss "^5.0.2" + stylelint-config-standard "^24.0.0" + +stylelint-config-standard@^24.0.0: + version "24.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz#6823f207ab997ae0b641f9a636d007cc44d77541" + integrity sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw== + dependencies: + stylelint-config-recommended "^6.0.0" + +stylelint-scss@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.1.0.tgz#39b808696f8152081163d970449257ff80b5c041" + integrity sha512-BNYTo7MMamhFOlcaAWp2dMpjg6hPyM/FFqfDIYzmYVLMmQJqc8lWRIiTqP4UX5bresj9Vo0dKC6odSh43VP2NA== + dependencies: + lodash "^4.17.21" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.6" + postcss-value-parser "^4.1.0" + +stylelint@^14.5.3: + version "14.5.3" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.5.3.tgz#103b6670128ba3dea69fe3a1a07c4a5d3e0e3450" + integrity sha512-omHETL+kGHR+fCXFK1SkZD/A+emCP9esggAdWEl8GPjTNeyRYj+H6uetRDcU+7E451zwWiUYGVAX+lApsAZgsQ== + dependencies: + balanced-match "^2.0.0" + colord "^2.9.2" + cosmiconfig "^7.0.1" + css-functions-list "^3.0.1" + debug "^4.3.3" + execall "^2.0.0" + fast-glob "^3.2.11" + fastest-levenshtein "^1.0.12" + file-entry-cache "^6.0.1" + get-stdin "^8.0.0" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.1.0" + ignore "^5.2.0" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.24.0" + mathml-tag-names "^2.1.3" + meow "^9.0.0" + micromatch "^4.0.4" + normalize-path "^3.0.0" + normalize-selector "^0.2.0" + picocolors "^1.0.0" + postcss "^8.4.6" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + specificity "^0.4.1" + string-width "^4.2.3" + strip-ansi "^6.0.1" + style-search "^0.1.0" + supports-hyperlinks "^2.2.0" + svg-tags "^1.0.0" + table "^6.8.0" + v8-compile-cache "^2.3.0" + write-file-atomic "^4.0.1" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -13303,6 +13671,14 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-hyperlinks@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -13366,23 +13742,22 @@ table@^6.0.4: string-width "^4.2.0" strip-ansi "^6.0.0" +table@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^6.0.2: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -13638,13 +14013,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -"true-case-path@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" - integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew== - dependencies: - glob "^7.1.2" - tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -14029,7 +14397,7 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.3: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -14134,6 +14502,19 @@ vue-eslint-parser@^7.6.0: esquery "^1.4.0" lodash "^4.17.15" +vue-eslint-parser@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz#5d31129a1b3dd89c0069ca0a1c88f970c360bd0d" + integrity sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g== + dependencies: + debug "^4.3.2" + eslint-scope "^7.0.0" + eslint-visitor-keys "^3.1.0" + espree "^9.0.0" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.5" + vue-flatpickr-component@^8.1.2: version "8.1.6" resolved "https://registry.yarnpkg.com/vue-flatpickr-component/-/vue-flatpickr-component-8.1.6.tgz#8fb25dc72946ceb1ab005b871a8151461c0883f6" @@ -14158,6 +14539,19 @@ vue-hot-reload-api@^2.3.0: resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== +vue-html-webpack-plugin@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/vue-html-webpack-plugin/-/vue-html-webpack-plugin-3.2.2.tgz#df1499f05bcdf22c2e7040fa760fb0f5de66beea" + integrity sha512-C1qz9hxxrzvPSkpr59GHY8oh0U6TUuKYkhRg47pcYoq8D7JQKDGON8xsI6LYudVKGowcPj8GulfdHsp0Cc1gJA== + dependencies: + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + tapable "^1.0.0" + toposort "^1.0.0" + util.promisify "1.0.0" + vue-i18n-extract@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/vue-i18n-extract/-/vue-i18n-extract-1.0.2.tgz#0a136e12d1634d6799e187aad81a7003d02f67a5" @@ -14599,7 +14993,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.9, which@^1.3.0: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -14613,13 +15007,6 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -14683,6 +15070,14 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" + integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" @@ -14759,6 +15154,20 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml-eslint-parser@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.5.0.tgz#01d4e4d992a820769ea85ef5fd526dfc20ebc6f5" + integrity sha512-nJeyLA3YHAzhBTZbRAbu3W6xrSCucyxExmA+ZDtEdUFpGllxAZpto2Zxo2IG0r0eiuEiBM4e+wiAdxTziTq94g== + dependencies: + eslint-visitor-keys "^3.0.0" + lodash "^4.17.21" + yaml "^1.10.2" + +yaml@^1.10.0, yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@10.x: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"