From d7df6d982d7ae28a932dc0947fc0d681ad7090ae Mon Sep 17 00:00:00 2001 From: Stefan Bussemaker <56066664+kad-busses@users.noreply.github.com> Date: Fri, 6 Aug 2021 16:43:37 +0200 Subject: [PATCH] set a more prescriptive eslint for better consistency (#187) * set a more prescriptive eslint for better consistency * update eslint to better reflect project needs * update code according to new ESLint rules * lint file --- .editorconfig | 19 - .eslintrc.js | 39 + .prettierrc | 7 + package-lock.json | 2243 ++++++++++++++++- package.json | 12 +- src/app/app-routing.module.ts | 41 +- src/app/app.component.spec.ts | 33 +- src/app/app.component.ts | 19 +- src/app/app.module.ts | 190 +- src/app/auth/auth-config.module.ts | 59 +- src/app/auth/auth.guard.ts | 26 +- src/app/auth/auth.interceptor.ts | 42 +- src/app/auth/error.interceptor.ts | 35 +- .../components/alert/alert.component.spec.ts | 35 +- src/app/components/alert/alert.component.ts | 55 +- .../confirmation-modal.component.ts | 34 +- .../language-switcher.component.ts | 31 +- src/app/components/map/map.component.ts | 1276 +++++----- src/app/components/map/map.service.ts | 282 +-- src/app/components/map/searchPDOK.ts | 76 +- .../observation-goal.component.ts | 220 +- .../observation-goals.component.ts | 173 +- .../organization/organization.component.ts | 110 +- .../datastream/datastream.component.ts | 283 ++- .../device-type/device-type.component.ts | 167 +- .../organization-contact.component.ts | 44 +- .../sensor-location.component.spec.ts | 33 +- .../sensor-location.component.ts | 204 +- .../sensor-status.component.spec.ts | 33 +- .../sensor-status/sensor-status.component.ts | 146 +- .../form-controls/sensor/sensor.component.ts | 155 +- .../form-controls/theme/theme.component.ts | 176 +- src/app/form-controls/type/type.component.ts | 171 +- src/app/forms/device/device.component.spec.ts | 35 +- src/app/forms/device/device.component.ts | 974 +++---- src/app/forms/devices/devices.component.ts | 560 ++-- .../organization-create.component.ts | 110 +- .../organization-join.component.ts | 130 +- .../organization-update.component.ts | 252 +- .../organization-users.component.ts | 124 +- src/app/login/login.component.spec.ts | 33 +- src/app/login/login.component.ts | 41 +- src/app/model/bodies/datastream-body.ts | 26 +- src/app/model/bodies/device-model.ts | 78 +- src/app/model/bodies/legal-entity-id.ts | 4 +- src/app/model/bodies/location.ts | 6 +- src/app/model/bodies/sensor-body.ts | 34 +- src/app/model/bodies/sensorTheme.ts | 81 +- src/app/model/bodies/sensorTypes.spec.ts | 2 +- src/app/model/bodies/sensorTypes.ts | 734 +++--- src/app/model/bodies/user-update.ts | 6 +- src/app/model/events/event-type.ts | 22 +- src/app/model/legalEntity.ts | 16 +- src/app/navbar/navbar.component.ts | 26 +- src/app/services/alert.service.ts | 92 +- src/app/services/connection.service.ts | 239 +- src/app/services/device.service.ts | 386 +-- src/app/services/env.service.provider.ts | 36 +- src/app/services/env.service.ts | 21 +- src/app/services/legal-entity.service.ts | 122 +- src/app/services/location.service.ts | 60 +- src/app/services/modal.service.ts | 54 +- src/app/services/observation-goal.service.ts | 88 +- src/app/services/user.service.ts | 27 +- src/app/sidebar/sidebar.component.ts | 19 +- .../validators/organization-mail.validator.ts | 36 +- src/app/viewer/viewer.component.ts | 6 +- src/environments/environment.prod.ts | 2 +- src/environments/environment.ts | 2 +- src/main.ts | 7 +- src/polyfills.ts | 2 +- src/test.ts | 22 +- tslint.json | 149 -- 73 files changed, 6660 insertions(+), 4473 deletions(-) delete mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .prettierrc delete mode 100644 tslint.json diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index c85f7d6..0000000 --- a/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.ts] -quote_type = single - -[*.html] -indent_size = 4 - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..5ffa5ce --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,39 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig/*.json', + sourceType: 'node', + ecmaVersion: 2020, + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:prettier/recommended', + 'prettier', + ], + root: true, + env: { + node: true, + jest: true, + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions', 'constructors'] }], + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'import/order': [ + 'error', + { + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], + }, +}; diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0226552 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "parser": "typescript", + "printWidth": 120, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "all" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0b69d94..85a819f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,8 +52,13 @@ "@types/ol": "^6.4.2", "@types/proj4": "^2.5.0", "@types/socket.io-client": "^1.4.36", + "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/parser": "^4.29.0", "codelyzer": "^6.0.0", - "eslint": "^7.23.0", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-import": "^2.23.4", + "eslint-plugin-prettier": "^3.4.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~6.3.4", @@ -61,9 +66,9 @@ "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", + "prettier": "^2.3.2", "protractor": "~7.0.0", "ts-node": "~8.3.0", - "tslint": "~6.1.0", "typescript": "~4.3.5" } }, @@ -2396,15 +2401,15 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -2416,12 +2421,12 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", "dev": true, "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -2440,12 +2445,15 @@ } }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@fortawesome/angular-fontawesome": { @@ -2505,6 +2513,26 @@ "node": ">=6" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -3054,6 +3082,252 @@ "integrity": "sha512-edqgAFXMEtVvaBZ3YnhamvmrHjoYpuxETmnb0lbTZmf/dXpAsO9ZKotUO4K2rn2SIZBDFCMOuA7fOe0H6dRZcA==", "dev": true }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz", + "integrity": "sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "4.29.0", + "@typescript-eslint/scope-manager": "4.29.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz", + "integrity": "sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + } + }, + "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.0.tgz", + "integrity": "sha512-+92YRNHFdXgq+GhWQPT2bmjX09X7EH36JfgN2/4wmhtwV/HPxozpCNst8jrWcngLtEVd/4zAwA6BKojAlf+YqA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz", + "integrity": "sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.0.tgz", + "integrity": "sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz", + "integrity": "sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz", + "integrity": "sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.29.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -3256,9 +3530,9 @@ } }, "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -3575,6 +3849,25 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "node_modules/array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3602,6 +3895,23 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -4279,6 +4589,7 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -6445,12 +6756,60 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-module-lexer": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", "dev": true }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -6489,28 +6848,31 @@ } }, "node_modules/eslint": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", - "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -6519,7 +6881,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -6528,7 +6890,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -6542,6 +6904,288 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-import/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6660,6 +7304,18 @@ "node": ">= 8" } }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.8.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", @@ -7256,6 +7912,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -7827,6 +8489,15 @@ "node": ">=0.10.0" } }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -7860,6 +8531,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -8578,6 +9264,20 @@ "node": ">=6" } }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -8653,6 +9353,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -8664,12 +9373,40 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-color-stop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", @@ -8694,9 +9431,9 @@ } }, "node_modules/is-core-module": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", - "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", "dependencies": { "has": "^1.0.3" }, @@ -8811,6 +9548,18 @@ "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", "dev": true }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8819,6 +9568,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -8895,6 +9659,36 @@ "node": ">=0.10.0" } }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -9619,6 +10413,43 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -9683,6 +10514,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -10507,6 +11344,24 @@ "node": ">=6" } }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -10823,6 +11678,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -10890,6 +11754,23 @@ "node": ">=0.10.0" } }, + "node_modules/object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -11520,6 +12401,85 @@ "node": ">=8" } }, + "node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -13784,6 +14744,30 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -14431,6 +15415,121 @@ "node": ">=10" } }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -15357,6 +16456,20 @@ "node": ">=0.10.0" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -15913,6 +17026,38 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "dev": true + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -16182,6 +17327,32 @@ "node": ">=8" } }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -16193,6 +17364,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -16805,6 +17985,17 @@ "typescript": ">=2.0" } }, + "node_modules/tsconfig-paths": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", + "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", + "dev": true, + "dependencies": { + "json5": "^2.2.0", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", @@ -16816,6 +18007,7 @@ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", @@ -16845,13 +18037,15 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, + "peer": true, "dependencies": { "tslib": "^1.8.1" }, @@ -16863,7 +18057,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -16951,6 +18146,21 @@ "node": "*" } }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -17209,6 +18419,16 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/validate-npm-package-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", @@ -18146,6 +19366,22 @@ "which": "bin/which" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -20017,15 +21253,15 @@ "dev": true }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -20034,12 +21270,12 @@ }, "dependencies": { "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", "dev": true, "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" } }, "ignore": { @@ -20049,9 +21285,9 @@ "dev": true }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true } } @@ -20093,6 +21329,23 @@ "@fortawesome/fontawesome-common-types": "^0.2.35" } }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -20559,6 +21812,151 @@ "integrity": "sha512-edqgAFXMEtVvaBZ3YnhamvmrHjoYpuxETmnb0lbTZmf/dXpAsO9ZKotUO4K2rn2SIZBDFCMOuA7fOe0H6dRZcA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz", + "integrity": "sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.29.0", + "@typescript-eslint/scope-manager": "4.29.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz", + "integrity": "sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.0.tgz", + "integrity": "sha512-+92YRNHFdXgq+GhWQPT2bmjX09X7EH36JfgN2/4wmhtwV/HPxozpCNst8jrWcngLtEVd/4zAwA6BKojAlf+YqA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.29.0", + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/typescript-estree": "4.29.0", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz", + "integrity": "sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0" + } + }, + "@typescript-eslint/types": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.0.tgz", + "integrity": "sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz", + "integrity": "sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "@typescript-eslint/visitor-keys": "4.29.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz", + "integrity": "sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.29.0", + "eslint-visitor-keys": "^2.0.0" + } + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -20752,9 +22150,9 @@ "dev": true }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -20994,6 +22392,19 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -21012,6 +22423,17 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -21540,7 +22962,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true + "dev": true, + "peer": true }, "builtins": { "version": "1.0.3", @@ -23238,12 +24661,48 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, "es-module-lexer": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.7.1.tgz", "integrity": "sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==", "dev": true }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -23276,28 +24735,31 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.25.0.tgz", - "integrity": "sha512-TVpSovpvCNpLURIScDRB6g5CYu/ZFq9GfX2hLNIV4dSBKxIWojeDODvYl3t0k0VtMxYeR8OXPCFE5+oHMlGfhw==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -23306,7 +24768,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -23315,7 +24777,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -23374,6 +24836,12 @@ "which": "^2.0.1" } }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "globals": { "version": "13.8.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", @@ -23451,6 +24919,226 @@ } } }, + "eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "requires": {} + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz", + "integrity": "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -23870,6 +25558,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -24311,6 +26005,12 @@ } } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -24335,6 +26035,15 @@ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -24910,6 +26619,17 @@ "ipaddr.js": "^1.9.0" } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -24967,6 +26687,12 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -24975,12 +26701,28 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, "is-color-stop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", @@ -25004,9 +26746,9 @@ } }, "is-core-module": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", - "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.5.0.tgz", + "integrity": "sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==", "requires": { "has": "^1.0.3" } @@ -25082,11 +26824,26 @@ "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", "dev": true }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -25142,6 +26899,24 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -25717,6 +27492,36 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -25772,6 +27577,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -26409,6 +28220,26 @@ "abbrev": "1" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -26654,6 +28485,12 @@ } } }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, "object-is": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", @@ -26700,6 +28537,17 @@ "isobject": "^3.0.1" } }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -27177,6 +29025,66 @@ "find-up": "^4.0.0" } }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -28797,6 +30705,21 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", + "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -29300,6 +31223,95 @@ "npm-normalize-package-bin": "^1.0.1" } }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -30031,6 +32043,17 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -30503,6 +32526,38 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "dev": true + }, "spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -30723,6 +32778,26 @@ "strip-ansi": "^6.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -30731,6 +32806,12 @@ "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -31172,6 +33253,17 @@ "yn": "^3.0.0" } }, + "tsconfig-paths": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz", + "integrity": "sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==", + "dev": true, + "requires": { + "json5": "^2.2.0", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", @@ -31182,6 +33274,7 @@ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", @@ -31202,7 +33295,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true } } }, @@ -31211,6 +33305,7 @@ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, + "peer": true, "requires": { "tslib": "^1.8.1" }, @@ -31219,7 +33314,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true } } }, @@ -31274,6 +33370,18 @@ "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -31489,6 +33597,16 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "validate-npm-package-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", @@ -32222,6 +34340,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", diff --git a/package.json b/package.json index fd49162..5871063 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "start": "ng serve --proxy-config src/proxy.conf.json --configuration=en", "start-nl": "npm run start -- --configuration=nl", "build": "ng build --configuration production", - "lint": "ng lint", + "lint": "eslint \"src/**/*.ts\"", + "lint:fix": "npm run lint -- --fix", "test": "ng test --no-watch --no-progress --browsers=ChromeHeadlessCI --code-coverage", "pree2e": "webdriver-manager update --versions.chrome 73.0.3683.75", "e2e": "ng e2e --webdriver-update=false", @@ -58,8 +59,13 @@ "@types/ol": "^6.4.2", "@types/proj4": "^2.5.0", "@types/socket.io-client": "^1.4.36", + "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/parser": "^4.29.0", "codelyzer": "^6.0.0", - "eslint": "^7.23.0", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-import": "^2.23.4", + "eslint-plugin-prettier": "^3.4.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~6.3.4", @@ -67,9 +73,9 @@ "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", + "prettier": "^2.3.2", "protractor": "~7.0.0", "ts-node": "~8.3.0", - "tslint": "~6.1.0", "typescript": "~4.3.5" } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 6bcf451..589e424 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,40 +1,37 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { LoginComponent } from './login/login.component'; -import { ViewerComponent } from './viewer/viewer.component'; +import { ObservationGoalComponent } from './components/observation-goal/observation-goal.component'; +import { ObservationGoalsComponent } from './components/observation-goals/observation-goals.component'; +import { OrganizationComponent } from './components/organization/organization.component'; import { DeviceComponent } from './forms/device/device.component'; import { DevicesComponent } from './forms/devices/devices.component'; -import { OrganizationComponent } from './components/organization/organization.component'; -import { ObservationGoalsComponent } from './components/observation-goals/observation-goals.component'; -import { ObservationGoalComponent } from './components/observation-goal/observation-goal.component'; +import { LoginComponent } from './login/login.component'; +import { ViewerComponent } from './viewer/viewer.component'; const routes: Routes = [ - { path: '', component: LoginComponent }, - { path: 'login', component: LoginComponent }, + { path: '', component: LoginComponent }, + { path: 'login', component: LoginComponent }, - { path: 'viewer', component: ViewerComponent }, + { path: 'viewer', component: ViewerComponent }, - { path: 'organization', component: OrganizationComponent }, + { path: 'organization', component: OrganizationComponent }, - { path: 'device', component: DeviceComponent }, - { path: 'device/:id', component: DeviceComponent }, + { path: 'device', component: DeviceComponent }, + { path: 'device/:id', component: DeviceComponent }, - { path: 'devices', component: DevicesComponent }, + { path: 'devices', component: DevicesComponent }, - { path: 'observationgoal', component: ObservationGoalComponent }, - { path: 'observationgoal/:id', component: ObservationGoalComponent }, + { path: 'observationgoal', component: ObservationGoalComponent }, + { path: 'observationgoal/:id', component: ObservationGoalComponent }, - { path: 'observationgoals', component: ObservationGoalsComponent }, + { path: 'observationgoals', component: ObservationGoalsComponent }, - // otherwise redirect to viewer - { path: '**', redirectTo: '', pathMatch: 'full' }, + // otherwise redirect to viewer + { path: '**', redirectTo: '', pathMatch: 'full' }, ]; @NgModule({ - imports: [ - RouterModule.forRoot(routes), - ], exports: [ - RouterModule, - ], + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index bcdbfe1..3bbb0b5 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -3,23 +3,24 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { - let component: AppComponent; - let fixture: ComponentFixture; + let component: AppComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ AppComponent ], - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AppComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(AppComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(AppComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 781e481..837aefe 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,18 +1,15 @@ -import { Title } from '@angular/platform-browser'; import { Component } from '@angular/core'; +import { Title } from '@angular/platform-browser'; @Component({ selector: 'app-root', templateUrl: 'app.component.html' }) export class AppComponent { + public title = $localize`:@@app.title:Sensor Registry`; - public title = $localize`:@@app.title:Sensor Registry`; - - constructor( - private titleService: Title, - ) { - this.setTitle(this.title); - } + constructor(private titleService: Title) { + this.setTitle(this.title); + } - public setTitle(newTitle: string) { - this.titleService.setTitle(newTitle); - } + public setTitle(newTitle: string) { + this.titleService.setTitle(newTitle); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ea1b114..14ad1ef 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,116 +1,116 @@ import '@angular/common/locales/global/nl'; +import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; -import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; +import * as freeRegularSvgIcons from '@fortawesome/free-regular-svg-icons'; +import * as freeSolidSvgIcons from '@fortawesome/free-solid-svg-icons'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { AuthConfigModule } from './auth/auth-config.module'; -import { ErrorInterceptor } from './auth/error.interceptor'; import { AuthInterceptor } from './auth/auth.interceptor'; -import { ModalService } from './services/modal.service'; -import { AppRoutingModule } from './app-routing.module'; -import { LoginComponent } from './login/login.component'; -import { MapService } from './components/map/map.service'; -import { ViewerComponent } from './viewer/viewer.component'; -import { NavBarComponent } from './navbar/navbar.component'; -import { MapComponent } from './components/map/map.component'; -import { SidebarComponent } from './sidebar/sidebar.component'; -import { DeviceComponent } from './forms/device/device.component'; +import { ErrorInterceptor } from './auth/error.interceptor'; import { AlertComponent } from './components/alert/alert.component'; -import { LanguageSwitcherComponent } from './components/language-switcher/language-switcher.component'; import { ConfirmationModalComponent } from './components/confirmation-modal/confirmation-modal.component'; -import { DevicesComponent } from './forms/devices/devices.component'; -import { ThemeComponent } from './form-controls/theme/theme.component'; +import { LanguageSwitcherComponent } from './components/language-switcher/language-switcher.component'; +import { MapComponent } from './components/map/map.component'; +import { MapService } from './components/map/map.service'; +import { ObservationGoalComponent } from './components/observation-goal/observation-goal.component'; +import { ObservationGoalsComponent } from './components/observation-goals/observation-goals.component'; +import { OrganizationComponent } from './components/organization/organization.component'; import { DatastreamComponent } from './form-controls/datastream/datastream.component'; import { DeviceTypeComponent } from './form-controls/device-type/device-type.component'; -import { OrganizationComponent } from './components/organization/organization.component'; -import { SensorStatusComponent } from './form-controls/sensor-status/sensor-status.component'; -import { OrganizationJoinComponent } from './forms/organization-join/organization-join.component'; +import { OrganizationContactComponent } from './form-controls/organization-contact/organization-contact.component'; import { SensorLocationComponent } from './form-controls/sensor-location/sensor-location.component'; +import { SensorStatusComponent } from './form-controls/sensor-status/sensor-status.component'; +import { SensorComponent } from './form-controls/sensor/sensor.component'; +import { ThemeComponent } from './form-controls/theme/theme.component'; +import { TypeComponent } from './form-controls/type/type.component'; +import { DeviceComponent } from './forms/device/device.component'; +import { DevicesComponent } from './forms/devices/devices.component'; import { OrganizationCreateComponent } from './forms/organization-create/organization-create.component'; +import { OrganizationJoinComponent } from './forms/organization-join/organization-join.component'; import { OrganizationUpdateComponent } from './forms/organization-update/organization-update.component'; -import { EnvServiceProvider } from './services/env.service.provider'; -import { OrganizationContactComponent } from './form-controls/organization-contact/organization-contact.component'; -import { SensorComponent } from './form-controls/sensor/sensor.component'; import { OrganizationUsersComponent } from './forms/organization-users/organization-users.component'; -import { TypeComponent } from './form-controls/type/type.component'; -import { ObservationGoalsComponent } from './components/observation-goals/observation-goals.component'; -import { ObservationGoalComponent } from './components/observation-goal/observation-goal.component'; -import * as freeRegularSvgIcons from '@fortawesome/free-regular-svg-icons'; -import * as freeSolidSvgIcons from '@fortawesome/free-solid-svg-icons'; +import { LoginComponent } from './login/login.component'; +import { NavBarComponent } from './navbar/navbar.component'; +import { EnvServiceProvider } from './services/env.service.provider'; +import { ModalService } from './services/modal.service'; +import { SidebarComponent } from './sidebar/sidebar.component'; +import { ViewerComponent } from './viewer/viewer.component'; @NgModule({ - declarations: [ - AppComponent, - AlertComponent, - LanguageSwitcherComponent, - ViewerComponent, - LoginComponent, - DeviceComponent, - ThemeComponent, - TypeComponent, - SensorLocationComponent, - DeviceTypeComponent, - OrganizationUpdateComponent, - SensorStatusComponent, - NavBarComponent, - SidebarComponent, - MapComponent, - DevicesComponent, - DatastreamComponent, - SensorComponent, - OrganizationUsersComponent, - ConfirmationModalComponent, - ObservationGoalComponent, - ObservationGoalsComponent, - OrganizationComponent, - OrganizationJoinComponent, - OrganizationCreateComponent, - OrganizationContactComponent, - ], imports: [ - BrowserModule, - AppRoutingModule, - ReactiveFormsModule, - FormsModule, - HttpClientModule, - FontAwesomeModule, - NgbModule, - AuthConfigModule, - ], providers: [ - MapService, - ModalService, - { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, - { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, - EnvServiceProvider, - ], bootstrap: [ - AppComponent, - ], + declarations: [ + AppComponent, + AlertComponent, + LanguageSwitcherComponent, + ViewerComponent, + LoginComponent, + DeviceComponent, + ThemeComponent, + TypeComponent, + SensorLocationComponent, + DeviceTypeComponent, + OrganizationUpdateComponent, + SensorStatusComponent, + NavBarComponent, + SidebarComponent, + MapComponent, + DevicesComponent, + DatastreamComponent, + SensorComponent, + OrganizationUsersComponent, + ConfirmationModalComponent, + ObservationGoalComponent, + ObservationGoalsComponent, + OrganizationComponent, + OrganizationJoinComponent, + OrganizationCreateComponent, + OrganizationContactComponent, + ], + imports: [ + BrowserModule, + AppRoutingModule, + ReactiveFormsModule, + FormsModule, + HttpClientModule, + FontAwesomeModule, + NgbModule, + AuthConfigModule, + ], + providers: [ + MapService, + ModalService, + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, + { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, + EnvServiceProvider, + ], + bootstrap: [AppComponent], }) - export class AppModule { - constructor(library: FaIconLibrary) { - // import font-awesome icons here to enable tree-shaking, making use of the "Icon Library" methodology, more info - // here: https://github.com/FortAwesome/angular-fontawesome/blob/master/docs/usage.md#methodologies - library.addIcons( - freeRegularSvgIcons.faCheckSquare, - freeSolidSvgIcons.faArrowRight, - freeSolidSvgIcons.faBullseye, - freeSolidSvgIcons.faChevronLeft, - freeSolidSvgIcons.faChevronRight, - freeSolidSvgIcons.faCity, - freeSolidSvgIcons.faEye, - freeSolidSvgIcons.faInfoCircle, - freeSolidSvgIcons.faLanguage, - freeSolidSvgIcons.faPencilAlt, - freeSolidSvgIcons.faPlus, - freeSolidSvgIcons.faSignOutAlt, - freeSolidSvgIcons.faSort, - freeSolidSvgIcons.faSortDown, - freeSolidSvgIcons.faSortUp, - freeSolidSvgIcons.faTrashAlt - ); - } + constructor(library: FaIconLibrary) { + // import font-awesome icons here to enable tree-shaking, making use of the "Icon Library" methodology, more info + // here: https://github.com/FortAwesome/angular-fontawesome/blob/master/docs/usage.md#methodologies + library.addIcons( + freeRegularSvgIcons.faCheckSquare, + freeSolidSvgIcons.faArrowRight, + freeSolidSvgIcons.faBullseye, + freeSolidSvgIcons.faChevronLeft, + freeSolidSvgIcons.faChevronRight, + freeSolidSvgIcons.faCity, + freeSolidSvgIcons.faEye, + freeSolidSvgIcons.faInfoCircle, + freeSolidSvgIcons.faLanguage, + freeSolidSvgIcons.faPencilAlt, + freeSolidSvgIcons.faPlus, + freeSolidSvgIcons.faSignOutAlt, + freeSolidSvgIcons.faSort, + freeSolidSvgIcons.faSortDown, + freeSolidSvgIcons.faSortUp, + freeSolidSvgIcons.faTrashAlt, + ); + } } diff --git a/src/app/auth/auth-config.module.ts b/src/app/auth/auth-config.module.ts index 0893362..5c70742 100644 --- a/src/app/auth/auth-config.module.ts +++ b/src/app/auth/auth-config.module.ts @@ -1,38 +1,39 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; import { AuthModule, OidcConfigService, LogLevel } from 'angular-auth-oidc-client'; -import { EnvService } from '../services/env.service'; import { environment } from '../../environments/environment'; +import { EnvService } from '../services/env.service'; export function configureAuth(oidcConfigService: OidcConfigService, envService: EnvService): () => Promise { - return () => - oidcConfigService.withConfig({ - stsServer: envService.oidcIssuer, - redirectUrl: window.location.origin, - postLogoutRedirectUri: window.location.origin, - clientId: envService.oidcClientId, - scope: 'openid profile email', - responseType: 'id_token token', - silentRenew: true, - silentRenewUrl: window.location.origin + '/silent-renew.html', - renewTimeBeforeTokenExpiresInSeconds: 10, - secureRoutes: [ // on which routes to include the id_token (our own backend) - envService.apiUrl, - ], - logLevel: environment.production ? LogLevel.None : LogLevel.Debug, - }); + return () => + oidcConfigService.withConfig({ + stsServer: envService.oidcIssuer, + redirectUrl: window.location.origin, + postLogoutRedirectUri: window.location.origin, + clientId: envService.oidcClientId, + scope: 'openid profile email', + responseType: 'id_token token', + silentRenew: true, + silentRenewUrl: window.location.origin + '/silent-renew.html', + renewTimeBeforeTokenExpiresInSeconds: 10, + secureRoutes: [ + // on which routes to include the id_token (our own backend) + envService.apiUrl, + ], + logLevel: environment.production ? LogLevel.None : LogLevel.Debug, + }); } @NgModule({ - imports: [AuthModule.forRoot()], - exports: [AuthModule], - providers: [ - OidcConfigService, - { - provide: APP_INITIALIZER, - useFactory: configureAuth, - deps: [OidcConfigService, EnvService], - multi: true, - }, - ], + imports: [AuthModule.forRoot()], + exports: [AuthModule], + providers: [ + OidcConfigService, + { + provide: APP_INITIALIZER, + useFactory: configureAuth, + deps: [OidcConfigService, EnvService], + multi: true, + }, + ], }) -export class AuthConfigModule { } +export class AuthConfigModule {} diff --git a/src/app/auth/auth.guard.ts b/src/app/auth/auth.guard.ts index efb08d0..ff2933f 100644 --- a/src/app/auth/auth.guard.ts +++ b/src/app/auth/auth.guard.ts @@ -6,20 +6,20 @@ import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { - constructor(private oidcSecurityService: OidcSecurityService, private router: Router) {} + constructor(private oidcSecurityService: OidcSecurityService, private router: Router) {} - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.oidcSecurityService.isAuthenticated$.pipe( - map((isAuthorized: boolean) => { - console.log('AuthorizationGuard, canActivate isAuthorized: ' + isAuthorized); + canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): Observable { + return this.oidcSecurityService.isAuthenticated$.pipe( + map((isAuthorized: boolean) => { + console.log('AuthorizationGuard, canActivate isAuthorized: ' + isAuthorized); - if (!isAuthorized) { - this.router.navigate(['/login']); - return false; - } + if (!isAuthorized) { + this.router.navigate(['/login']); + return false; + } - return true; - }) - ); - } + return true; + }), + ); + } } diff --git a/src/app/auth/auth.interceptor.ts b/src/app/auth/auth.interceptor.ts index c66b1b0..cd7b200 100644 --- a/src/app/auth/auth.interceptor.ts +++ b/src/app/auth/auth.interceptor.ts @@ -5,34 +5,32 @@ import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { - constructor( - private oidcSecurityService: OidcSecurityService, - ) {} + constructor(private oidcSecurityService: OidcSecurityService) {} - intercept(req: HttpRequest, next: HttpHandler): Observable> { - // Ensure we send the token only to routes which are secured - const { secureRoutes } = this.oidcSecurityService.configuration.configuration; + intercept(req: HttpRequest, next: HttpHandler): Observable> { + // Ensure we send the token only to routes which are secured + const { secureRoutes } = this.oidcSecurityService.configuration.configuration; - if (!secureRoutes) { - return next.handle(req); - } + if (!secureRoutes) { + return next.handle(req); + } - const matchingRoute = secureRoutes.find((x) => req.url.startsWith(x)); + const matchingRoute = secureRoutes.find((x) => req.url.startsWith(x)); - if (!matchingRoute) { - return next.handle(req); - } + if (!matchingRoute) { + return next.handle(req); + } - const token = this.oidcSecurityService.getIdToken(); + const token = this.oidcSecurityService.getIdToken(); - if (!token) { - return next.handle(req); - } + if (!token) { + return next.handle(req); + } - req = req.clone({ - headers: req.headers.set('Authorization', 'Bearer ' + token), - }); + req = req.clone({ + headers: req.headers.set('Authorization', 'Bearer ' + token), + }); - return next.handle(req); - } + return next.handle(req); + } } diff --git a/src/app/auth/error.interceptor.ts b/src/app/auth/error.interceptor.ts index 338e6e7..5d93cf9 100644 --- a/src/app/auth/error.interceptor.ts +++ b/src/app/auth/error.interceptor.ts @@ -7,26 +7,25 @@ import { ConnectionService } from '../services/connection.service'; @Injectable() export class ErrorInterceptor implements HttpInterceptor { - constructor( - private connectionService: ConnectionService, - ) { } + constructor(private connectionService: ConnectionService) {} - public intercept(request: HttpRequest, next: HttpHandler): Observable { - return next.handle(request) - .pipe(catchError(async (error) => { - if (error.status === 401) { - this.connectionService.logoutRedirect(); - } + public intercept(request: HttpRequest, next: HttpHandler): Observable { + return next.handle(request).pipe( + catchError(async (error) => { + if (error.status === 401) { + this.connectionService.logoutRedirect(); + } - if (error.status !== 401) { - if (error.status === 403) { - error.error.message = `You do not have the required rights to perform this operation`; - } + if (error.status !== 401) { + if (error.status === 403) { + error.error.message = `You do not have the required rights to perform this operation`; + } - throw error; - } + throw error; + } - return next.handle(request); - })); - } + return next.handle(request); + }), + ); + } } diff --git a/src/app/components/alert/alert.component.spec.ts b/src/app/components/alert/alert.component.spec.ts index 8e5104b..71126dd 100644 --- a/src/app/components/alert/alert.component.spec.ts +++ b/src/app/components/alert/alert.component.spec.ts @@ -1,24 +1,25 @@ -import { AlertComponent } from './alert.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { AlertComponent } from './alert.component'; describe('AlertComponent', () => { - let component: AlertComponent; - let fixture: ComponentFixture; + let component: AlertComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ AlertComponent ], - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AlertComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(AlertComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(AlertComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/components/alert/alert.component.ts b/src/app/components/alert/alert.component.ts index f72b50f..224ff2f 100644 --- a/src/app/components/alert/alert.component.ts +++ b/src/app/components/alert/alert.component.ts @@ -1,40 +1,37 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; import { Subscription } from 'rxjs'; import { AlertService } from '../../services/alert.service'; -import { Component, OnDestroy, OnInit } from '@angular/core'; @Component({ - selector: 'app-alert', - templateUrl: './alert.component.html', - styleUrls: ['./alert.component.scss'] + selector: 'app-alert', + templateUrl: './alert.component.html', + styleUrls: ['./alert.component.scss'], }) export class AlertComponent implements OnInit, OnDestroy { - public message: any; - private subscription: Subscription; + public message: any; + private subscription: Subscription; - constructor( - private alertService: AlertService, - ) {} + constructor(private alertService: AlertService) {} - public ngOnInit() { - this.subscription = this.alertService.getAlert() - .subscribe((message) => { - switch (message && message.type) { - case 'success': - message.cssClass = 'alert alert-success'; - break; - case 'warning': - message.cssClass = 'alert alert-warning'; - break; - case 'error': - message.cssClass = 'alert alert-danger'; - break; - } + public ngOnInit() { + this.subscription = this.alertService.getAlert().subscribe((message) => { + switch (message && message.type) { + case 'success': + message.cssClass = 'alert alert-success'; + break; + case 'warning': + message.cssClass = 'alert alert-warning'; + break; + case 'error': + message.cssClass = 'alert alert-danger'; + break; + } - this.message = message; - }); - } + this.message = message; + }); + } - public ngOnDestroy() { - this.subscription.unsubscribe(); - } + public ngOnDestroy() { + this.subscription.unsubscribe(); + } } diff --git a/src/app/components/confirmation-modal/confirmation-modal.component.ts b/src/app/components/confirmation-modal/confirmation-modal.component.ts index d94fed8..dd61c11 100644 --- a/src/app/components/confirmation-modal/confirmation-modal.component.ts +++ b/src/app/components/confirmation-modal/confirmation-modal.component.ts @@ -2,28 +2,26 @@ import { Component, Input } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @Component({ - selector: 'app--confirmation-modal', - templateUrl: './confirmation-modal.html', + selector: 'app--confirmation-modal', + templateUrl: './confirmation-modal.html', }) export class ConfirmationModalComponent { - @Input() title: string; - @Input() message: string; - @Input() btnOkText: string; - @Input() btnCancelText: string; + @Input() title: string; + @Input() message: string; + @Input() btnOkText: string; + @Input() btnCancelText: string; - constructor( - private activeModal: NgbActiveModal, - ) {} + constructor(private activeModal: NgbActiveModal) {} - public decline() { - this.activeModal.dismiss(); - } + public decline() { + this.activeModal.dismiss(); + } - public accept() { - this.activeModal.close(); - } + public accept() { + this.activeModal.close(); + } - public dismiss() { - this.activeModal.dismiss(); - } + public dismiss() { + this.activeModal.dismiss(); + } } diff --git a/src/app/components/language-switcher/language-switcher.component.ts b/src/app/components/language-switcher/language-switcher.component.ts index 9e60d4a..94844d0 100644 --- a/src/app/components/language-switcher/language-switcher.component.ts +++ b/src/app/components/language-switcher/language-switcher.component.ts @@ -2,23 +2,24 @@ import { Component } from '@angular/core'; import { Router } from '@angular/router'; @Component({ - selector: 'app-language-switcher', - templateUrl: './language-switcher.component.html', + selector: 'app-language-switcher', + templateUrl: './language-switcher.component.html', }) export class LanguageSwitcherComponent { - allLanguages = [{ - lang: 'en', - name: 'English' - }, { - lang: 'nl', - name: 'Nederlands' - }]; + allLanguages = [ + { + lang: 'en', + name: 'English', + }, + { + lang: 'nl', + name: 'Nederlands', + }, + ]; - constructor( - private router: Router, - ) {} + constructor(private router: Router) {} - getCurrentRoute() { - return this.router.url; - } + getCurrentRoute() { + return this.router.url; + } } diff --git a/src/app/components/map/map.component.ts b/src/app/components/map/map.component.ts index 223f35b..67c3fda 100644 --- a/src/app/components/map/map.component.ts +++ b/src/app/components/map/map.component.ts @@ -1,658 +1,704 @@ -import proj4 from 'proj4'; -import { Router } from '@angular/router'; import { HttpClient } from '@angular/common/http'; import { Component, ElementRef, Input, HostBinding, OnDestroy, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; -import OlMap from 'ol/Map'; +import { OidcSecurityService } from 'angular-auth-oidc-client'; +import { MapBrowserEvent, Overlay } from 'ol'; +import SelectCluster from 'ol-ext/interaction/SelectCluster'; +import AnimatedCluster from 'ol-ext/layer/AnimatedCluster'; +import LayerSwitcher from 'ol-layerswitcher'; +import OverlayPositioning from 'ol//OverlayPositioning'; +import Control from 'ol/control/Control'; +import { extend, Extent, getBottomLeft, getCenter, getTopRight } from 'ol/extent'; import Feature from 'ol/Feature'; -import Point from 'ol/geom/Point'; -import { Cluster } from 'ol/source'; -import Stroke from 'ol/style/Stroke'; -import { FitOptions } from 'ol/View'; -import { MultiPoint } from 'ol/geom'; import GeoJSON from 'ol/format/GeoJSON'; +import { MultiPoint } from 'ol/geom'; import Geometry from 'ol/geom/Geometry'; -import Control from 'ol/control/Control'; +import Point from 'ol/geom/Point'; import VectorLayer from 'ol/layer/Vector'; -import { MapBrowserEvent, Overlay } from 'ol'; +import OlMap from 'ol/Map'; +import { Cluster } from 'ol/source'; import VectorSource from 'ol/source/Vector'; -import LayerSwitcher from 'ol-layerswitcher'; -import OverlayPositioning from 'ol//OverlayPositioning'; -import AnimatedCluster from 'ol-ext/layer/AnimatedCluster'; -import SelectCluster from 'ol-ext/interaction/SelectCluster'; import { Circle as CircleStyle, Fill, Icon, Style, Text } from 'ol/style'; -import { extend, Extent, getBottomLeft, getCenter, getTopRight } from 'ol/extent'; - -import { SearchPDOK } from './searchPDOK'; -import { MapService } from './map.service'; -import { OidcSecurityService } from 'angular-auth-oidc-client'; +import Stroke from 'ol/style/Stroke'; +import { FitOptions } from 'ol/View'; +import proj4 from 'proj4'; import { IDevice } from '../../model/bodies/device-model'; +import { Category, getCategoryTranslation } from '../../model/bodies/sensorTypes'; import { AlertService } from '../../services/alert.service'; -import { ModalService } from '../../services/modal.service'; +import { ConnectionService } from '../../services/connection.service'; import { DeviceService } from '../../services/device.service'; import { LocationService } from '../../services/location.service'; -import { Category, getCategoryTranslation } from '../../model/bodies/sensorTypes'; -import { ConnectionService } from '../../services/connection.service'; - +import { ModalService } from '../../services/modal.service'; +import { MapService } from './map.service'; +import { SearchPDOK } from './searchPDOK'; @Component({ - selector: 'app-map', - templateUrl: './map.component.html', - styleUrls: ['./map.component.scss'], + selector: 'app-map', + templateUrl: './map.component.html', + styleUrls: ['./map.component.scss'], }) export class MapComponent implements OnInit, OnDestroy { + @HostBinding('style.--searchBarHeight') @Input() searchBarHeight; + @Input() clearLocationHighLight = true; + + constructor( + private router: Router, + private elementRef: ElementRef, + private mapService: MapService, + private httpClient: HttpClient, + private alertService: AlertService, + private modalService: ModalService, + private deviceService: DeviceService, + private locationService: LocationService, + private connectionService: ConnectionService, + private oidcSecurityService: OidcSecurityService, + ) {} + + public map: OlMap; + public subscriptions = []; + + public mapUpdated; + public overlayVisible = false; + public selectedDevice: IDevice; + + public getCategoryTranslation = getCategoryTranslation; + + public popupOverlay: Overlay; + public clusterSource: Cluster; + public vectorSource: VectorSource; + public highlightLayer: VectorLayer; + public selectCluster: SelectCluster; + public highlightSource: VectorSource; + public clusterLayer: AnimatedCluster; + public selectLocationLayer: VectorLayer; + public selectLocationSource: VectorSource; + + private epsgRD = + '+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 ' + + '+y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,' + + '-1.8703473836068,4.0812 +no_defs'; + private epsgWGS84 = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'; + + public hideTreeDataset = false; + + public clusterMaxZoom = 15; + + public locateMeString = $localize`:@@map.locate:Locate me`; + public confirmTitleString = $localize`:@@confirm.title:Please confirm`; + public confirmBodyString = $localize`:@@delete.device.confirm.body:Do you really want to delete the device?`; + public geoLocationNotSupportedString = $localize`:@@map.geo.support:Geolocation is not supported by this browser.`; + + private static sensorToFeatureProperties(device: IDevice) { + return { + device, + name: device.name, + canEdit: device.canEdit, + description: device.description, + category: device.category, + connectivity: device.connectivity, + locationDetails: device.locationDetails, + location: device.location, + }; + } - @HostBinding('style.--searchBarHeight') @Input() searchBarHeight; - @Input() clearLocationHighLight = true; - - constructor( - private router: Router, - private elementRef: ElementRef, - private mapService: MapService, - private httpClient: HttpClient, - private alertService: AlertService, - private modalService: ModalService, - private deviceService: DeviceService, - private locationService: LocationService, - private connectionService: ConnectionService, - private oidcSecurityService: OidcSecurityService, - ) { } - - public map: OlMap; - public subscriptions = []; - - public mapUpdated; - public overlayVisible = false; - public selectedDevice: IDevice; - - public getCategoryTranslation = getCategoryTranslation; - - public popupOverlay: Overlay; - public clusterSource: Cluster; - public vectorSource: VectorSource; - public highlightLayer: VectorLayer; - public selectCluster: SelectCluster; - public highlightSource: VectorSource; - public clusterLayer: AnimatedCluster; - public selectLocationLayer: VectorLayer; - public selectLocationSource: VectorSource; - - private epsgRD = '+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 ' + - '+y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,' + - '-1.8703473836068,4.0812 +no_defs'; - private epsgWGS84 = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'; - - public hideTreeDataset = false; - - public clusterMaxZoom = 15; - - public locateMeString = $localize`:@@map.locate:Locate me`; - public confirmTitleString = $localize`:@@confirm.title:Please confirm`; - public confirmBodyString = $localize`:@@delete.device.confirm.body:Do you really want to delete the device?`; - public geoLocationNotSupportedString = $localize`:@@map.geo.support:Geolocation is not supported by this browser.`; - - private static sensorToFeatureProperties(device: IDevice) { - return { - device, - name: device.name, - canEdit: device.canEdit, - description: device.description, - category: device.category, - connectivity: device.connectivity, - locationDetails: device.locationDetails, - location: device.location, - }; - } - - public getStyleCache() { - const styleCache = {}; - for (const item of [true, false]) { - for (const item1 of Object.keys(Category)) { - const styleName = `${item}_${item1}`; - const styleActive = [new Style({ - image: new CircleStyle({ - radius: 15, - fill: new Fill({ - color: this.getNodeColor(item, 0.9), - }), - stroke: new Stroke({ - color: '#fff', - width: 1.5 - }) - }) - }), new Style({ - image: new Icon({ - scale: 0.25, - src: `/assets/icons/${item1}_op.png` - }) - })]; - - for (const style of Object.values(styleActive)) { - style.getImage().load(); + public getStyleCache() { + const styleCache = {}; + for (const item of [true, false]) { + for (const item1 of Object.keys(Category)) { + const styleName = `${item}_${item1}`; + const styleActive = [ + new Style({ + image: new CircleStyle({ + radius: 15, + fill: new Fill({ + color: this.getNodeColor(item, 0.9), + }), + stroke: new Stroke({ + color: '#fff', + width: 1.5, + }), + }), + }), + new Style({ + image: new Icon({ + scale: 0.25, + src: `/assets/icons/${item1}_op.png`, + }), + }), + ]; + + for (const style of Object.values(styleActive)) { + style.getImage().load(); + } + + styleCache[styleName] = styleActive; + } } - styleCache[styleName] = styleActive; - } + return styleCache; } - return styleCache; - } + public initMap() { + this.map = this.mapService.getMap(); + const mapElement = this.elementRef.nativeElement.querySelector('#map'); + this.map.setTarget(mapElement); - public initMap() { - this.map = this.mapService.getMap(); - const mapElement = this.elementRef.nativeElement.querySelector('#map'); - this.map.setTarget(mapElement); + // Observe map resizes + // https://openlayers.org/en/latest/doc/faq.html#user-content-why-is-zooming-or-clicking-off-inaccurate + const sizeObserver = new ResizeObserver(() => { + this.map.updateSize(); + }); + sizeObserver.observe(mapElement); - // Observe map resizes - // https://openlayers.org/en/latest/doc/faq.html#user-content-why-is-zooming-or-clicking-off-inaccurate - const sizeObserver = new ResizeObserver(() => { - this.map.updateSize(); - }); - sizeObserver.observe(mapElement); + this.addMapEvents(); + } - this.addMapEvents(); - } + public initFeatures() { + const features: Array = new GeoJSON().readFeatures({ + features: [], + type: 'FeatureCollection', + }); + const styleCache = this.getStyleCache(); + const styleCluster = (feature) => { + let style: Style[]; + + const FEATURES_ = feature.get('features'); + const numberOfFeatures = FEATURES_.length; + if (numberOfFeatures === 1) { + const category = feature.get('features')[0].values_.category; + const ownsDevice = this.ownsDevice(feature.get('features')[0].values_.device); + + style = styleCache[`${ownsDevice}_${category}`]; + } else { + style = styleCache[numberOfFeatures]; + } + + if (!style) { + style = [ + new Style({ + image: new CircleStyle({ + radius: 15, + fill: new Fill({ + color: 'rgba(19, 65, 115, 0.9)', + }), + }), + text: new Text({ + text: numberOfFeatures.toString(), + font: 'bold 11px "Helvetica Neue", Helvetica,Arial, sans-serif', + fill: new Fill({ + color: '#ffffff', + }), + textAlign: 'center', + }), + }), + ]; + styleCache[numberOfFeatures] = style; + } + + return style; + }; + + const styleSelectedCluster = (feature) => { + const zoomLevel = this.map.getView().getZoom(); + + if ( + Object.prototype.hasOwnProperty.call(feature.values_, 'selectclusterfeature') && + zoomLevel > this.clusterMaxZoom + ) { + const category = feature.get('features')[0].values_.category; + const ownsDevice = this.ownsDevice(feature.get('features')[0].values_.device); + return styleCache[`${ownsDevice}_${category}`]; + } + }; + + this.vectorSource = new VectorSource({ + features, + }); - public initFeatures() { - const features: Array = new GeoJSON().readFeatures({ - features: [], - type: 'FeatureCollection', - }); + this.clusterSource = new Cluster({ + distance: 40, + source: this.vectorSource, + }); - const styleCache = this.getStyleCache(); - const styleCluster = (feature) => { - let style: Style[]; + this.clusterLayer = new AnimatedCluster({ + name: 'Cluster', + source: this.clusterSource, + style: styleCluster, + zIndex: 1, + }); - const FEATURES_ = feature.get('features'); - const numberOfFeatures = FEATURES_.length; - if (numberOfFeatures === 1) { - const category = feature.get('features')[0].values_.category; - const ownsDevice = this.ownsDevice(feature.get('features')[0].values_.device); + this.map.addLayer(this.clusterLayer); - style = styleCache[`${ownsDevice}_${category}`]; - } else { - style = styleCache[numberOfFeatures]; - } + this.selectCluster = new SelectCluster({ + pointRadius: 40, + style: styleCluster, + featureStyle: styleSelectedCluster, + }); + this.map.addInteraction(this.selectCluster); - if (!style) { - style = [new Style({ - image: new CircleStyle({ - radius: 15, - fill: new Fill({ - color: 'rgba(19, 65, 115, 0.9)', - }), - }), - text: new Text({ - text: numberOfFeatures.toString(), - font: 'bold 11px "Helvetica Neue", Helvetica,Arial, sans-serif', - fill: new Fill({ - color: '#ffffff', - }), - textAlign: 'center', - }), - })]; - styleCache[numberOfFeatures] = style; - } - - return style; - }; - - const styleSelectedCluster = (feature) => { - const zoomLevel = this.map.getView().getZoom(); - if (feature.values_.hasOwnProperty('selectclusterfeature') && zoomLevel > this.clusterMaxZoom) { - const category = feature.get('features')[0].values_.category; - const ownsDevice = this.ownsDevice(feature.get('features')[0].values_.device); - return styleCache[`${ownsDevice}_${category}`]; - } - }; - - this.vectorSource = new VectorSource({ - features, - }); - - this.clusterSource = new Cluster({ - distance: 40, - source: this.vectorSource - }); - - this.clusterLayer = new AnimatedCluster({ - name: 'Cluster', - source: this.clusterSource, - style: styleCluster, - zIndex: 1, - }); - - this.map.addLayer(this.clusterLayer); - - this.selectCluster = new SelectCluster({ - pointRadius: 40, - style: styleCluster, - featureStyle: styleSelectedCluster, - }); - this.map.addInteraction(this.selectCluster); - - this.popupOverlay = new Overlay({ - autoPan: false, - positioning: OverlayPositioning.BOTTOM_CENTER, - element: document.getElementById('popup') - }); - this.map.addOverlay(this.popupOverlay); - - this.selectCluster.getFeatures().on(['add'], (event) => { - this.removeHighlight(); - - const activeFeatures = event.element.get('features'); - if (activeFeatures.length === 1) { - const feature = activeFeatures[0]; - const geometry = new Feature({ - geometry: feature.values_.geometry, + this.popupOverlay = new Overlay({ + autoPan: false, + positioning: OverlayPositioning.BOTTOM_CENTER, + element: document.getElementById('popup'), + }); + this.map.addOverlay(this.popupOverlay); + + this.selectCluster.getFeatures().on(['add'], (event) => { + this.removeHighlight(); + + const activeFeatures = event.element.get('features'); + if (activeFeatures.length === 1) { + const feature = activeFeatures[0]; + const geometry = new Feature({ + geometry: feature.values_.geometry, + }); + this.selectedDevice = feature.values_.device; + this.showOverlay(feature.values_.geometry.flatCoordinates); + + this.highlightFeature(geometry); + } else if (activeFeatures.length > 1) { + this.removeHighlight(); + this.hideOverlay(); + } }); - this.selectedDevice = feature.values_.device; - this.showOverlay(feature.values_.geometry.flatCoordinates); + } + + public updateMap(devices) { + const featuresData: Array> = devices.map((device) => this.deviceToFeature(device)); + const features: Array = new GeoJSON().readFeatures({ + features: featuresData, + type: 'FeatureCollection', + }); + + this.vectorSource.clear(); + this.vectorSource.addFeatures(features); + + // TODO features do not show without removing and re-adding the layer. Check if there is a better way + this.map.removeLayer(this.clusterLayer); + this.map.addLayer(this.clusterLayer); + } + + public showOverlay(coordinates) { + this.overlayVisible = true; + this.popupOverlay.setPosition(coordinates); + this.popupOverlay.getElement().classList.remove('hidden'); + } + + public hideOverlay() { + this.overlayVisible = false; + this.popupOverlay.getElement().classList.add('hidden'); + } + + public getNodeColor(ownerType: boolean, opacity: number) { + return ownerType ? `rgba(0,160,60, ${opacity})` : `rgba(19, 65, 115, ${opacity})`; + } + + public updateDevice(updatedDevice: IDevice) { + const props = MapComponent.sensorToFeatureProperties(updatedDevice); + + const device = this.vectorSource.getFeatureById(updatedDevice._id); + if (device) { + // In case the sensor is currently visible on the map: update map. + device.setProperties(props); + const geom: Geometry = new Point( + proj4(this.epsgWGS84, this.epsgRD, [ + updatedDevice.location.coordinates[0], + updatedDevice.location.coordinates[1], + ]), + ); + device.setGeometry(geom); + this.clearLocationLayer(); + + if (this.selectedDevice && this.selectedDevice._id === updatedDevice._id) { + this.selectedDevice = updatedDevice; // In case the sensor is selected: update overlay. + } + } + } + + public deviceDeleted(deletedDevice: IDevice) { + this.locationService.hideLocationHighlight(); + const device = this.vectorSource.getFeatureById(deletedDevice._id); + if (device) { + // In case the sensor is currently visible on the map: update map. + if (this.selectedDevice && this.selectedDevice._id === deletedDevice._id) { + // In case the sensor is selected. + this.hideOverlay(); + this.selectedDevice = null; + } + this.vectorSource.removeFeature(device); + } + } + + private async onMoveEnd(event: MapBrowserEvent) { + const map = event.map; + + const currentRequestTimestamp = new Date().valueOf(); + if (!this.mapUpdated || currentRequestTimestamp - this.mapUpdated > 500) { + // In case of e.g. resizing window. + this.mapUpdated = currentRequestTimestamp; + + const extent = map.getView().calculateExtent(map.getSize()); + const topRight = proj4(this.epsgRD, this.epsgWGS84, getTopRight(extent)); + const bottomLeft = proj4(this.epsgRD, this.epsgWGS84, getBottomLeft(extent)); + + const devices = await this.deviceService.getDevices( + bottomLeft[0].toString(), + bottomLeft[1].toString(), + topRight[0].toString(), + topRight[1].toString(), + ); + + if (devices) { + this.updateMap(devices); + } + } + } + + private onSingleClick(event: MapBrowserEvent) { + const mapCoordinateRD = event.coordinate; + const mapCoordinateWGS84 = proj4(this.epsgRD, this.epsgWGS84, mapCoordinateRD); - this.highlightFeature(geometry); - } else if (activeFeatures.length > 1) { - this.removeHighlight(); - this.hideOverlay(); - } - }); - } - - public updateMap(devices) { - const featuresData: Array = devices.map((device) => this.deviceToFeature(device)); - const features: Array = new GeoJSON().readFeatures({ - features: featuresData, - type: 'FeatureCollection', - }); - - this.vectorSource.clear(); - this.vectorSource.addFeatures(features); - - // TODO features do not show without removing and re-adding the layer. Check if there is a better way - this.map.removeLayer(this.clusterLayer); - this.map.addLayer(this.clusterLayer); - } - - public showOverlay(coordinates) { - this.overlayVisible = true; - this.popupOverlay.setPosition(coordinates); - this.popupOverlay.getElement().classList.remove('hidden'); - } - - public hideOverlay() { - this.overlayVisible = false; - this.popupOverlay.getElement().classList.add('hidden'); - } - - public getNodeColor(ownerType: boolean, opacity: number) { - return ownerType ? `rgba(0,160,60, ${opacity})` : `rgba(19, 65, 115, ${opacity})`; - } - - public updateDevice(updatedDevice: IDevice) { - const props = MapComponent.sensorToFeatureProperties(updatedDevice); - - const device = this.vectorSource.getFeatureById(updatedDevice._id); - if (device) { // In case the sensor is currently visible on the map: update map. - device.setProperties(props); - const geom: Geometry = new Point(proj4(this.epsgWGS84, this.epsgRD, [ - updatedDevice.location.coordinates[0], updatedDevice.location.coordinates[1] - ])); - device.setGeometry(geom); - this.clearLocationLayer(); - - if (this.selectedDevice && this.selectedDevice._id === updatedDevice._id) { - this.selectedDevice = updatedDevice; // In case the sensor is selected: update overlay. - } - } - } - - public deviceDeleted(deletedDevice: IDevice) { - this.locationService.hideLocationHighlight(); - const device = this.vectorSource.getFeatureById(deletedDevice._id); - if (device) { // In case the sensor is currently visible on the map: update map. - if (this.selectedDevice && this.selectedDevice._id === deletedDevice._id) { // In case the sensor is selected. this.hideOverlay(); - this.selectedDevice = null; - } - this.vectorSource.removeFeature(device); - } - } - - private async onMoveEnd(event: MapBrowserEvent) { - const map = event.map; - - const currentRequestTimestamp = new Date().valueOf(); - if (!this.mapUpdated || currentRequestTimestamp - this.mapUpdated > 500) { // In case of e.g. resizing window. - this.mapUpdated = currentRequestTimestamp; - - const extent = map.getView().calculateExtent(map.getSize()); - const topRight = proj4(this.epsgRD, this.epsgWGS84, getTopRight(extent)); - const bottomLeft = proj4(this.epsgRD, this.epsgWGS84, getBottomLeft(extent)); - - const devices = await this.deviceService.getDevices(bottomLeft[0].toString(), bottomLeft[1].toString(), - topRight[0].toString(), topRight[1].toString()); - - if (devices) { - this.updateMap(devices); - } - } - } - - private onSingleClick(event: MapBrowserEvent) { - const mapCoordinateRD = event.coordinate; - const mapCoordinateWGS84 = proj4(this.epsgRD, this.epsgWGS84, mapCoordinateRD); - - this.hideOverlay(); - this.removeHighlight(); - - this.locationService.setLocation({ - type: 'Point', - coordinates: [mapCoordinateWGS84[1], mapCoordinateWGS84[0], 0], - }); - - event.map.forEachFeatureAtPixel(event.pixel, (data) => { - const features = data.getProperties().features; - - // check if feature is a cluster with multiple features - if (features.length < 2) { - return; - } - - // determine extent for new view - const extent: Extent = features[0].getGeometry().getExtent().slice(0) as Extent; - features.forEach((f: Feature) => { extend(extent, f.getGeometry().getExtent()); }); - - // if we're already zoomed in, zoom in no more. Setting maxZoom in fit() also does this to some extent, however, - // in that case the camera is also centered. Returning early here also prevents the unnecessary panning. - if (event.map.getView().getZoom() > this.clusterMaxZoom) { - return; - } - - const size = this.map.getSize(); // [width, height] - const fitOptions: FitOptions = { - duration: 1000, - maxZoom: this.clusterMaxZoom + 1, - padding: [size[1] * 0.2, size[0] * 0.2, size[1] * 0.2, size[0] * 0.2], // up, right, down, left - size, - }; - this.map.getView().fit(extent, fitOptions); - }); - } - - public addMapEvents() { - this.map.on('moveend', this.onMoveEnd.bind(this)); - this.map.on('singleclick', this.onSingleClick.bind(this)); - } - - private deviceToFeature(newDevice: IDevice): object { - return { - geometry: { - coordinates: proj4(this.epsgWGS84, this.epsgRD, - [newDevice.location.coordinates[0], newDevice.location.coordinates[1]]), - type: 'Point', - }, - id: newDevice._id, - properties: MapComponent.sensorToFeatureProperties(newDevice), - type: 'Feature', - }; - } - - private setLocation(feature: Feature) { - this.selectLocationSource = new VectorSource({ - features: [feature], - }); - this.selectLocationLayer = new VectorLayer({ - source: this.selectLocationSource, - style: new Style({ - image: new CircleStyle({ - radius: 5, - stroke: new Stroke({ - color: '#F34E15', - width: 2, - }), - fill: new Fill({ - color: '#F34E15', - }), - }), - }), - zIndex: 3, - }); - - this.map.addLayer(this.selectLocationLayer); - } - - public removeLocationFeatures() { - this.map.removeLayer(this.selectLocationLayer); - } - - public clearLocationLayer() { - this.map.removeLayer(this.selectLocationLayer); - } - - public highlightFeature(feature: Feature) { - this.map.removeLayer(this.highlightLayer); - this.highlightSource = new VectorSource({ - features: [feature], - }); - this.highlightLayer = new VectorLayer({ - source: this.highlightSource, - style: [new Style({ - image: new CircleStyle({ - radius: 20, - stroke: new Stroke({ - color: '#FF0000', - width: 2, - }), - }), - })], - opacity: 0.7, - zIndex: 2, - }); - - this.map.addLayer(this.highlightLayer); - } - - public removeHighlight() { - this.map.removeLayer(this.highlightLayer); - } - - private zoomToPoint(point: Point) { - const view = this.map.getView(); - view.fit(point, { - duration: 250, - maxZoom: 14, - }); - } - - private zoomToExtent(extent: Extent) { - const view = this.map.getView(); - const resolution = view.getResolutionForExtent(extent, this.map.getSize()); - const zoom = view.getZoomForResolution(resolution); - const center = getCenter(extent); - - setTimeout(() => { - view.animate({ - center, - zoom: Math.min(zoom, 16) - }); - }, 250); - } - - private zoomToPosition(position: GeolocationPosition) { - const coords = [position.coords.longitude, position.coords.latitude]; - const coordsRD = proj4(this.epsgWGS84, this.epsgRD, coords); - const point = new Point(coordsRD); - this.zoomToPoint(point); - } - - private zoomToGeometry(geometry: Geometry): void { - if (geometry instanceof Point) { - this.zoomToPoint(geometry); - } else { - this.zoomToExtent(geometry.getExtent()); - } - } - - private findMe() { - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition((position: GeolocationPosition) => { - this.zoomToPosition(position); - }); - } else { - alert(this.geoLocationNotSupportedString); - } - } - - private addFindMeButton() { - if (window.location.protocol !== 'https:') { - console.warn('Geolocation only allowed over secure connections'); - return; - } - - const locate = document.createElement('div'); - locate.className = 'ol-control ol-unselectable locate'; - locate.innerHTML = ``; - locate.addEventListener('click', () => { - this.findMe(); - }); - - this.map.addControl(new Control({ - element: locate, - })); - } - - private addLayerSwitcher(): void { - const layerSwitcher = new LayerSwitcher({ - reverse: true, - groupSelectStyle: 'children' - }); - - this.map.addControl(layerSwitcher); - } - - /** - * Adds a search button on the map which can be used to search for a location - * Makes use of the 'Locatieserver' of PDOK (Dutch address lookup) https://github.com/PDOK/locatieserver/wiki - */ - private addSearchButton(): void { - const search = new SearchPDOK({ - minLength: 1, - maxHistory: -1, - collapsed: false, - className: 'search-bar', - placeholder: $localize`Enter location`, - }) as any; - search.clearHistory(); - - search.on('select', (event) => { - let feature: Feature; - if (event.search instanceof Feature) { - feature = event.search; - } else { - const values = event.search.values_; - const geometry = new MultiPoint(values.geometry.flatCoordinates, values.geometry.layout); - - feature = new Feature({ - geometry, - name: values.name, - type: values.type, + this.removeHighlight(); + + this.locationService.setLocation({ + type: 'Point', + coordinates: [mapCoordinateWGS84[1], mapCoordinateWGS84[0], 0], + }); + + event.map.forEachFeatureAtPixel(event.pixel, (data) => { + const features = data.getProperties().features; + + // check if feature is a cluster with multiple features + if (features.length < 2) { + return; + } + + // determine extent for new view + const extent: Extent = features[0].getGeometry().getExtent().slice(0) as Extent; + features.forEach((f: Feature) => { + extend(extent, f.getGeometry().getExtent()); + }); + + // if we're already zoomed in, zoom in no more. Setting maxZoom in fit() also does this to some extent, however, + // in that case the camera is also centered. Returning early here also prevents the unnecessary panning. + if (event.map.getView().getZoom() > this.clusterMaxZoom) { + return; + } + + const size = this.map.getSize(); // [width, height] + const fitOptions: FitOptions = { + duration: 1000, + maxZoom: this.clusterMaxZoom + 1, + padding: [size[1] * 0.2, size[0] * 0.2, size[1] * 0.2, size[0] * 0.2], // up, right, down, left + size, + }; + this.map.getView().fit(extent, fitOptions); + }); + } + + public addMapEvents() { + this.map.on('moveend', this.onMoveEnd.bind(this)); + this.map.on('singleclick', this.onSingleClick.bind(this)); + } + + private deviceToFeature(newDevice: IDevice): Record { + return { + geometry: { + coordinates: proj4(this.epsgWGS84, this.epsgRD, [ + newDevice.location.coordinates[0], + newDevice.location.coordinates[1], + ]), + type: 'Point', + }, + id: newDevice._id, + properties: MapComponent.sensorToFeatureProperties(newDevice), + type: 'Feature', + }; + } + + private setLocation(feature: Feature) { + this.selectLocationSource = new VectorSource({ + features: [feature], + }); + this.selectLocationLayer = new VectorLayer({ + source: this.selectLocationSource, + style: new Style({ + image: new CircleStyle({ + radius: 5, + stroke: new Stroke({ + color: '#F34E15', + width: 2, + }), + fill: new Fill({ + color: '#F34E15', + }), + }), + }), + zIndex: 3, + }); + + this.map.addLayer(this.selectLocationLayer); + } + + public removeLocationFeatures() { + this.map.removeLayer(this.selectLocationLayer); + } + + public clearLocationLayer() { + this.map.removeLayer(this.selectLocationLayer); + } + + public highlightFeature(feature: Feature) { + this.map.removeLayer(this.highlightLayer); + this.highlightSource = new VectorSource({ + features: [feature], + }); + this.highlightLayer = new VectorLayer({ + source: this.highlightSource, + style: [ + new Style({ + image: new CircleStyle({ + radius: 20, + stroke: new Stroke({ + color: '#FF0000', + width: 2, + }), + }), + }), + ], + opacity: 0.7, + zIndex: 2, + }); + + this.map.addLayer(this.highlightLayer); + } + + public removeHighlight() { + this.map.removeLayer(this.highlightLayer); + } + + private zoomToPoint(point: Point) { + const view = this.map.getView(); + view.fit(point, { + duration: 250, + maxZoom: 14, + }); + } + + private zoomToExtent(extent: Extent) { + const view = this.map.getView(); + const resolution = view.getResolutionForExtent(extent, this.map.getSize()); + const zoom = view.getZoomForResolution(resolution); + const center = getCenter(extent); + + setTimeout(() => { + view.animate({ + center, + zoom: Math.min(zoom, 16), + }); + }, 250); + } + + private zoomToPosition(position: GeolocationPosition) { + const coords = [position.coords.longitude, position.coords.latitude]; + const coordsRD = proj4(this.epsgWGS84, this.epsgRD, coords); + const point = new Point(coordsRD); + this.zoomToPoint(point); + } + + private zoomToGeometry(geometry: Geometry): void { + if (geometry instanceof Point) { + this.zoomToPoint(geometry); + } else { + this.zoomToExtent(geometry.getExtent()); + } + } + + private findMe() { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition((position: GeolocationPosition) => { + this.zoomToPosition(position); + }); + } else { + alert(this.geoLocationNotSupportedString); + } + } + + private addFindMeButton() { + if (window.location.protocol !== 'https:') { + console.warn('Geolocation only allowed over secure connections'); + return; + } + + const locate = document.createElement('div'); + locate.className = 'ol-control ol-unselectable locate'; + locate.innerHTML = ``; + locate.addEventListener('click', () => { + this.findMe(); }); - } - - this.zoomToGeometry(feature.getGeometry()); - }); - - this.map.addControl(search); - } - - public ownsDevice(device): boolean { - return device.canEdit; - } - - public async editDevice(): Promise { - await this.router.navigate([`/device/${this.selectedDevice._id}`]); - } - - public async deleteDevice(): Promise { - await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then(async () => { - try { - await this.deviceService.unregister(this.selectedDevice._id); - } catch (e) { - this.alertService.error(e.error.message); - } - }, () => {}); - } - - public async ngOnInit(): Promise { - this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { - if (auth) { - this.connectionService.refreshLegalEntity(); - } - }); - - this.locationService.hideLocationMarker(); - if (this.clearLocationHighLight) { - this.locationService.hideLocationHighlight(); - } - this.initMap(); - this.initFeatures(); - - const { onLocate, onUpdate, onRemove } = await this.deviceService.subscribe(); - - this.subscriptions.push(onLocate.subscribe((newDevice: IDevice) => { - const feature: object = this.deviceToFeature(newDevice); - const newFeature = new GeoJSON().readFeature(feature); - this.vectorSource.addFeature(newFeature); - })); - - this.subscriptions.push(onUpdate.subscribe((updatedDevice: IDevice) => { - this.updateDevice(updatedDevice); - })); - - this.subscriptions.push(onRemove.subscribe((removedDevice: IDevice) => { - this.deviceDeleted(removedDevice); - })); - - this.subscriptions.push(this.locationService.showLocation$.subscribe((deviceLocation) => { - this.removeLocationFeatures(); - - if (deviceLocation) { - const locationFeature = new Feature({ - geometry: new Point(proj4(this.epsgWGS84, this.epsgRD, - [deviceLocation.coordinates[1], deviceLocation.coordinates[0]])), + + this.map.addControl( + new Control({ + element: locate, + }), + ); + } + + private addLayerSwitcher(): void { + const layerSwitcher = new LayerSwitcher({ + reverse: true, + groupSelectStyle: 'children', }); - this.setLocation(locationFeature); - } else { - this.clearLocationLayer(); - } - })); - - this.subscriptions.push(this.locationService.locationHighlight$.subscribe((deviceLocation) => { - this.removeHighlight(); - - if (deviceLocation) { - const geometry = new Feature({ - geometry: new Point(proj4(this.epsgWGS84, this.epsgRD, - [deviceLocation.coordinates[0], deviceLocation.coordinates[1]])) + + this.map.addControl(layerSwitcher); + } + + /** + * Adds a search button on the map which can be used to search for a location + * Makes use of the 'Locatieserver' of PDOK (Dutch address lookup) https://github.com/PDOK/locatieserver/wiki + */ + private addSearchButton(): void { + const search = new SearchPDOK({ + minLength: 1, + maxHistory: -1, + collapsed: false, + className: 'search-bar', + placeholder: $localize`Enter location`, + }) as any; + search.clearHistory(); + + search.on('select', (event) => { + let feature: Feature; + if (event.search instanceof Feature) { + feature = event.search; + } else { + const values = event.search.values_; + const geometry = new MultiPoint(values.geometry.flatCoordinates, values.geometry.layout); + + feature = new Feature({ + geometry, + name: values.name, + type: values.type, + }); + } + + this.zoomToGeometry(feature.getGeometry()); }); - this.highlightFeature(geometry); - } - })); + this.map.addControl(search); + } - this.addSearchButton(); - this.addLayerSwitcher(); - this.addFindMeButton(); - } + public ownsDevice(device): boolean { + return device.canEdit; + } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); + public async editDevice(): Promise { + await this.router.navigate([`/device/${this.selectedDevice._id}`]); + } - // this.map actually lives in map.service.ts. If we let it live, all layers and listeners are re-added each time the - // map component is recreated. Instead of manually keeping track of it, just kill the map and recreate it next time. - // Perhaps not an efficient way, but at least keeps this component more predictable. - this.mapService.deleteMap(); - } + public async deleteDevice(): Promise { + await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then( + async () => { + try { + await this.deviceService.unregister(this.selectedDevice._id); + } catch (e) { + this.alertService.error(e.error.message); + } + }, + () => {}, + ); + } + + public async ngOnInit(): Promise { + this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { + if (auth) { + this.connectionService.refreshLegalEntity(); + } + }); + + this.locationService.hideLocationMarker(); + if (this.clearLocationHighLight) { + this.locationService.hideLocationHighlight(); + } + this.initMap(); + this.initFeatures(); + + const { onLocate, onUpdate, onRemove } = await this.deviceService.subscribe(); + + this.subscriptions.push( + onLocate.subscribe((newDevice: IDevice) => { + const feature: Record = this.deviceToFeature(newDevice); + const newFeature = new GeoJSON().readFeature(feature); + this.vectorSource.addFeature(newFeature); + }), + ); + + this.subscriptions.push( + onUpdate.subscribe((updatedDevice: IDevice) => { + this.updateDevice(updatedDevice); + }), + ); + + this.subscriptions.push( + onRemove.subscribe((removedDevice: IDevice) => { + this.deviceDeleted(removedDevice); + }), + ); + + this.subscriptions.push( + this.locationService.showLocation$.subscribe((deviceLocation) => { + this.removeLocationFeatures(); + + if (deviceLocation) { + const locationFeature = new Feature({ + geometry: new Point( + proj4(this.epsgWGS84, this.epsgRD, [ + deviceLocation.coordinates[1], + deviceLocation.coordinates[0], + ]), + ), + }); + this.setLocation(locationFeature); + } else { + this.clearLocationLayer(); + } + }), + ); + + this.subscriptions.push( + this.locationService.locationHighlight$.subscribe((deviceLocation) => { + this.removeHighlight(); + + if (deviceLocation) { + const geometry = new Feature({ + geometry: new Point( + proj4(this.epsgWGS84, this.epsgRD, [ + deviceLocation.coordinates[0], + deviceLocation.coordinates[1], + ]), + ), + }); + + this.highlightFeature(geometry); + } + }), + ); + + this.addSearchButton(); + this.addLayerSwitcher(); + this.addFindMeButton(); + } + + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + + // this.map actually lives in map.service.ts. If we let it live, all layers and listeners are re-added each time the + // map component is recreated. Instead of manually keeping track of it, just kill the map and recreate it next time. + // Perhaps not an efficient way, but at least keeps this component more predictable. + this.mapService.deleteMap(); + } } diff --git a/src/app/components/map/map.service.ts b/src/app/components/map/map.service.ts index 27d01d0..44633f4 100644 --- a/src/app/components/map/map.service.ts +++ b/src/app/components/map/map.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import proj4 from 'proj4'; import { Map, View } from 'ol'; +import { BaseLayerOptions, GroupLayerOptions } from 'ol-layerswitcher'; import { defaults as defaultControls, ScaleLine } from 'ol/control'; import { Extent, getTopLeft } from 'ol/extent'; import { Group } from 'ol/layer'; @@ -12,163 +12,157 @@ import Projection from 'ol/proj/Projection'; import { TileWMS } from 'ol/source'; import WMTS from 'ol/source/WMTS'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; -import { BaseLayerOptions, GroupLayerOptions } from 'ol-layerswitcher'; - +import proj4 from 'proj4'; /** * Openlayers map service to acces maps by id * Inject the service in the class that have to use it and access the map with the getMap method. */ @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MapService { - extent: Extent = [-285401.92, 22598.08, 595401.92, 903401.92]; - projection: Projection; - - /** - * List of Openlayer map objects [ol.Map](https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html) - */ - private map = {}; - - constructor() { - proj4.defs('EPSG:28992', - '+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 ' + - '+y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,' + - '-1.8703473836068,4.0812 +no_defs'); - register(proj4); - - this.projection = getProjection('EPSG:28992'); - this.projection.setExtent(this.extent); - } - - private createLayers(): BaseLayer[] { - // Resoluties (pixels per meter) van de zoomniveaus: - const resolutions = [3440.640, 1720.320, 860.160, 430.080, 215.040, 107.520, 53.760, 26.880, 13.440, 6.720, 3.360, - 1.680, 0.840, 0.420, 0.210]; - // Er zijn 15 (0 tot 14) zoomniveaus beschikbaar van de WMTS-service voor de BRT-Achtergrondkaart: - const matrixIds = new Array(15); - for (let z = 0; z < 15; ++z) { - matrixIds[z] = 'EPSG:28992:' + z; + extent: Extent = [-285401.92, 22598.08, 595401.92, 903401.92]; + projection: Projection; + + /** + * List of Openlayer map objects [ol.Map](https://openlayers.org/en/latest/apidoc/module-ol_Map-Map.html) + */ + private map = {}; + + constructor() { + proj4.defs( + 'EPSG:28992', + '+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.999908 +x_0=155000 ' + + '+y_0=463000 +ellps=bessel +units=m +towgs84=565.2369,50.0087,465.658,-0.406857330322398,0.350732676542563,' + + '-1.8703473836068,4.0812 +no_defs', + ); + register(proj4); + + this.projection = getProjection('EPSG:28992'); + this.projection.setExtent(this.extent); } - const bagLayer = new TileLayer({ - title: $localize`BAG`, - visible: false, - opacity: 0.85, - source: new TileWMS({ - attributions: 'Kaartgegevens: © Kadaster', - url: 'https://geodata.nationaalgeoregister.nl/bag/wms/v1_1?', - // projection, - params: { LAYERS: 'pand', TILED: true }, - wrapX: false - }) - } as BaseLayerOptions); - - const overlayGroup = new Group({ - title: $localize`Overlays`, - layers: [ - bagLayer - ] - } as GroupLayerOptions); - - const brtLayer = new TileLayer({ - title: $localize`BRT`, - type: 'base', - visible: true, - opacity: 1, - source: new WMTS({ - attributions: 'Kaartgegevens: © Kadaster', - url: 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts?', - layer: 'brtachtergrondkaartgrijs', - matrixSet: 'EPSG:28992', - format: 'image/png', - projection: this.projection, - tileGrid: new WMTSTileGrid({ - origin: getTopLeft(this.extent), - resolutions, - matrixIds - }), - style: 'default', - wrapX: false - }) - } as BaseLayerOptions); - - const luchtfotoLayer = new TileLayer({ - title: $localize`Aerial photo`, - type: 'base', - visible: true, - source: new WMTS({ - attributions: 'Kaartgegevens: © Kadaster', - url: 'https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0?', - layer: 'Actueel_ortho25', - matrixSet: 'EPSG:28992', - format: 'image/png', - projection: this.projection, - tileGrid: new WMTSTileGrid({ - origin: getTopLeft(this.extent), - resolutions, - matrixIds - }), - style: 'default', - wrapX: false - }) - } as BaseLayerOptions); - - const baseLayers = new Group({ - title: $localize`Base maps`, - layers: [ - luchtfotoLayer, - brtLayer, - ] - } as GroupLayerOptions); - - return [ - baseLayers, - overlayGroup, - ]; - } - - private createMap(id): Map { - const map = new Map({ - controls: defaultControls().extend([ - new ScaleLine({ - units: 'metric', - }) - ]), - target: id, - view: new View({ - center: [155000, 463000], - projection: this.projection, - extent: this.extent, - zoom: 3, - }), - layers: this.createLayers(), - }); - - return map; - } - - /** - * Get a map. If it doesn't exist it will be created. - */ - getMap(id?): Map { - id = id || 'map'; - - if (!this.map[id]) { - this.map[id] = this.createMap(id); // Create map if not exist + private createLayers(): BaseLayer[] { + // Resoluties (pixels per meter) van de zoomniveaus: + const resolutions = [ + 3440.64, 1720.32, 860.16, 430.08, 215.04, 107.52, 53.76, 26.88, 13.44, 6.72, 3.36, 1.68, 0.84, 0.42, 0.21, + ]; + // Er zijn 15 (0 tot 14) zoomniveaus beschikbaar van de WMTS-service voor de BRT-Achtergrondkaart: + const matrixIds = new Array(15); + for (let z = 0; z < 15; ++z) { + matrixIds[z] = 'EPSG:28992:' + z; + } + + const bagLayer = new TileLayer({ + title: $localize`BAG`, + visible: false, + opacity: 0.85, + source: new TileWMS({ + attributions: 'Kaartgegevens: © Kadaster', + url: 'https://geodata.nationaalgeoregister.nl/bag/wms/v1_1?', + // projection, + params: { LAYERS: 'pand', TILED: true }, + wrapX: false, + }), + } as BaseLayerOptions); + + const overlayGroup = new Group({ + title: $localize`Overlays`, + layers: [bagLayer], + } as GroupLayerOptions); + + const brtLayer = new TileLayer({ + title: $localize`BRT`, + type: 'base', + visible: true, + opacity: 1, + source: new WMTS({ + attributions: 'Kaartgegevens: © Kadaster', + url: 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts?', + layer: 'brtachtergrondkaartgrijs', + matrixSet: 'EPSG:28992', + format: 'image/png', + projection: this.projection, + tileGrid: new WMTSTileGrid({ + origin: getTopLeft(this.extent), + resolutions, + matrixIds, + }), + style: 'default', + wrapX: false, + }), + } as BaseLayerOptions); + + const luchtfotoLayer = new TileLayer({ + title: $localize`Aerial photo`, + type: 'base', + visible: true, + source: new WMTS({ + attributions: 'Kaartgegevens: © Kadaster', + url: 'https://service.pdok.nl/hwh/luchtfotorgb/wmts/v1_0?', + layer: 'Actueel_ortho25', + matrixSet: 'EPSG:28992', + format: 'image/png', + projection: this.projection, + tileGrid: new WMTSTileGrid({ + origin: getTopLeft(this.extent), + resolutions, + matrixIds, + }), + style: 'default', + wrapX: false, + }), + } as BaseLayerOptions); + + const baseLayers = new Group({ + title: $localize`Base maps`, + layers: [luchtfotoLayer, brtLayer], + } as GroupLayerOptions); + + return [baseLayers, overlayGroup]; } - return this.map[id]; // return the map - } + private createMap(id): Map { + const map = new Map({ + controls: defaultControls().extend([ + new ScaleLine({ + units: 'metric', + }), + ]), + target: id, + view: new View({ + center: [155000, 463000], + projection: this.projection, + extent: this.extent, + zoom: 3, + }), + layers: this.createLayers(), + }); + + return map; + } + + /** + * Get a map. If it doesn't exist it will be created. + */ + getMap(id?): Map { + id = id || 'map'; - deleteMap(id?): void { - id = id || 'map'; + if (!this.map[id]) { + this.map[id] = this.createMap(id); // Create map if not exist + } - if (!this.map[id]) { - return; + return this.map[id]; // return the map } - delete this.map[id]; - } + deleteMap(id?): void { + id = id || 'map'; + + if (!this.map[id]) { + return; + } + + delete this.map[id]; + } } diff --git a/src/app/components/map/searchPDOK.ts b/src/app/components/map/searchPDOK.ts index a87620c..de9f860 100644 --- a/src/app/components/map/searchPDOK.ts +++ b/src/app/components/map/searchPDOK.ts @@ -1,43 +1,43 @@ -import { Feature } from 'ol'; -import { Geometry } from 'ol/geom'; -import { GeoJSON } from 'ol/format'; import { wktToGeoJSON } from '@terraformer/wkt'; +import { Feature } from 'ol'; import SearchJSON from 'ol-ext/control/SearchJSON'; +import { GeoJSON } from 'ol/format'; +import { Geometry } from 'ol/geom'; export class SearchPDOK extends SearchJSON { - - constructor(options) { - super({ - ...options, - url: 'https://geodata.nationaalgeoregister.nl/locatieserver/v3/suggest' - }); - } - - requestData(query) { - // SearchJSON doesn't allow for additional parameters by default, therefore add 'fl' here - return { - q: query, - fl: '*', - }; - } - - handleResponse(response): Feature[] { - const features: Feature[] = []; - - response.response.docs.forEach(doc => { - const geometry: Geometry = (doc.type === 'adres') ? wktToGeoJSON(doc.centroide_rd) : wktToGeoJSON(doc.geometrie_rd); - - const feature: Feature = new GeoJSON().readFeature(geometry); - feature.set('type', doc.type); - feature.set('name', doc.weergavenaam); - - features.push(feature); - }); - - return features; - } - - getTitle(feature): string { - return feature.values_.name; - } + constructor(options) { + super({ + ...options, + url: 'https://geodata.nationaalgeoregister.nl/locatieserver/v3/suggest', + }); + } + + requestData(query) { + // SearchJSON doesn't allow for additional parameters by default, therefore add 'fl' here + return { + q: query, + fl: '*', + }; + } + + handleResponse(response): Feature[] { + const features: Feature[] = []; + + response.response.docs.forEach((doc) => { + const geometry: Geometry = + doc.type === 'adres' ? wktToGeoJSON(doc.centroide_rd) : wktToGeoJSON(doc.geometrie_rd); + + const feature: Feature = new GeoJSON().readFeature(geometry); + feature.set('type', doc.type); + feature.set('name', doc.weergavenaam); + + features.push(feature); + }); + + return features; + } + + getTitle(feature): string { + return feature.values_.name; + } } diff --git a/src/app/components/observation-goal/observation-goal.component.ts b/src/app/components/observation-goal/observation-goal.component.ts index 1f52f44..ab2424e 100644 --- a/src/app/components/observation-goal/observation-goal.component.ts +++ b/src/app/components/observation-goal/observation-goal.component.ts @@ -1,127 +1,131 @@ -import {Validators, FormGroup, FormBuilder} from '@angular/forms'; import { Component, OnDestroy, OnInit } from '@angular/core'; -import {ActivatedRoute} from '@angular/router'; +import { Validators, FormGroup, FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { urlRegex } from '../../helpers/form.helpers'; +import { AlertService } from '../../services/alert.service'; import { - IObservationGoal, - IRegisterObservationGoalBody, IUpdateObservationGoalBody, - ObservationGoalService + IObservationGoal, + IRegisterObservationGoalBody, + IUpdateObservationGoalBody, + ObservationGoalService, } from '../../services/observation-goal.service'; -import { AlertService } from '../../services/alert.service'; -import { urlRegex } from '../../helpers/form.helpers'; @Component({ - selector: 'app-observation-goal', - templateUrl: './observation-goal.component.html', - styleUrls: ['./observation-goal.component.scss'], + selector: 'app-observation-goal', + templateUrl: './observation-goal.component.html', + styleUrls: ['./observation-goal.component.scss'], }) export class ObservationGoalComponent implements OnInit, OnDestroy { - public observationGoalId: string; - - public form: FormGroup; - public submitted = false; - public subscriptions = []; - - constructor( - private readonly route: ActivatedRoute, - private readonly formBuilder: FormBuilder, - private readonly alertService: AlertService, - private readonly observationGoalService: ObservationGoalService, - ) {} - - get f() { - return this.form.controls; - } - - public async saveObservationGoal() { - const observationGoalBody: IRegisterObservationGoalBody = { - name: this.form.value.name, - description: this.form.value.description, - legalGround: this.form.value.legalGround, - legalGroundLink: this.form.value.legalGroundLink, - }; - - try { - const result = await this.observationGoalService.register(observationGoalBody).toPromise() as Record; - this.alertService.success('Registered!'); - - this.form.markAsPristine(); - this.observationGoalId = result.observationGoalId; - } catch (e) { - this.alertService.error(e.error.message); - } - } + public observationGoalId: string; - public async updateObservationGoal() { - const observationGoalBody: IUpdateObservationGoalBody = {}; - if (this.form.controls.name.dirty) { - observationGoalBody.name = this.form.value.name; - } - if (this.form.controls.description.dirty) { - observationGoalBody.description = this.form.value.description; + public form: FormGroup; + public submitted = false; + public subscriptions = []; + + constructor( + private readonly route: ActivatedRoute, + private readonly formBuilder: FormBuilder, + private readonly alertService: AlertService, + private readonly observationGoalService: ObservationGoalService, + ) {} + + get f() { + return this.form.controls; } - if (this.form.controls.legalGround.dirty) { - observationGoalBody.legalGround = this.form.value.legalGround; + + public async saveObservationGoal() { + const observationGoalBody: IRegisterObservationGoalBody = { + name: this.form.value.name, + description: this.form.value.description, + legalGround: this.form.value.legalGround, + legalGroundLink: this.form.value.legalGroundLink, + }; + + try { + const result = (await this.observationGoalService.register(observationGoalBody).toPromise()) as Record< + string, + any + >; + this.alertService.success('Registered!'); + + this.form.markAsPristine(); + this.observationGoalId = result.observationGoalId; + } catch (e) { + this.alertService.error(e.error.message); + } } - if (this.form.controls.legalGroundLink.dirty) { - observationGoalBody.legalGroundLink = this.form.value.legalGroundLink; + + public async updateObservationGoal() { + const observationGoalBody: IUpdateObservationGoalBody = {}; + if (this.form.controls.name.dirty) { + observationGoalBody.name = this.form.value.name; + } + if (this.form.controls.description.dirty) { + observationGoalBody.description = this.form.value.description; + } + if (this.form.controls.legalGround.dirty) { + observationGoalBody.legalGround = this.form.value.legalGround; + } + if (this.form.controls.legalGroundLink.dirty) { + observationGoalBody.legalGroundLink = this.form.value.legalGroundLink; + } + + try { + await this.observationGoalService.update(this.observationGoalId, observationGoalBody).toPromise(); + this.alertService.success('Updated!'); + + this.form.markAsPristine(); + } catch (e) { + this.alertService.error(e.error.message); + } } - try { - await this.observationGoalService.update(this.observationGoalId, observationGoalBody).toPromise(); - this.alertService.success('Updated!'); + public async submit() { + this.submitted = true; - this.form.markAsPristine(); - } catch (e) { - this.alertService.error(e.error.message); + if (this.form.valid) { + if (this.observationGoalId) { + await this.updateObservationGoal(); + } else { + await this.saveObservationGoal(); + } + } } - } - public async submit() { - this.submitted = true; + public async setObservationGoal(observationGoal: IObservationGoal): Promise { + this.form.patchValue({ + id: observationGoal._id, + name: observationGoal.name || null, + description: observationGoal.description || null, + legalGround: observationGoal.legalGround || null, + legalGroundLink: observationGoal.legalGroundLink || null, + }); + this.form.markAsPristine(); + } - if (this.form.valid) { - if (this.observationGoalId) { - await this.updateObservationGoal(); - } else { - await this.saveObservationGoal(); - } + public ngOnInit() { + this.form = this.formBuilder.group({ + id: null, + name: [null, [Validators.required, Validators.minLength(4)]], + description: [null, [Validators.required]], + legalGround: null, + legalGroundLink: [null, [Validators.pattern(urlRegex)]], + }); + + this.subscriptions.push( + this.route.params.subscribe(async (params) => { + if (params.id) { + const observationGoal = await this.observationGoalService.get(params.id).toPromise(); + if (observationGoal) { + this.observationGoalId = params.id; + await this.setObservationGoal(observationGoal as IObservationGoal); + } + } + }), + ); } - } - - public async setObservationGoal(observationGoal: IObservationGoal): Promise { - this.form.patchValue({ - id: observationGoal._id, - name: observationGoal.name || null, - description: observationGoal.description || null, - legalGround: observationGoal.legalGround || null, - legalGroundLink: observationGoal.legalGroundLink || null, - }); - this.form.markAsPristine(); - } - - public ngOnInit() { - this.form = this.formBuilder.group({ - id: null, - name: [null, [Validators.required, Validators.minLength(4)]], - description: [null, [Validators.required]], - legalGround: null, - legalGroundLink: [null, [Validators.pattern(urlRegex)]], - }); - - this.subscriptions.push( - this.route.params.subscribe(async params => { - if (params.id) { - const observationGoal = await this.observationGoalService.get(params.id).toPromise(); - if (observationGoal) { - this.observationGoalId = params.id; - await this.setObservationGoal(observationGoal as IObservationGoal); - } - } - }) - ); - } - public ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + public ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/components/observation-goals/observation-goals.component.ts b/src/app/components/observation-goals/observation-goals.component.ts index 9237ad1..210cee7 100644 --- a/src/app/components/observation-goals/observation-goals.component.ts +++ b/src/app/components/observation-goals/observation-goals.component.ts @@ -1,106 +1,115 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { ModalService } from '../../services/modal.service'; import { AlertService } from '../../services/alert.service'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ModalService } from '../../services/modal.service'; import { IObservationGoal, ObservationGoalService } from '../../services/observation-goal.service'; @Component({ - selector: 'app-observation-goals', - templateUrl: './observation-goals.component.html', - styleUrls: ['./observation-goals.component.scss'] + selector: 'app-observation-goals', + templateUrl: './observation-goals.component.html', + styleUrls: ['./observation-goals.component.scss'], }) export class ObservationGoalsComponent implements OnInit, OnDestroy { - public subscriptions = []; - public observationGoals: IObservationGoal[] = []; + public subscriptions = []; + public observationGoals: IObservationGoal[] = []; + + public pageIndex = 0; + public pageSize = 15; - public pageIndex = 0; - public pageSize = 15; + public sortField = 'name'; + public sortDirections = { ASCENDING: 'ASCENDING', DESCENDING: 'DESCENDING' }; + public sortDirection = this.sortDirections.ASCENDING; - public sortField = 'name'; - public sortDirections = { ASCENDING: 'ASCENDING', DESCENDING: 'DESCENDING' }; - public sortDirection = this.sortDirections.ASCENDING; + public successString = $localize`:@@goal.success:Successfully removed observation goal.`; - public successString = $localize`:@@goal.success:Successfully removed observation goal.`; + public confirmTitleString = $localize`:@@remove.goal.confirm.title:Please confirm`; + public confirmBodyString = $localize`:@@remove.goal.confirm.body:Do you really want to remove the observation goal?`; - public confirmTitleString = $localize`:@@remove.goal.confirm.title:Please confirm`; - public confirmBodyString = $localize`:@@remove.goal.confirm.body:Do you really want to remove the observation goal?`; + constructor( + private readonly router: Router, + private readonly modalService: ModalService, + private readonly alertService: AlertService, + private readonly observationGoalService: ObservationGoalService, + ) {} - constructor( - private readonly router: Router, - private readonly modalService: ModalService, - private readonly alertService: AlertService, - private readonly observationGoalService: ObservationGoalService, - ) { } + public async getPreviousPage() { + if (this.pageIndex > 0) { + await this.getPage(this.pageIndex - 1); + } + } - public async getPreviousPage() { - if (this.pageIndex > 0) { - await this.getPage(this.pageIndex - 1); + public async getNextPage() { + if (this.observationGoals.length === this.pageSize) { + await this.getPage(this.pageIndex + 1); + } } - } - public async getNextPage() { - if (this.observationGoals.length === this.pageSize) { - await this.getPage(this.pageIndex + 1); + public async getPage(pageIndex) { + const goalsPromise = this.observationGoalService + .getObservationGoals({ + pageIndex, + pageSize: this.pageSize, + sortField: this.sortField, + sortDirection: this.sortDirection, + }) + .toPromise(); + this.observationGoals = (await goalsPromise) as IObservationGoal[]; + + this.pageIndex = pageIndex; } - } - - public async getPage(pageIndex) { - const goalsPromise = this.observationGoalService.getObservationGoals({ - pageIndex, pageSize: this.pageSize, sortField: this.sortField, sortDirection: this.sortDirection, - }).toPromise(); - this.observationGoals = await goalsPromise as IObservationGoal[]; - - this.pageIndex = pageIndex; - } - - public async editObservationGoal(observationGoalId: string): Promise { - await this.router.navigate([`/observationgoal/${observationGoalId}`]); - } - - public async removeObservationGoal(observationGoalId: string): Promise { - await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then(() => { - try { - this.observationGoalService.delete(observationGoalId).toPromise(); - this.getPage(this.pageIndex); - - this.alertService.success(this.successString); - } catch (e) { - this.alertService.error(e.error.message); - } - }, () => { }); - - } - - getSortClass(sortField) { - let sortClass; - if (this.sortField === sortField) { - if (this.sortDirection === this.sortDirections.ASCENDING) { - sortClass = 'sort-up'; - } else { - sortClass = 'sort-down'; - } - } else { - sortClass = 'sort'; + + public async editObservationGoal(observationGoalId: string): Promise { + await this.router.navigate([`/observationgoal/${observationGoalId}`]); } - return sortClass; - } + public async removeObservationGoal(observationGoalId: string): Promise { + await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then( + () => { + try { + this.observationGoalService.delete(observationGoalId).toPromise(); + this.getPage(this.pageIndex); + + this.alertService.success(this.successString); + } catch (e) { + this.alertService.error(e.error.message); + } + }, + () => {}, + ); + } - async setSort(sortField) { - if (sortField === this.sortField) { - this.sortDirection = (this.sortDirection === this.sortDirections.ASCENDING ? - this.sortDirections.DESCENDING : this.sortDirections.ASCENDING); + getSortClass(sortField) { + let sortClass; + if (this.sortField === sortField) { + if (this.sortDirection === this.sortDirections.ASCENDING) { + sortClass = 'sort-up'; + } else { + sortClass = 'sort-down'; + } + } else { + sortClass = 'sort'; + } + + return sortClass; } - this.sortField = sortField; - await this.getPage(this.pageIndex); - } + async setSort(sortField) { + if (sortField === this.sortField) { + this.sortDirection = + this.sortDirection === this.sortDirections.ASCENDING + ? this.sortDirections.DESCENDING + : this.sortDirections.ASCENDING; + } + + this.sortField = sortField; + await this.getPage(this.pageIndex); + } - async ngOnInit(): Promise { - await this.getPage(0); - } + async ngOnInit(): Promise { + await this.getPage(0); + } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/components/organization/organization.component.ts b/src/app/components/organization/organization.component.ts index 78ec84e..599edcf 100644 --- a/src/app/components/organization/organization.component.ts +++ b/src/app/components/organization/organization.component.ts @@ -1,76 +1,80 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ILegalEntity } from '../../model/legalEntity'; import { LegalEntityService } from '../../services/legal-entity.service'; -import {ILegalEntity} from '../../model/legalEntity'; enum UpdateView { - Join = 0, - Create = 1, + Join = 0, + Create = 1, } enum OrganizationView { - View = 0, - Users = 1, + View = 0, + Users = 1, } @Component({ - selector: 'app-organization', - templateUrl: './organization.component.html', + selector: 'app-organization', + templateUrl: './organization.component.html', }) export class OrganizationComponent implements OnInit, OnDestroy { - public legalEntityId: string; - public legalEntity: ILegalEntity; + public legalEntityId: string; + public legalEntity: ILegalEntity; - public subscriptions = []; + public subscriptions = []; - public UpdateViewEnum = UpdateView; - public OrganizationViewEnum = OrganizationView; + public UpdateViewEnum = UpdateView; + public OrganizationViewEnum = OrganizationView; - public activeUpdateView = this.UpdateViewEnum.Join; - public activeOrganizationView = this.OrganizationViewEnum.View; + public activeUpdateView = this.UpdateViewEnum.Join; + public activeOrganizationView = this.OrganizationViewEnum.View; - constructor( - private legalEntityService: LegalEntityService, - ) {} + constructor(private legalEntityService: LegalEntityService) {} - async getLegalEntity() { - return await this.legalEntityService.get().toPromise(); - } - - setLegalEntity(legalEntity) { - this.legalEntity = legalEntity; - } - - async setLegalEntityId(legalEntityId: string) { - this.legalEntityId = legalEntityId; - - if (!legalEntityId) { - this.setLegalEntity(null); - } else { - this.setLegalEntity(await this.getLegalEntity()); + async getLegalEntity() { + return await this.legalEntityService.get().toPromise(); } - } - - async ngOnInit(): Promise { - this.setLegalEntity(await this.getLegalEntity()); - const { onRegister, onUpdate, onRemove } = await this.legalEntityService.subscribe(); + setLegalEntity(legalEntity) { + this.legalEntity = legalEntity; + } - this.subscriptions.push(onRegister.subscribe((legalEntity: ILegalEntity) => { - if (this.legalEntityId === legalEntity._id) { - this.setLegalEntity(legalEntity); - } - })); + async setLegalEntityId(legalEntityId: string) { + this.legalEntityId = legalEntityId; - this.subscriptions.push(onUpdate.subscribe((legalEntity: ILegalEntity) => { - this.setLegalEntity(legalEntity); - })); + if (!legalEntityId) { + this.setLegalEntity(null); + } else { + this.setLegalEntity(await this.getLegalEntity()); + } + } - this.subscriptions.push(onRemove.subscribe(_ => { - this.setLegalEntity(null); - })); - } + async ngOnInit(): Promise { + this.setLegalEntity(await this.getLegalEntity()); + + const { onRegister, onUpdate, onRemove } = await this.legalEntityService.subscribe(); + + this.subscriptions.push( + onRegister.subscribe((legalEntity: ILegalEntity) => { + if (this.legalEntityId === legalEntity._id) { + this.setLegalEntity(legalEntity); + } + }), + ); + + this.subscriptions.push( + onUpdate.subscribe((legalEntity: ILegalEntity) => { + this.setLegalEntity(legalEntity); + }), + ); + + this.subscriptions.push( + onRemove.subscribe((_) => { + this.setLegalEntity(null); + }), + ); + } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/form-controls/datastream/datastream.component.ts b/src/app/form-controls/datastream/datastream.component.ts index 7c3bde7..742ef80 100644 --- a/src/app/form-controls/datastream/datastream.component.ts +++ b/src/app/form-controls/datastream/datastream.component.ts @@ -1,157 +1,170 @@ +import { ViewChildren, Component, forwardRef, Input } from '@angular/core'; +import { FormBuilder, FormGroup, FormArray, ControlValueAccessor, Validators, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Observable } from 'rxjs'; -import { ViewChildren } from '@angular/core'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { urlRegex } from '../../helpers/form.helpers'; -import { ModalService } from '../../services/modal.service'; import { AlertService } from '../../services/alert.service'; -import { Component, forwardRef, Input } from '@angular/core'; import { DeviceService } from '../../services/device.service'; -import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; +import { ModalService } from '../../services/modal.service'; + import { ObservationGoalService } from '../../services/observation-goal.service'; -import { FormBuilder, FormGroup, FormArray, ControlValueAccessor, Validators, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ - selector: 'app-datastream', - templateUrl: './datastream.component.html', - styleUrls: ['./datastream.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DatastreamComponent), - multi: true, - }, - ] + selector: 'app-datastream', + templateUrl: './datastream.component.html', + styleUrls: ['./datastream.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DatastreamComponent), + multi: true, + }, + ], }) export class DatastreamComponent implements ControlValueAccessor { - @Input() public deviceId: string; - @Input() public submitted: boolean; - @Input() public parentForm: FormGroup; - - @ViewChildren('observationGoals') observationGoalsElements; - - public confirmTitleString = $localize`:@@datastream.delete.confirm.title:Please confirm`; - public confirmBodyString = $localize`:@@datastream.delete.confirm.body:Do you really want to delete the datastream?`; - - constructor( - private readonly formBuilder: FormBuilder, - private readonly modalService: ModalService, - private readonly alertService: AlertService, - private readonly deviceService: DeviceService, - private readonly observationGoalService: ObservationGoalService, - ) { } - - createDatastream(): FormGroup { - return this.formBuilder.group({ - id: null, - name: [null, Validators.required], - description: null, - observedProperty: null, - theme: [], - dataQuality: null, - isActive: true, - isPublic: true, - isOpenData: true, - containsPersonalInfoData: false, - isReusable: true, - documentation: [null, [Validators.pattern(urlRegex)]], - dataLink: [null, [Validators.pattern(urlRegex)]], - observationGoals: [[]], - }); - } - - addDatastream(sensorIndex): void { - const sensors = this.parentForm.get(`sensors`) as FormArray; - const datastreams = sensors.at(sensorIndex).get(`datastreams`) as FormArray; - datastreams.push(this.createDatastream()); - } - - removeDatastream(sensorIndex, datastreamIndex): void { - const sensors = this.parentForm.get(`sensors`) as FormArray; - const sensorId = this.parentForm.get(`sensors.${sensorIndex}`).value.id; - - const datastreams = sensors.at(sensorIndex).get(`datastreams`) as FormArray; - const datastreamId = this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}`).value.id; - - this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then(() => { - if (sensorId && datastreamId) { - try { - this.deviceService.removeDatastream(this.deviceId, sensorId, datastreamId).toPromise(); - } catch (e) { - this.alertService.error(e.error.message); + @Input() public deviceId: string; + @Input() public submitted: boolean; + @Input() public parentForm: FormGroup; + + @ViewChildren('observationGoals') observationGoalsElements; + + public confirmTitleString = $localize`:@@datastream.delete.confirm.title:Please confirm`; + public confirmBodyString = $localize`:@@datastream.delete.confirm.body:Do you really want to delete the datastream?`; + + constructor( + private readonly formBuilder: FormBuilder, + private readonly modalService: ModalService, + private readonly alertService: AlertService, + private readonly deviceService: DeviceService, + private readonly observationGoalService: ObservationGoalService, + ) {} + + createDatastream(): FormGroup { + return this.formBuilder.group({ + id: null, + name: [null, Validators.required], + description: null, + observedProperty: null, + theme: [], + dataQuality: null, + isActive: true, + isPublic: true, + isOpenData: true, + containsPersonalInfoData: false, + isReusable: true, + documentation: [null, [Validators.pattern(urlRegex)]], + dataLink: [null, [Validators.pattern(urlRegex)]], + observationGoals: [[]], + }); + } + + addDatastream(sensorIndex): void { + const sensors = this.parentForm.get(`sensors`) as FormArray; + const datastreams = sensors.at(sensorIndex).get(`datastreams`) as FormArray; + datastreams.push(this.createDatastream()); + } + + removeDatastream(sensorIndex, datastreamIndex): void { + const sensors = this.parentForm.get(`sensors`) as FormArray; + const sensorId = this.parentForm.get(`sensors.${sensorIndex}`).value.id; + + const datastreams = sensors.at(sensorIndex).get(`datastreams`) as FormArray; + const datastreamId = this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}`).value.id; + + this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then( + () => { + if (sensorId && datastreamId) { + try { + this.deviceService.removeDatastream(this.deviceId, sensorId, datastreamId).toPromise(); + } catch (e) { + this.alertService.error(e.error.message); + } + } + datastreams.removeAt(datastreamIndex); + }, + () => {}, + ); + } + + public getDatastreamElement(sensorIndex, datastreamIndex, element) { + return this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}.${element}`); + } + + get value() { + return this.parentForm.controls.sensors.value; + } + + set value(value) { + if (!value) { + return; } - } - datastreams.removeAt(datastreamIndex); - }, () => { }); - } - - public getDatastreamElement(sensorIndex, datastreamIndex, element) { - return this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}.${element}`); - } - - get value() { - return this.parentForm.controls.sensors.value; - } - - set value(value) { - if (!value) { return; } - this.parentForm.controls.sensors.setValue(value); - this.onChange(value); - this.onTouched(); - } - - public onChange: any = () => { }; - public onTouched: any = () => { }; - - public registerOnChange(fn: any) { - this.onChange = fn; - } - - public registerOnTouched(fn: any) { - this.onTouched = fn; - } - - public writeValue(value) { - if (value) { - this.value = value; + this.parentForm.controls.sensors.setValue(value); + this.onChange(value); + this.onTouched(); } - } - formatObservationGoal(observationGoal: any) { - return observationGoal.name; - } + public onChange: any = () => {}; + public onTouched: any = () => {}; + + public registerOnChange(fn: any) { + this.onChange = fn; + } - addObservationGoal(sensorIndex, datastreamIndex, $e) { - $e.preventDefault(); + public registerOnTouched(fn: any) { + this.onTouched = fn; + } + + public writeValue(value) { + if (value) { + this.value = value; + } + } - const newGoal = $e.item; - const goals = this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}.observationGoals`).value; - if (!goals.some(x => x._id === newGoal._id)) { - goals.push($e.item); + formatObservationGoal(observationGoal: any) { + return observationGoal.name; } - this.observationGoalsElements.toArray().map(x => x.nativeElement.value = ''); - } + addObservationGoal(sensorIndex, datastreamIndex, $e) { + $e.preventDefault(); - removeObservationGoal(sensorIndex, datastreamIndex, item) { - const observationGoals = this.parentForm.get(`sensors.${sensorIndex}.datastreams.${datastreamIndex}.observationGoals`).value; + const newGoal = $e.item; + const goals = this.parentForm.get( + `sensors.${sensorIndex}.datastreams.${datastreamIndex}.observationGoals`, + ).value; + if (!goals.some((x) => x._id === newGoal._id)) { + goals.push($e.item); + } - let observationGoalIndex = null; - for (let i = 0; i < observationGoals.length; i++) { - if (observationGoals[i]._id === item._id) { - observationGoalIndex = i; - } + this.observationGoalsElements.toArray().map((x) => (x.nativeElement.value = '')); } - if (observationGoalIndex !== null) { - observationGoals.splice(observationGoalIndex, 1); + removeObservationGoal(sensorIndex, datastreamIndex, item) { + const observationGoals = this.parentForm.get( + `sensors.${sensorIndex}.datastreams.${datastreamIndex}.observationGoals`, + ).value; + + let observationGoalIndex = null; + for (let i = 0; i < observationGoals.length; i++) { + if (observationGoals[i]._id === item._id) { + observationGoalIndex = i; + } + } + + if (observationGoalIndex !== null) { + observationGoals.splice(observationGoalIndex, 1); + } } - } - - search = (text$: Observable) => - text$.pipe( - debounceTime(200), - distinctUntilChanged(), - switchMap(x => this.observationGoalService.getObservationGoals({ - pageIndex: 0, pageSize: 15, name: x, - })) - ) + + search = (text$: Observable) => + text$.pipe( + debounceTime(200), + distinctUntilChanged(), + switchMap((x) => + this.observationGoalService.getObservationGoals({ + pageIndex: 0, + pageSize: 15, + name: x, + }), + ), + ); } diff --git a/src/app/form-controls/device-type/device-type.component.ts b/src/app/form-controls/device-type/device-type.component.ts index dc23c2c..ff175e3 100644 --- a/src/app/form-controls/device-type/device-type.component.ts +++ b/src/app/form-controls/device-type/device-type.component.ts @@ -1,107 +1,118 @@ +import { Component, forwardRef, OnDestroy, Input, EventEmitter, Output } from '@angular/core'; +import { + NG_VALUE_ACCESSOR, + NG_VALIDATORS, + ControlValueAccessor, + FormBuilder, + FormControl, + Validators, + FormGroup, +} from '@angular/forms'; import { Subscription } from 'rxjs'; import { Category, getCategoryTranslation } from '../../model/bodies/sensorTypes'; -import { Component, forwardRef, OnDestroy, Input, EventEmitter, Output } from '@angular/core'; -import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor, FormBuilder, FormControl, Validators, - FormGroup } from '@angular/forms'; export interface SensorTypeFormValues { - category: string; - typeName: string; + category: string; + typeName: string; } @Component({ - selector: 'app-device-type', - templateUrl: './device-type.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DeviceTypeComponent), - multi: true, - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => DeviceTypeComponent), - multi: true, - }, - ], + selector: 'app-device-type', + templateUrl: './device-type.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DeviceTypeComponent), + multi: true, + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => DeviceTypeComponent), + multi: true, + }, + ], }) export class DeviceTypeComponent implements ControlValueAccessor, OnDestroy { - public form: FormGroup; - public subscriptions: Subscription[] = []; - - @Input() public submitted: boolean; - @Output() sensorType = new EventEmitter(); - - public sensorCategories = Category; - public getCategoryTranslation = getCategoryTranslation; + public form: FormGroup; + public subscriptions: Subscription[] = []; - get value(): SensorTypeFormValues { - return this.form.value; - } + @Input() public submitted: boolean; + @Output() sensorType = new EventEmitter(); - set value(value: SensorTypeFormValues) { - if (!value) { return; } - this.form.setValue(value); - this.onChange(value); - this.onTouched(); - } + public sensorCategories = Category; + public getCategoryTranslation = getCategoryTranslation; - constructor(private formBuilder: FormBuilder) { - this.form = this.formBuilder.group({ - category: new FormControl('', Validators.required), - }); + get value(): SensorTypeFormValues { + return this.form.value; + } - this.subscriptions.push( - this.form.valueChanges.subscribe((value) => { + set value(value: SensorTypeFormValues) { + if (!value) { + return; + } + this.form.setValue(value); this.onChange(value); this.onTouched(); - }), - ); + } + + constructor(private formBuilder: FormBuilder) { + this.form = this.formBuilder.group({ + category: new FormControl('', Validators.required), + }); - this.onFormChanges(); - } + this.subscriptions.push( + this.form.valueChanges.subscribe((value) => { + this.onChange(value); + this.onTouched(); + }), + ); - get f() { - return this.form.controls; - } + this.onFormChanges(); + } - private onFormChanges() { - if (!this.form.get('category')) { return; } + get f() { + return this.form.controls; + } - this.form.get('category').valueChanges.subscribe((category: Category) => { - if (category) { - this.sensorType.emit(category); - } - }); - } + private onFormChanges() { + if (!this.form.get('category')) { + return; + } - public ngOnDestroy() { - this.subscriptions.forEach((s) => s.unsubscribe()); - } + this.form.get('category').valueChanges.subscribe((category: Category) => { + if (category) { + this.sensorType.emit(category); + } + }); + } - public onChange: any = () => { }; - public onTouched: any = () => { }; + public ngOnDestroy() { + this.subscriptions.forEach((s) => s.unsubscribe()); + } - public registerOnChange(fn: any) { - this.onChange = fn; - } + public onChange: any = () => {}; + public onTouched: any = () => {}; - public writeValue(value: SensorTypeFormValues) { - if (value) { - this.value = value; + public registerOnChange(fn: any) { + this.onChange = fn; } - if (value === null) { - this.form.reset(); + public writeValue(value: SensorTypeFormValues) { + if (value) { + this.value = value; + } + + if (value === null) { + this.form.reset(); + } } - } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - // communicate the inner form validation to the parent form - public validate(_: FormControl) { - return this.form.valid ? null : { type: { valid: false } }; - } + // communicate the inner form validation to the parent form + public validate(_: FormControl) { + return this.form.valid ? null : { type: { valid: false } }; + } } diff --git a/src/app/form-controls/organization-contact/organization-contact.component.ts b/src/app/form-controls/organization-contact/organization-contact.component.ts index 1951103..df54c4a 100644 --- a/src/app/form-controls/organization-contact/organization-contact.component.ts +++ b/src/app/form-controls/organization-contact/organization-contact.component.ts @@ -3,33 +3,33 @@ import { FormGroup } from '@angular/forms'; import { supportedNames } from '../../validators/organization-mail.validator'; @Component({ - selector: 'app-organization-contact', - templateUrl: './organization-contact.component.html', + selector: 'app-organization-contact', + templateUrl: './organization-contact.component.html', }) export class OrganizationContactComponent { - @Input() public submitted: boolean; - @Input() public parentForm: FormGroup; + @Input() public submitted: boolean; + @Input() public parentForm: FormGroup; - public orString = $localize`or`; + public orString = $localize`or`; - public getSupportedDomainNamesString() { - let stringValue = ''; + public getSupportedDomainNamesString() { + let stringValue = ''; - const supportedDomainNamesLength = supportedNames.length; - for (let i = 0; i < supportedDomainNamesLength; i++) { - if (i === supportedDomainNamesLength - 1) { - stringValue += `'${supportedNames[i]}'`; - } else if (i === supportedDomainNamesLength - 2) { - stringValue += `'${supportedNames[i]}' ${this.orString} `; - } else { - stringValue += `'${supportedNames[i]}', `; - } - } + const supportedDomainNamesLength = supportedNames.length; + for (let i = 0; i < supportedDomainNamesLength; i++) { + if (i === supportedDomainNamesLength - 1) { + stringValue += `'${supportedNames[i]}'`; + } else if (i === supportedDomainNamesLength - 2) { + stringValue += `'${supportedNames[i]}' ${this.orString} `; + } else { + stringValue += `'${supportedNames[i]}', `; + } + } - return stringValue; - } + return stringValue; + } - get f() { - return this.parentForm; - } + get f() { + return this.parentForm; + } } diff --git a/src/app/form-controls/sensor-location/sensor-location.component.spec.ts b/src/app/form-controls/sensor-location/sensor-location.component.spec.ts index 01f1196..e60adae 100644 --- a/src/app/form-controls/sensor-location/sensor-location.component.spec.ts +++ b/src/app/form-controls/sensor-location/sensor-location.component.spec.ts @@ -3,23 +3,24 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { SensorLocationComponent } from './sensor-location.component'; describe('SensorLocationComponent', () => { - let component: SensorLocationComponent; - let fixture: ComponentFixture; + let component: SensorLocationComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ SensorLocationComponent ] - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SensorLocationComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(SensorLocationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(SensorLocationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/form-controls/sensor-location/sensor-location.component.ts b/src/app/form-controls/sensor-location/sensor-location.component.ts index 9ac2a1e..2cb61f6 100644 --- a/src/app/form-controls/sensor-location/sensor-location.component.ts +++ b/src/app/form-controls/sensor-location/sensor-location.component.ts @@ -1,120 +1,126 @@ +import { Component, forwardRef, OnDestroy, Input } from '@angular/core'; +import { + NG_VALUE_ACCESSOR, + NG_VALIDATORS, + ControlValueAccessor, + FormGroup, + FormBuilder, + FormControl, + Validators, +} from '@angular/forms'; import { Subscription } from 'rxjs'; import { ISensorLocation } from '../../model/bodies/location'; import { LocationService } from '../../services/location.service'; -import { Component, forwardRef, OnDestroy, Input } from '@angular/core'; -import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor, FormGroup, FormBuilder, FormControl, - Validators } from '@angular/forms'; export interface SensorLocationFormValues { - latitude: number; - longitude: number; - height: number; + latitude: number; + longitude: number; + height: number; } @Component({ - selector: 'app-sensor-location', - templateUrl: './sensor-location.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SensorLocationComponent), - multi: true, - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => SensorLocationComponent), - multi: true, - }, - ], + selector: 'app-sensor-location', + templateUrl: './sensor-location.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SensorLocationComponent), + multi: true, + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SensorLocationComponent), + multi: true, + }, + ], }) export class SensorLocationComponent implements ControlValueAccessor, OnDestroy { - public form: FormGroup; - public subscriptions: Subscription[] = []; - - public selectLocation = false; - - private location: ISensorLocation; - - @Input() public submitted: boolean; - - get f() { - return this.form.controls; - } - - get value(): SensorLocationFormValues { - return this.form.value; - } - - set value(value: SensorLocationFormValues) { - if (!value) { return; } - this.form.setValue(value); - this.onChange(value); - this.onTouched(); - } - - constructor( - private readonly locationService: LocationService, - private readonly formBuilder: FormBuilder, - ) { - this.form = this.formBuilder.group({ - height: [0, Validators.required], - latitude: [null, Validators.required], - longitude: [null, Validators.required] - }); - - this.subscriptions.push( - // any time the inner form changes update the parent of any change - this.form.valueChanges.subscribe((value) => { + public form: FormGroup; + public subscriptions: Subscription[] = []; + + public selectLocation = false; + + private location: ISensorLocation; + + @Input() public submitted: boolean; + + get f() { + return this.form.controls; + } + + get value(): SensorLocationFormValues { + return this.form.value; + } + + set value(value: SensorLocationFormValues) { + if (!value) { + return; + } + this.form.setValue(value); this.onChange(value); this.onTouched(); - }), - ); - - this.subscriptions.push( - this.locationService.location$.subscribe(location => { - if (this.selectLocation === true) { - this.location = location; - - this.form.setValue({ - height: this.form.get('height').value, - latitude: location.coordinates[0], - longitude: location.coordinates[1], - }); - - this.locationService.showLocation(location); - this.selectLocation = false; - } - }) - ); - } + } - public locationValidator(g: FormGroup) { - return g.get('latitude').value && g.get('longitude') && g.get('height') ? null : {required: true}; - } + constructor(private readonly locationService: LocationService, private readonly formBuilder: FormBuilder) { + this.form = this.formBuilder.group({ + height: [0, Validators.required], + latitude: [null, Validators.required], + longitude: [null, Validators.required], + }); + + this.subscriptions.push( + // any time the inner form changes update the parent of any change + this.form.valueChanges.subscribe((value) => { + this.onChange(value); + this.onTouched(); + }), + ); + + this.subscriptions.push( + this.locationService.location$.subscribe((location) => { + if (this.selectLocation === true) { + this.location = location; + + this.form.setValue({ + height: this.form.get('height').value, + latitude: location.coordinates[0], + longitude: location.coordinates[1], + }); + + this.locationService.showLocation(location); + this.selectLocation = false; + } + }), + ); + } + + public locationValidator(g: FormGroup) { + return g.get('latitude').value && g.get('longitude') && g.get('height') ? null : { required: true }; + } - public onChange: any = () => {}; - public onTouched: any = () => {}; + public onChange: any = () => {}; + public onTouched: any = () => {}; - public registerOnChange(fn: any) { - this.onChange = fn; - } + public registerOnChange(fn: any) { + this.onChange = fn; + } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - public writeValue(value: SensorLocationFormValues) { - if (value) { - this.value = value; + public writeValue(value: SensorLocationFormValues) { + if (value) { + this.value = value; + } } - } - // communicate the inner form validation to the parent form - public validate(_: FormControl) { - return this.form.valid ? null : { location: { valid: false } }; - } + // communicate the inner form validation to the parent form + public validate(_: FormControl) { + return this.form.valid ? null : { location: { valid: false } }; + } - public ngOnDestroy() { - this.subscriptions.forEach((s) => s.unsubscribe()); - } + public ngOnDestroy() { + this.subscriptions.forEach((s) => s.unsubscribe()); + } } diff --git a/src/app/form-controls/sensor-status/sensor-status.component.spec.ts b/src/app/form-controls/sensor-status/sensor-status.component.spec.ts index 8cda8c9..d07c3ed 100644 --- a/src/app/form-controls/sensor-status/sensor-status.component.spec.ts +++ b/src/app/form-controls/sensor-status/sensor-status.component.spec.ts @@ -3,23 +3,24 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { SensorStatusComponent } from './sensor-status.component'; describe('SensorStatusComponent', () => { - let component: SensorStatusComponent; - let fixture: ComponentFixture; + let component: SensorStatusComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ SensorStatusComponent ] - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SensorStatusComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(SensorStatusComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(SensorStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/form-controls/sensor-status/sensor-status.component.ts b/src/app/form-controls/sensor-status/sensor-status.component.ts index b4a8b05..fcdb857 100644 --- a/src/app/form-controls/sensor-status/sensor-status.component.ts +++ b/src/app/form-controls/sensor-status/sensor-status.component.ts @@ -1,94 +1,100 @@ -import { Subscription } from 'rxjs'; import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; -import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormGroup, FormBuilder, FormControl, Validators, - ControlValueAccessor } from '@angular/forms'; +import { + NG_VALUE_ACCESSOR, + NG_VALIDATORS, + FormGroup, + FormBuilder, + FormControl, + Validators, + ControlValueAccessor, +} from '@angular/forms'; +import { Subscription } from 'rxjs'; export interface SensorThemeFormValues { - value: boolean; + value: boolean; } @Component({ - selector: 'app-sensor-status', - templateUrl: './sensor-status.component.html', - styleUrls: ['./sensor-status.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SensorStatusComponent), - multi: true, - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => SensorStatusComponent), - multi: true, - }, - ], + selector: 'app-sensor-status', + templateUrl: './sensor-status.component.html', + styleUrls: ['./sensor-status.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SensorStatusComponent), + multi: true, + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => SensorStatusComponent), + multi: true, + }, + ], }) export class SensorStatusComponent implements ControlValueAccessor, OnDestroy { - public form: FormGroup; - public subscriptions: Subscription[] = []; - - @Input() public submitted: boolean; - - get value(): SensorThemeFormValues { - return this.form.value; - } + public form: FormGroup; + public subscriptions: Subscription[] = []; - set value(value: SensorThemeFormValues) { - if (!value) { return; } - this.form.setValue(value); - this.onChange(value); - this.onTouched(); - } + @Input() public submitted: boolean; - constructor( - private formBuilder: FormBuilder, - ) { - this.form = this.formBuilder.group({ - value: new FormControl(null, Validators.required), - }); + get value(): SensorThemeFormValues { + return this.form.value; + } - this.subscriptions.push( - // any time the inner form changes update the parent of any change - this.form.valueChanges.subscribe((value) => { + set value(value: SensorThemeFormValues) { + if (!value) { + return; + } + this.form.setValue(value); this.onChange(value); this.onTouched(); - }), - ); - } + } - public get f() { - return this.form.controls; - } + constructor(private formBuilder: FormBuilder) { + this.form = this.formBuilder.group({ + value: new FormControl(null, Validators.required), + }); + + this.subscriptions.push( + // any time the inner form changes update the parent of any change + this.form.valueChanges.subscribe((value) => { + this.onChange(value); + this.onTouched(); + }), + ); + } - public ngOnDestroy() { - this.subscriptions.forEach((s) => s.unsubscribe()); - } + public get f() { + return this.form.controls; + } - public onChange: any = () => { }; - public onTouched: any = () => { }; + public ngOnDestroy() { + this.subscriptions.forEach((s) => s.unsubscribe()); + } - public registerOnChange(fn: any) { - this.onChange = fn; - } + public onChange: any = () => {}; + public onTouched: any = () => {}; - public writeValue(value: SensorThemeFormValues) { - if (value) { - this.value = value; + public registerOnChange(fn: any) { + this.onChange = fn; } - if (value === null) { - this.form.reset(); + public writeValue(value: SensorThemeFormValues) { + if (value) { + this.value = value; + } + + if (value === null) { + this.form.reset(); + } } - } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - // communicate the inner form validation to the parent form - public validate(_: FormControl) { - return this.form.valid ? null : { theme: { valid: false } }; - } + // communicate the inner form validation to the parent form + public validate(_: FormControl) { + return this.form.valid ? null : { theme: { valid: false } }; + } } - diff --git a/src/app/form-controls/sensor/sensor.component.ts b/src/app/form-controls/sensor/sensor.component.ts index 465eed4..d51ee0d 100644 --- a/src/app/form-controls/sensor/sensor.component.ts +++ b/src/app/form-controls/sensor/sensor.component.ts @@ -6,95 +6,100 @@ import { DeviceService } from '../../services/device.service'; import { ModalService } from '../../services/modal.service'; @Component({ - selector: 'app-sensor', - templateUrl: './sensor.component.html', - styleUrls: ['./sensor.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SensorComponent), - multi: true, - }, - ] + selector: 'app-sensor', + templateUrl: './sensor.component.html', + styleUrls: ['./sensor.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SensorComponent), + multi: true, + }, + ], }) export class SensorComponent implements ControlValueAccessor { - @Input() public deviceId: string; - @Input() public submitted: boolean; - @Input() public parentForm: FormGroup; + @Input() public deviceId: string; + @Input() public submitted: boolean; + @Input() public parentForm: FormGroup; - public confirmTitleString = $localize`:@@sensor.delete.confirm.title:Please confirm`; - public confirmBodyString = $localize`:@@sensor.delete.confirm.body:Do you really want to delete the sensor?`; + public confirmTitleString = $localize`:@@sensor.delete.confirm.title:Please confirm`; + public confirmBodyString = $localize`:@@sensor.delete.confirm.body:Do you really want to delete the sensor?`; - constructor( - private formBuilder: FormBuilder, - private modalService: ModalService, - private readonly alertService: AlertService, - private readonly deviceService: DeviceService, - ) { } + constructor( + private formBuilder: FormBuilder, + private modalService: ModalService, + private readonly alertService: AlertService, + private readonly deviceService: DeviceService, + ) {} - createSensor(): FormGroup { - return this.formBuilder.group({ - id: null, - name: [null, Validators.required], - description: [null, Validators.required], - typeName: [null, Validators.required], - manufacturer: null, - supplier: null, - documentation: [null, [Validators.pattern(urlRegex)]], - datastreams: new FormArray([]), - }); - } + createSensor(): FormGroup { + return this.formBuilder.group({ + id: null, + name: [null, Validators.required], + description: [null, Validators.required], + typeName: [null, Validators.required], + manufacturer: null, + supplier: null, + documentation: [null, [Validators.pattern(urlRegex)]], + datastreams: new FormArray([]), + }); + } - addSensor(): void { - const sensors = this.parentForm.get('sensors') as FormArray; - sensors.push(this.createSensor()); - } + addSensor(): void { + const sensors = this.parentForm.get('sensors') as FormArray; + sensors.push(this.createSensor()); + } - async removeSensor(index): Promise { - const sensors = this.parentForm.get('sensors') as FormArray; - const sensorId = this.parentForm.get(`sensors.${index}`).value.id; + async removeSensor(index): Promise { + const sensors = this.parentForm.get('sensors') as FormArray; + const sensorId = this.parentForm.get(`sensors.${index}`).value.id; - await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then(() => { - if (sensorId) { - try { - this.deviceService.removeSensor(this.deviceId, sensorId).toPromise(); - } catch (e) { - this.alertService.error(e.error.message); - } - } - sensors.removeAt(index); - }, () => { }); - } + await this.modalService.confirm(this.confirmTitleString, this.confirmBodyString).then( + () => { + if (sensorId) { + try { + this.deviceService.removeSensor(this.deviceId, sensorId).toPromise(); + } catch (e) { + this.alertService.error(e.error.message); + } + } + sensors.removeAt(index); + }, + () => {}, + ); + } - public getSensorElement(i, elem) { - return this.parentForm.get(`sensors.${i}.${elem}`); - } + public getSensorElement(i, elem) { + return this.parentForm.get(`sensors.${i}.${elem}`); + } - get value() { - return this.parentForm.controls.sensors.value; - } + get value() { + return this.parentForm.controls.sensors.value; + } - set value(value) { - if (!value) { return; } - this.parentForm.controls.sensors.setValue(value); - this.onChange(value); - this.onTouched(); - } + set value(value) { + if (!value) { + return; + } + this.parentForm.controls.sensors.setValue(value); + this.onChange(value); + this.onTouched(); + } - public onChange: any = () => { }; - public onTouched: any = () => { }; + public onChange: any = () => {}; + public onTouched: any = () => {}; - public registerOnChange(fn: any) { - this.onChange = fn; - } + public registerOnChange(fn: any) { + this.onChange = fn; + } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - public writeValue(value) { - if (value) { - this.value = value; + public writeValue(value) { + if (value) { + this.value = value; + } } - } } diff --git a/src/app/form-controls/theme/theme.component.ts b/src/app/form-controls/theme/theme.component.ts index ec2d640..b9d3d18 100644 --- a/src/app/form-controls/theme/theme.component.ts +++ b/src/app/form-controls/theme/theme.component.ts @@ -1,108 +1,116 @@ +import { Component, forwardRef, OnDestroy, Input, AfterViewInit } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; import { Subscription } from 'rxjs'; import { SensorTheme, SensorThemeTranslation } from '../../model/bodies/sensorTheme'; -import { Component, forwardRef, OnDestroy, Input, AfterViewInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormControl, FormGroup, NG_VALIDATORS, - NG_VALUE_ACCESSOR } from '@angular/forms'; // describes what the return value of the form control will look like export interface SensorThemeFormValues { - value: SensorTheme[]; + value: SensorTheme[]; } @Component({ - selector: 'app-sensor-theme', - templateUrl: './theme.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ThemeComponent), - multi: true, - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ThemeComponent), - multi: true, - }, - ], + selector: 'app-sensor-theme', + templateUrl: './theme.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ThemeComponent), + multi: true, + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ThemeComponent), + multi: true, + }, + ], }) export class ThemeComponent implements ControlValueAccessor, OnDestroy, AfterViewInit { - public form: FormGroup; - public subscriptions: Subscription[] = []; - - @Input() public submitted: boolean; - - get value(): SensorThemeFormValues { - return this.form.value; - } - - set value(value: SensorThemeFormValues) { - if (!value) { return; } - this.form.setValue(value); - this.onChange(value); - this.onTouched(); - } - - public sensorThemes = SensorTheme; - public sensorThemesList: string[]; - public sensorThemeTranslation = SensorThemeTranslation; - - constructor( - private formBuilder: FormBuilder, - ) { - this.form = this.formBuilder.group({ - value: new FormControl([]), - }); - - this.sensorThemesList = Object.keys(this.sensorThemes).filter(String).sort((a, b) => { - return this.sensorThemeTranslation[a].localeCompare(this.sensorThemeTranslation[b]); - }); - - this.subscriptions.push( - // any time the inner form changes update the parent of any change - this.form.valueChanges.subscribe((value) => { + public form: FormGroup; + public subscriptions: Subscription[] = []; + + @Input() public submitted: boolean; + + get value(): SensorThemeFormValues { + return this.form.value; + } + + set value(value: SensorThemeFormValues) { + if (!value) { + return; + } + this.form.setValue(value); this.onChange(value); this.onTouched(); - }), - ); - } + } - public get f() { - return this.form.controls; - } + public sensorThemes = SensorTheme; + public sensorThemesList: string[]; + public sensorThemeTranslation = SensorThemeTranslation; + + constructor(private formBuilder: FormBuilder) { + this.form = this.formBuilder.group({ + value: new FormControl([]), + }); + + this.sensorThemesList = Object.keys(this.sensorThemes) + .filter(String) + .sort((a, b) => { + return this.sensorThemeTranslation[a].localeCompare(this.sensorThemeTranslation[b]); + }); + + this.subscriptions.push( + // any time the inner form changes update the parent of any change + this.form.valueChanges.subscribe((value) => { + this.onChange(value); + this.onTouched(); + }), + ); + } - public ngOnDestroy() { - this.subscriptions.forEach((s) => s.unsubscribe()); - } + public get f() { + return this.form.controls; + } - public onChange: any = () => {}; - public onTouched: any = () => {}; + public ngOnDestroy() { + this.subscriptions.forEach((s) => s.unsubscribe()); + } - public registerOnChange(fn: any) { - this.onChange = fn; - } + public onChange: any = () => {}; + public onTouched: any = () => {}; - public writeValue(value: SensorThemeFormValues) { - if (value) { - this.value = value; + public registerOnChange(fn: any) { + this.onChange = fn; } - if (value === null) { - this.form.reset(); - this.form.markAsPristine(); + public writeValue(value: SensorThemeFormValues) { + if (value) { + this.value = value; + } + + if (value === null) { + this.form.reset(); + this.form.markAsPristine(); + } + ($('.selectpicker') as any).selectpicker('refresh'); } - ($('.selectpicker') as any).selectpicker('refresh'); - } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - // communicate the inner form validation to the parent form - public validate(_: FormControl) { - return this.form.valid ? null : { theme: { valid: false } }; - } + // communicate the inner form validation to the parent form + public validate(_: FormControl) { + return this.form.valid ? null : { theme: { valid: false } }; + } - ngAfterViewInit(): void { - ($('.selectpicker') as any).selectpicker('refresh'); - } + ngAfterViewInit(): void { + ($('.selectpicker') as any).selectpicker('refresh'); + } } diff --git a/src/app/form-controls/type/type.component.ts b/src/app/form-controls/type/type.component.ts index 60917b2..01b631b 100644 --- a/src/app/form-controls/type/type.component.ts +++ b/src/app/form-controls/type/type.component.ts @@ -1,104 +1,109 @@ +import { Component, forwardRef, OnDestroy, Input, AfterViewInit, OnInit, Inject, LOCALE_ID } from '@angular/core'; + +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; import { Subscription } from 'rxjs'; import { getSensorTypesTranslation } from '../../model/bodies/sensorTypes'; -import {Component, forwardRef, OnDestroy, Input, AfterViewInit, OnInit, Inject} from '@angular/core'; -import {LOCALE_ID} from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormControl, FormGroup, NG_VALIDATORS, - NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ - selector: 'app-sensor-type', - templateUrl: './type.component.html', - styleUrls: ['./type.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TypeComponent), - multi: true, - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => TypeComponent), - multi: true, - }, - ], + selector: 'app-sensor-type', + templateUrl: './type.component.html', + styleUrls: ['./type.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TypeComponent), + multi: true, + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TypeComponent), + multi: true, + }, + ], }) export class TypeComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit { - public form: FormGroup; - public subscriptions: Subscription[] = []; - - @Input() public submitted: boolean; - - get value() { - return this.form.value; - } - - set value(value) { - if (!value) { return; } - this.form.setValue(value); - this.onChange(value); - this.onTouched(); - } - - public sensorTypes = getSensorTypesTranslation(this.locale); - - constructor( - private formBuilder: FormBuilder, - @Inject(LOCALE_ID) private locale: string, - ) { - this.form = this.formBuilder.group({ - value: new FormControl([]), - }); - - this.subscriptions.push( - // any time the inner form changes update the parent of any change - this.form.valueChanges.subscribe((value) => { + public form: FormGroup; + public subscriptions: Subscription[] = []; + + @Input() public submitted: boolean; + + get value() { + return this.form.value; + } + + set value(value) { + if (!value) { + return; + } + this.form.setValue(value); this.onChange(value); this.onTouched(); - }), - ); - } + } - public get f() { - return this.form.controls; - } + public sensorTypes = getSensorTypesTranslation(this.locale); - public ngOnDestroy() { - this.subscriptions.forEach((s) => s.unsubscribe()); - } + constructor(private formBuilder: FormBuilder, @Inject(LOCALE_ID) private locale: string) { + this.form = this.formBuilder.group({ + value: new FormControl([]), + }); - public onChange: any = () => {}; - public onTouched: any = () => {}; + this.subscriptions.push( + // any time the inner form changes update the parent of any change + this.form.valueChanges.subscribe((value) => { + this.onChange(value); + this.onTouched(); + }), + ); + } - public registerOnChange(fn: any) { - this.onChange = fn; - } + public get f() { + return this.form.controls; + } - public writeValue(value) { - if (value) { - this.value = value; + public ngOnDestroy() { + this.subscriptions.forEach((s) => s.unsubscribe()); } - if (value === null) { - this.form.reset(); - this.form.markAsPristine(); + public onChange: any = () => {}; + public onTouched: any = () => {}; + + public registerOnChange(fn: any) { + this.onChange = fn; } - ($('.selectpicker') as any).selectpicker('refresh'); - } - public registerOnTouched(fn: any) { - this.onTouched = fn; - } + public writeValue(value) { + if (value) { + this.value = value; + } - // communicate the inner form validation to the parent form - public validate(_: FormControl) { - return this.form.valid ? null : { theme: { valid: false } }; - } + if (value === null) { + this.form.reset(); + this.form.markAsPristine(); + } + ($('.selectpicker') as any).selectpicker('refresh'); + } + + public registerOnTouched(fn: any) { + this.onTouched = fn; + } - ngAfterViewInit(): void { - ($('.selectpicker') as any).selectpicker('refresh'); - } + // communicate the inner form validation to the parent form + public validate(_: FormControl) { + return this.form.valid ? null : { theme: { valid: false } }; + } - ngOnInit(): void { - ($('.selectpicker') as any).selectpicker('refresh'); - } + ngAfterViewInit(): void { + ($('.selectpicker') as any).selectpicker('refresh'); + } + + ngOnInit(): void { + ($('.selectpicker') as any).selectpicker('refresh'); + } } diff --git a/src/app/forms/device/device.component.spec.ts b/src/app/forms/device/device.component.spec.ts index c5a898e..93256d1 100644 --- a/src/app/forms/device/device.component.spec.ts +++ b/src/app/forms/device/device.component.spec.ts @@ -1,24 +1,25 @@ -import { DeviceComponent } from './device.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { DeviceComponent } from './device.component'; describe('SensorRegisterComponent', () => { - let component: DeviceComponent; - let fixture: ComponentFixture; + let component: DeviceComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ DeviceComponent ], - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [DeviceComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(DeviceComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(DeviceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/device/device.component.ts b/src/app/forms/device/device.component.ts index aa3c6eb..0cb74ce 100644 --- a/src/app/forms/device/device.component.ts +++ b/src/app/forms/device/device.component.ts @@ -7,531 +7,569 @@ import { IDevice } from '../../model/bodies/device-model'; import { getCategoryTranslation } from '../../model/bodies/sensorTypes'; import { AlertService } from '../../services/alert.service'; import { - DeviceService, IRegisterDatastreamBody, IRegisterDeviceBody, IRegisterSensorBody, IUpdateDatastreamBody, - IUpdateDeviceBody, IUpdateSensorBody + DeviceService, + IRegisterDatastreamBody, + IRegisterDeviceBody, + IRegisterSensorBody, + IUpdateDatastreamBody, + IUpdateDeviceBody, + IUpdateSensorBody, } from '../../services/device.service'; import { LocationService } from '../../services/location.service'; import { ObservationGoalService } from '../../services/observation-goal.service'; @Component({ - selector: 'app-device', - templateUrl: './device.component.html', - styleUrls: ['./device.component.scss'], + selector: 'app-device', + templateUrl: './device.component.html', + styleUrls: ['./device.component.scss'], }) export class DeviceComponent implements OnInit, OnDestroy { - public deviceId: string; - - public submitted = false; - public activeStepIndex = 0; - - public subscriptions: Subscription[] = []; - - public deviceForm: FormGroup; - public sensorForm: FormGroup; - - public formControlSteps: Array>; - - public formInvalidMessage = $localize`:@@form.register.invalid:The form is invalid`; - public saveSuccessMessage = $localize`:@@device.register.success:Saved!`; - public saveFailedMessage = $localize`:@@device.register.failure:An error has occurred during saving:`; - - public saveTitleString = $localize`:@@step.confirm.title:Save the step!`; - public saveBodyString = $localize`:@@step.confirm.body:You need to save before continuing`; - - constructor( - private readonly route: ActivatedRoute, - private readonly formBuilder: FormBuilder, - private readonly alertService: AlertService, - private readonly deviceService: DeviceService, - private readonly locationService: LocationService, - private readonly observationGoalService: ObservationGoalService, - ) {} - - get deviceControls() { - return this.deviceForm.controls; - } - - public setNameFromCategory(item: string) { - this.deviceForm.controls.name.setValue(getCategoryTranslation(item)); - } - - public async goToStep(step: number): Promise { - if (this.formControlSteps[this.activeStepIndex].some(x => x.invalid)) { - this.submitted = true; - } else { - this.submitted = false; - - if (step !== this.activeStepIndex) { - if (this.activeStepIndex === 0) { - if (this.deviceId) { - this.activeStepIndex = step; - } else { - this.alertService.warning(this.saveBodyString); - } - } else if (this.activeStepIndex === 1) { - const sensors = this.sensorForm.get('sensors') as FormArray; - const allRegistered = sensors.controls.every(x => x.value.id); - - if (allRegistered) { - this.activeStepIndex = step; - } else { - this.alertService.warning(this.saveBodyString); - } - } else if (this.activeStepIndex === 2) { - let allRegistered = true; - - const sensors = this.sensorForm.get('sensors') as FormArray; - for (const sensorEntry of sensors.controls) { - const datastreams = sensorEntry.get('datastreams') as FormArray; - for (const datastreamForm of datastreams.controls) { - if (!datastreamForm.value.id) { - allRegistered = false; - } - } - } + public deviceId: string; - if (allRegistered) { - this.activeStepIndex = step; - } else { - this.alertService.warning(this.saveBodyString); - } - } - } - } - } + public submitted = false; + public activeStepIndex = 0; - public async submitDevice() { - this.submitted = true; + public subscriptions: Subscription[] = []; - if (!this.deviceForm.valid) { - this.alertService.error(this.formInvalidMessage); - return; - } + public deviceForm: FormGroup; + public sensorForm: FormGroup; - await this.saveDevice(); - } + public formControlSteps: Array>; - public async submitSensors() { - this.submitted = true; + public formInvalidMessage = $localize`:@@form.register.invalid:The form is invalid`; + public saveSuccessMessage = $localize`:@@device.register.success:Saved!`; + public saveFailedMessage = $localize`:@@device.register.failure:An error has occurred during saving:`; - if (!this.sensorForm.valid) { - this.alertService.error(this.formInvalidMessage); - return; - } + public saveTitleString = $localize`:@@step.confirm.title:Save the step!`; + public saveBodyString = $localize`:@@step.confirm.body:You need to save before continuing`; - await this.registerSensors(); - } + constructor( + private readonly route: ActivatedRoute, + private readonly formBuilder: FormBuilder, + private readonly alertService: AlertService, + private readonly deviceService: DeviceService, + private readonly locationService: LocationService, + private readonly observationGoalService: ObservationGoalService, + ) {} - public async submitDatastreams() { - this.submitted = true; + get deviceControls() { + return this.deviceForm.controls; + } - if (!this.sensorForm.valid) { - this.alertService.error(this.formInvalidMessage); - return; + public setNameFromCategory(item: string) { + this.deviceForm.controls.name.setValue(getCategoryTranslation(item)); } - await this.registerDatastreams(); - } - - public getStepCount(): number { - return this.formControlSteps.length; - } - - public getStepClasses(pageIndex: number) { - return { active: this.activeStepIndex === pageIndex, finished: this.activeStepIndex > pageIndex }; - } - - public async setDevice(device: IDevice): Promise { - this.locationService.highlightLocation({ - type: 'Point', - coordinates: device.location.coordinates - }); - - const location = device.location; - const longitude = location.coordinates.length > 0 ? location.coordinates[0] : null; - const latitude = location.coordinates.length > 1 ? location.coordinates[1] : null; - const height = location.coordinates.length > 2 ? location.coordinates[2] : null; - - this.deviceForm.patchValue({ - id: device._id, - category: { category: device.category || null }, - name: device.name || null, - connectivity: device.connectivity || null, - description: device.description || null, - location: { longitude, latitude, height }, - locationName: device.locationDetails && device.locationDetails.name ? device.locationDetails.name : null, - locationDescription: device.locationDetails && device.locationDetails.description ? device.locationDetails.description : null, - }); - this.deviceForm.markAsPristine(); - - const sensors = this.sensorForm.get('sensors') as FormArray; - sensors.clear(); - - for (const sensor of device.sensors) { - const datastreams = new FormArray([]); - if (device.datastreams) { - for (const datastream of device.datastreams) { - if (datastream.sensorId === sensor._id) { - const observationGoals = []; - if (datastream.observationGoalIds) { - for (const observationGoalId of datastream.observationGoalIds) { - const observationGoal = await this.observationGoalService.get(observationGoalId).toPromise(); - if (observationGoal) { - observationGoals.push(observationGoal); + public async goToStep(step: number): Promise { + if (this.formControlSteps[this.activeStepIndex].some((x) => x.invalid)) { + this.submitted = true; + } else { + this.submitted = false; + + if (step !== this.activeStepIndex) { + if (this.activeStepIndex === 0) { + if (this.deviceId) { + this.activeStepIndex = step; + } else { + this.alertService.warning(this.saveBodyString); + } + } else if (this.activeStepIndex === 1) { + const sensors = this.sensorForm.get('sensors') as FormArray; + const allRegistered = sensors.controls.every((x) => x.value.id); + + if (allRegistered) { + this.activeStepIndex = step; + } else { + this.alertService.warning(this.saveBodyString); + } + } else if (this.activeStepIndex === 2) { + let allRegistered = true; + + const sensors = this.sensorForm.get('sensors') as FormArray; + for (const sensorEntry of sensors.controls) { + const datastreams = sensorEntry.get('datastreams') as FormArray; + for (const datastreamForm of datastreams.controls) { + if (!datastreamForm.value.id) { + allRegistered = false; + } + } + } + + if (allRegistered) { + this.activeStepIndex = step; + } else { + this.alertService.warning(this.saveBodyString); + } } - } } - - datastreams.push(this.formBuilder.group({ - id: datastream._id, - name: [datastream.name, Validators.required], - description: datastream.description, - theme: datastream.theme ? {value: datastream.theme} : null, - dataQuality: datastream.dataQuality, - isActive: !!datastream.isActive, - isPublic: !!datastream.isPublic, - isOpenData: !!datastream.isOpenData, - containsPersonalInfoData: !!datastream.containsPersonalInfoData, - isReusable: !!datastream.isReusable, - documentation: datastream.documentation, - dataLink: datastream.dataLink, - observationGoals: [observationGoals], - })); - } } - } - - sensors.push(this.formBuilder.group({ - id: sensor._id, - name: [sensor.name, Validators.required], - description: [sensor.description, Validators.required], - typeName: [sensor.type ? {value: sensor.type} : null, Validators.required], - manufacturer: sensor.manufacturer, - supplier: sensor.supplier, - documentation: [sensor.documentation, [Validators.pattern(urlRegex)]], - datastreams, - })); - this.sensorForm.markAsPristine(); } - } - - public async saveDevice() { - if (this.deviceForm.value.id) { - const deviceUpdate: Record = {}; - if (this.deviceForm.controls.name.dirty) { - deviceUpdate.name = this.deviceForm.value.name; - } - if (this.deviceForm.controls.description.dirty) { - deviceUpdate.description = this.deviceForm.value.description; - } - if (this.deviceForm.controls.category.dirty) { - deviceUpdate.category = this.deviceForm.value.category.category; - } - if (this.deviceForm.controls.connectivity.dirty) { - deviceUpdate.connectivity = this.deviceForm.value.connectivity; - } - const location: Record = {}; - if (this.deviceForm.controls.locationName.dirty) { - location.name = this.deviceForm.value.locationName; - } - if (this.deviceForm.controls.locationDescription.dirty) { - location.description = this.deviceForm.value.locationDescription; - } - if (this.deviceForm.controls.location.dirty) { - const deviceLocation = this.deviceForm.value.location; - location.location = [deviceLocation.longitude, deviceLocation.latitude, deviceLocation.height]; - } - if (Object.keys(location).length) { - deviceUpdate.location = location; - } - - try { - if (Object.keys(deviceUpdate).length) { - await this.deviceService.update(this.deviceForm.value.id, deviceUpdate as IUpdateDeviceBody).toPromise(); - this.deviceForm.markAsPristine(); - this.alertService.success(this.saveSuccessMessage); + + public async submitDevice() { + this.submitted = true; + + if (!this.deviceForm.valid) { + this.alertService.error(this.formInvalidMessage); + return; } - } catch (e) { - this.alertService.error(e.error.message); - } - - this.submitted = false; - } else { - const deviceLocation = this.deviceForm.value.location; - - const device: IRegisterDeviceBody = { - name: this.deviceForm.value.name, - description: this.deviceForm.value.description, - category: this.deviceForm.value.category.category, - connectivity: this.deviceForm.value.connectivity, - location: { - name: this.deviceForm.value.locationName, - description: this.deviceForm.value.locationDescription, - location: deviceLocation ? [deviceLocation.longitude, deviceLocation.latitude, deviceLocation.height] : null, - }, - }; - - try { - const deviceDetails: Record = await this.deviceService.register(device).toPromise(); - this.deviceId = deviceDetails.deviceId; - this.deviceForm.markAsPristine(); - this.locationService.showLocation(null); - this.alertService.success(this.saveSuccessMessage); - this.submitted = false; - } catch (e) { - this.alertService.error(`${this.saveFailedMessage} ${e.error.message}.`); - } + await this.saveDevice(); } - } - - public async updateObservationGoals(sensorId, datastreamId, observationGoalIds) { - const device = await this.deviceService.get(this.deviceId).toPromise() as IDevice; - const deviceDatastreams = device && device.datastreams ? device.datastreams.filter( - x => x._id === datastreamId) : []; - - const promises = []; - if (deviceDatastreams.length) { - const existingObservationGoalIds = deviceDatastreams[0].observationGoalIds; - for (const observationGoalId of observationGoalIds) { - if (!existingObservationGoalIds || !existingObservationGoalIds.includes(observationGoalId)) { - promises.push(this.deviceService.linkObservationGoal(this.deviceId, sensorId, datastreamId, - observationGoalId).toPromise()); - } - } - if (existingObservationGoalIds) { - for (const existingObservationGoalId of existingObservationGoalIds) { - if (!observationGoalIds.includes(existingObservationGoalId)) { - promises.push(this.deviceService.unlinkObservationGoal(this.deviceId, sensorId, datastreamId, - existingObservationGoalId).toPromise()); - } + + public async submitSensors() { + this.submitted = true; + + if (!this.sensorForm.valid) { + this.alertService.error(this.formInvalidMessage); + return; } - } + + await this.registerSensors(); } - await Promise.all(promises); - } - - public async registerSensors() { - const sensors = this.sensorForm.get('sensors') as FormArray; - - let failed = false; - for (const sensorEntry of sensors.controls) { - const sensorEntryValue = sensorEntry.value; - if (!sensorEntryValue.id) { - const sensor: IRegisterSensorBody = { - name: sensorEntryValue.name, - description: sensorEntryValue.description, - type: sensorEntryValue.typeName.value, - manufacturer: sensorEntryValue.manufacturer, - supplier: sensorEntryValue.supplier, - documentation: sensorEntryValue.documentation, - }; - - try { - const sensorResult: Record = await this.deviceService.registerSensor(this.deviceId, sensor).toPromise(); - sensorEntry.patchValue({id: sensorResult.sensorId}); - } catch (e) { - failed = true; - this.alertService.error(e.error.message); - } - } else { - const sensorUpdate: Record = {}; - if (sensorEntry.get('name').dirty) { - sensorUpdate.name = sensorEntryValue.name; - } - if (sensorEntry.get('description').dirty) { - sensorUpdate.description = sensorEntryValue.description; - } - if (sensorEntry.get('typeName').dirty) { - sensorUpdate.type = sensorEntryValue.typeName.value; - } - if (sensorEntry.get('manufacturer').dirty) { - sensorUpdate.manufacturer = sensorEntryValue.manufacturer; - } - if (sensorEntry.get('supplier').dirty) { - sensorUpdate.supplier = sensorEntryValue.supplier; - } - if (sensorEntry.get('documentation').dirty) { - sensorUpdate.documentation = sensorEntryValue.documentation; - } + public async submitDatastreams() { + this.submitted = true; - try { - if (Object.keys(sensorUpdate).length) { - await this.deviceService.updateSensor(this.deviceId, sensorEntryValue.id, - sensorUpdate as IUpdateSensorBody).toPromise(); - this.sensorForm.markAsPristine(); - this.alertService.success(this.saveSuccessMessage); - } - } catch (e) { - this.alertService.error(e.error.message); + if (!this.sensorForm.valid) { + this.alertService.error(this.formInvalidMessage); + return; } - this.submitted = false; - } + await this.registerDatastreams(); } - if (!failed) { - this.alertService.success(this.saveSuccessMessage); + public getStepCount(): number { + return this.formControlSteps.length; } - this.submitted = false; - } - - public async registerDatastreams() { - const sensors = this.sensorForm.get('sensors') as FormArray; - - let failed = false; - for (const sensorEntry of sensors.controls) { - const sensorId = sensorEntry.value.id; - - const datastreams = sensorEntry.get('datastreams') as FormArray; - for (const datastreamEntry of datastreams.controls) { - const datastreamFormValue = datastreamEntry.value; - const datastream: IRegisterDatastreamBody = { - name: datastreamFormValue.name, - description: datastreamFormValue.description, - theme: datastreamFormValue.theme ? datastreamFormValue.theme.value : null, - dataQuality: datastreamFormValue.dataQuality, - isActive: datastreamFormValue.isActive, - isPublic: datastreamFormValue.isPublic, - isOpenData: datastreamFormValue.isOpenData, - containsPersonalInfoData: datastreamFormValue.containsPersonalInfoData, - isReusable: datastreamFormValue.isReusable, - documentation: datastreamFormValue.documentation, - dataLink: datastreamFormValue.dataLink, - }; - - try { - const datastreamId = datastreamEntry.value.id; - if (sensorId && !datastreamId) { - try { - const datastreamResult: Record = await this.deviceService.registerDatastream(this.deviceId, - sensorId, datastream).toPromise(); - datastreamEntry.patchValue({id: datastreamResult.datastreamId}); - - if (datastreamFormValue.observationGoals) { - for (const observationGoal of datastreamFormValue.observationGoals) { - await this.deviceService.linkObservationGoal(this.deviceId, sensorId, datastreamResult.datastreamId, - observationGoal._id).toPromise(); + public getStepClasses(pageIndex: number) { + return { active: this.activeStepIndex === pageIndex, finished: this.activeStepIndex > pageIndex }; + } + + public async setDevice(device: IDevice): Promise { + this.locationService.highlightLocation({ + type: 'Point', + coordinates: device.location.coordinates, + }); + + const location = device.location; + const longitude = location.coordinates.length > 0 ? location.coordinates[0] : null; + const latitude = location.coordinates.length > 1 ? location.coordinates[1] : null; + const height = location.coordinates.length > 2 ? location.coordinates[2] : null; + + this.deviceForm.patchValue({ + id: device._id, + category: { category: device.category || null }, + name: device.name || null, + connectivity: device.connectivity || null, + description: device.description || null, + location: { longitude, latitude, height }, + locationName: device.locationDetails && device.locationDetails.name ? device.locationDetails.name : null, + locationDescription: + device.locationDetails && device.locationDetails.description + ? device.locationDetails.description + : null, + }); + this.deviceForm.markAsPristine(); + + const sensors = this.sensorForm.get('sensors') as FormArray; + sensors.clear(); + + for (const sensor of device.sensors) { + const datastreams = new FormArray([]); + if (device.datastreams) { + for (const datastream of device.datastreams) { + if (datastream.sensorId === sensor._id) { + const observationGoals = []; + if (datastream.observationGoalIds) { + for (const observationGoalId of datastream.observationGoalIds) { + const observationGoal = await this.observationGoalService + .get(observationGoalId) + .toPromise(); + if (observationGoal) { + observationGoals.push(observationGoal); + } + } + } + + datastreams.push( + this.formBuilder.group({ + id: datastream._id, + name: [datastream.name, Validators.required], + description: datastream.description, + theme: datastream.theme ? { value: datastream.theme } : null, + dataQuality: datastream.dataQuality, + isActive: !!datastream.isActive, + isPublic: !!datastream.isPublic, + isOpenData: !!datastream.isOpenData, + containsPersonalInfoData: !!datastream.containsPersonalInfoData, + isReusable: !!datastream.isReusable, + documentation: datastream.documentation, + dataLink: datastream.dataLink, + observationGoals: [observationGoals], + }), + ); + } } - } - } catch (e) { - failed = true; - this.alertService.error(e.error.message); } - } else { - const datastreamUpdate: Record = {}; - if (datastreamEntry.get('name').dirty) { - datastreamUpdate.name = datastreamFormValue.name; + + sensors.push( + this.formBuilder.group({ + id: sensor._id, + name: [sensor.name, Validators.required], + description: [sensor.description, Validators.required], + typeName: [sensor.type ? { value: sensor.type } : null, Validators.required], + manufacturer: sensor.manufacturer, + supplier: sensor.supplier, + documentation: [sensor.documentation, [Validators.pattern(urlRegex)]], + datastreams, + }), + ); + this.sensorForm.markAsPristine(); + } + } + + public async saveDevice() { + if (this.deviceForm.value.id) { + const deviceUpdate: Record = {}; + if (this.deviceForm.controls.name.dirty) { + deviceUpdate.name = this.deviceForm.value.name; } - if (datastreamEntry.get('description').dirty) { - datastreamUpdate.description = datastreamFormValue.description; + if (this.deviceForm.controls.description.dirty) { + deviceUpdate.description = this.deviceForm.value.description; } - if (datastreamEntry.get('theme').dirty) { - datastreamUpdate.theme = datastreamFormValue.theme ? datastreamFormValue.theme.value : null; + if (this.deviceForm.controls.category.dirty) { + deviceUpdate.category = this.deviceForm.value.category.category; } - if (datastreamEntry.get('dataQuality').dirty) { - datastreamUpdate.dataQuality = datastreamFormValue.dataQuality; + if (this.deviceForm.controls.connectivity.dirty) { + deviceUpdate.connectivity = this.deviceForm.value.connectivity; } - if (datastreamEntry.get('isActive').dirty) { - datastreamUpdate.isActive = datastreamFormValue.isActive; + const location: Record = {}; + if (this.deviceForm.controls.locationName.dirty) { + location.name = this.deviceForm.value.locationName; } - if (datastreamEntry.get('isPublic').dirty) { - datastreamUpdate.isPublic = datastreamFormValue.isPublic; + if (this.deviceForm.controls.locationDescription.dirty) { + location.description = this.deviceForm.value.locationDescription; } - if (datastreamEntry.get('isOpenData').dirty) { - datastreamUpdate.isOpenData = datastreamFormValue.isOpenData; + if (this.deviceForm.controls.location.dirty) { + const deviceLocation = this.deviceForm.value.location; + location.location = [deviceLocation.longitude, deviceLocation.latitude, deviceLocation.height]; } - if (datastreamEntry.get('containsPersonalInfoData').dirty) { - datastreamUpdate.containsPersonalInfoData = datastreamFormValue.containsPersonalInfoData; + if (Object.keys(location).length) { + deviceUpdate.location = location; } - if (datastreamEntry.get('isReusable').dirty) { - datastreamUpdate.isReusable = datastreamFormValue.isReusable; + + try { + if (Object.keys(deviceUpdate).length) { + await this.deviceService + .update(this.deviceForm.value.id, deviceUpdate as IUpdateDeviceBody) + .toPromise(); + this.deviceForm.markAsPristine(); + this.alertService.success(this.saveSuccessMessage); + } + } catch (e) { + this.alertService.error(e.error.message); } - if (datastreamEntry.get('documentation').dirty) { - datastreamUpdate.documentation = datastreamFormValue.documentation; + + this.submitted = false; + } else { + const deviceLocation = this.deviceForm.value.location; + + const device: IRegisterDeviceBody = { + name: this.deviceForm.value.name, + description: this.deviceForm.value.description, + category: this.deviceForm.value.category.category, + connectivity: this.deviceForm.value.connectivity, + location: { + name: this.deviceForm.value.locationName, + description: this.deviceForm.value.locationDescription, + location: deviceLocation + ? [deviceLocation.longitude, deviceLocation.latitude, deviceLocation.height] + : null, + }, + }; + + try { + const deviceDetails: Record = await this.deviceService.register(device).toPromise(); + this.deviceId = deviceDetails.deviceId; + this.deviceForm.markAsPristine(); + + this.locationService.showLocation(null); + this.alertService.success(this.saveSuccessMessage); + this.submitted = false; + } catch (e) { + this.alertService.error(`${this.saveFailedMessage} ${e.error.message}.`); } - if (datastreamEntry.get('dataLink').dirty) { - datastreamUpdate.dataLink = datastreamFormValue.dataLink; + } + } + + public async updateObservationGoals(sensorId, datastreamId, observationGoalIds) { + const device = (await this.deviceService.get(this.deviceId).toPromise()) as IDevice; + const deviceDatastreams = + device && device.datastreams ? device.datastreams.filter((x) => x._id === datastreamId) : []; + + const promises = []; + if (deviceDatastreams.length) { + const existingObservationGoalIds = deviceDatastreams[0].observationGoalIds; + for (const observationGoalId of observationGoalIds) { + if (!existingObservationGoalIds || !existingObservationGoalIds.includes(observationGoalId)) { + promises.push( + this.deviceService + .linkObservationGoal(this.deviceId, sensorId, datastreamId, observationGoalId) + .toPromise(), + ); + } + } + if (existingObservationGoalIds) { + for (const existingObservationGoalId of existingObservationGoalIds) { + if (!observationGoalIds.includes(existingObservationGoalId)) { + promises.push( + this.deviceService + .unlinkObservationGoal(this.deviceId, sensorId, datastreamId, existingObservationGoalId) + .toPromise(), + ); + } + } } + } + + await Promise.all(promises); + } + + public async registerSensors() { + const sensors = this.sensorForm.get('sensors') as FormArray; + + let failed = false; + for (const sensorEntry of sensors.controls) { + const sensorEntryValue = sensorEntry.value; + if (!sensorEntryValue.id) { + const sensor: IRegisterSensorBody = { + name: sensorEntryValue.name, + description: sensorEntryValue.description, + type: sensorEntryValue.typeName.value, + manufacturer: sensorEntryValue.manufacturer, + supplier: sensorEntryValue.supplier, + documentation: sensorEntryValue.documentation, + }; + + try { + const sensorResult: Record = await this.deviceService + .registerSensor(this.deviceId, sensor) + .toPromise(); + sensorEntry.patchValue({ id: sensorResult.sensorId }); + } catch (e) { + failed = true; + this.alertService.error(e.error.message); + } + } else { + const sensorUpdate: Record = {}; + if (sensorEntry.get('name').dirty) { + sensorUpdate.name = sensorEntryValue.name; + } + if (sensorEntry.get('description').dirty) { + sensorUpdate.description = sensorEntryValue.description; + } + if (sensorEntry.get('typeName').dirty) { + sensorUpdate.type = sensorEntryValue.typeName.value; + } + if (sensorEntry.get('manufacturer').dirty) { + sensorUpdate.manufacturer = sensorEntryValue.manufacturer; + } + if (sensorEntry.get('supplier').dirty) { + sensorUpdate.supplier = sensorEntryValue.supplier; + } + if (sensorEntry.get('documentation').dirty) { + sensorUpdate.documentation = sensorEntryValue.documentation; + } - if (datastreamFormValue.observationGoals) { - const observationGoalIds = datastreamFormValue.observationGoals.map(x => x._id); - await this.updateObservationGoals(sensorId, datastreamId, observationGoalIds); + try { + if (Object.keys(sensorUpdate).length) { + await this.deviceService + .updateSensor(this.deviceId, sensorEntryValue.id, sensorUpdate as IUpdateSensorBody) + .toPromise(); + this.sensorForm.markAsPristine(); + this.alertService.success(this.saveSuccessMessage); + } + } catch (e) { + this.alertService.error(e.error.message); + } + + this.submitted = false; } + } + + if (!failed) { + this.alertService.success(this.saveSuccessMessage); + } + + this.submitted = false; + } + + public async registerDatastreams() { + const sensors = this.sensorForm.get('sensors') as FormArray; + + let failed = false; + for (const sensorEntry of sensors.controls) { + const sensorId = sensorEntry.value.id; - if (Object.keys(datastreamUpdate).length) { - await this.deviceService.updateDatastream(this.deviceId, sensorId, datastreamId, - datastreamUpdate as IUpdateDatastreamBody).toPromise(); - this.sensorForm.markAsPristine(); + const datastreams = sensorEntry.get('datastreams') as FormArray; + for (const datastreamEntry of datastreams.controls) { + const datastreamFormValue = datastreamEntry.value; + const datastream: IRegisterDatastreamBody = { + name: datastreamFormValue.name, + description: datastreamFormValue.description, + theme: datastreamFormValue.theme ? datastreamFormValue.theme.value : null, + dataQuality: datastreamFormValue.dataQuality, + isActive: datastreamFormValue.isActive, + isPublic: datastreamFormValue.isPublic, + isOpenData: datastreamFormValue.isOpenData, + containsPersonalInfoData: datastreamFormValue.containsPersonalInfoData, + isReusable: datastreamFormValue.isReusable, + documentation: datastreamFormValue.documentation, + dataLink: datastreamFormValue.dataLink, + }; + + try { + const datastreamId = datastreamEntry.value.id; + if (sensorId && !datastreamId) { + try { + const datastreamResult: Record = await this.deviceService + .registerDatastream(this.deviceId, sensorId, datastream) + .toPromise(); + datastreamEntry.patchValue({ id: datastreamResult.datastreamId }); + + if (datastreamFormValue.observationGoals) { + for (const observationGoal of datastreamFormValue.observationGoals) { + await this.deviceService + .linkObservationGoal( + this.deviceId, + sensorId, + datastreamResult.datastreamId, + observationGoal._id, + ) + .toPromise(); + } + } + } catch (e) { + failed = true; + this.alertService.error(e.error.message); + } + } else { + const datastreamUpdate: Record = {}; + if (datastreamEntry.get('name').dirty) { + datastreamUpdate.name = datastreamFormValue.name; + } + if (datastreamEntry.get('description').dirty) { + datastreamUpdate.description = datastreamFormValue.description; + } + if (datastreamEntry.get('theme').dirty) { + datastreamUpdate.theme = datastreamFormValue.theme ? datastreamFormValue.theme.value : null; + } + if (datastreamEntry.get('dataQuality').dirty) { + datastreamUpdate.dataQuality = datastreamFormValue.dataQuality; + } + if (datastreamEntry.get('isActive').dirty) { + datastreamUpdate.isActive = datastreamFormValue.isActive; + } + if (datastreamEntry.get('isPublic').dirty) { + datastreamUpdate.isPublic = datastreamFormValue.isPublic; + } + if (datastreamEntry.get('isOpenData').dirty) { + datastreamUpdate.isOpenData = datastreamFormValue.isOpenData; + } + if (datastreamEntry.get('containsPersonalInfoData').dirty) { + datastreamUpdate.containsPersonalInfoData = datastreamFormValue.containsPersonalInfoData; + } + if (datastreamEntry.get('isReusable').dirty) { + datastreamUpdate.isReusable = datastreamFormValue.isReusable; + } + if (datastreamEntry.get('documentation').dirty) { + datastreamUpdate.documentation = datastreamFormValue.documentation; + } + if (datastreamEntry.get('dataLink').dirty) { + datastreamUpdate.dataLink = datastreamFormValue.dataLink; + } + + if (datastreamFormValue.observationGoals) { + const observationGoalIds = datastreamFormValue.observationGoals.map((x) => x._id); + await this.updateObservationGoals(sensorId, datastreamId, observationGoalIds); + } + + if (Object.keys(datastreamUpdate).length) { + await this.deviceService + .updateDatastream( + this.deviceId, + sensorId, + datastreamId, + datastreamUpdate as IUpdateDatastreamBody, + ) + .toPromise(); + this.sensorForm.markAsPristine(); + } + + this.submitted = false; + this.alertService.success(this.saveSuccessMessage, false, 4000); + } + } catch (e) { + this.alertService.error(e.error.message, false, 4000); + } } + } - this.submitted = false; + if (!failed) { this.alertService.success(this.saveSuccessMessage, false, 4000); - } - } catch (e) { - this.alertService.error(e.error.message, false, 4000); } - } - } - if (!failed) { - this.alertService.success(this.saveSuccessMessage, false, 4000); + this.submitted = false; } - this.submitted = false; - } - - public ngOnInit() { - this.deviceForm = this.formBuilder.group({ - id: null, - category: null, - name: [null, [Validators.required, Validators.minLength(6)]], - connectivity: null, - description: null, - location: [], - locationName: null, - locationDescription: null, - sensors: new FormArray([]), - }); - - this.sensorForm = this.formBuilder.group({ - sensors: new FormArray([]), - }); - - this.formControlSteps = [ - [ - this.deviceForm.controls.category, - this.deviceForm.controls.name, - this.deviceForm.controls.connectivity, - this.deviceForm.controls.description, - this.deviceForm.controls.location, - this.deviceForm.controls.locationName, - this.deviceForm.controls.locationDescription, - ], [ - this.sensorForm.controls.sensors, - ], [ - this.sensorForm.controls.sensors, - ] - ]; - - this.subscriptions.push( - this.route.params.subscribe(async params => { - if (params.id) { - const device = await this.deviceService.get(params.id).toPromise(); - if (device) { - this.deviceId = params.id; - this.setDevice(device as IDevice); - } - - this.locationService.showLocation(null); - } - }) - ); - } + public ngOnInit() { + this.deviceForm = this.formBuilder.group({ + id: null, + category: null, + name: [null, [Validators.required, Validators.minLength(6)]], + connectivity: null, + description: null, + location: [], + locationName: null, + locationDescription: null, + sensors: new FormArray([]), + }); + + this.sensorForm = this.formBuilder.group({ + sensors: new FormArray([]), + }); + + this.formControlSteps = [ + [ + this.deviceForm.controls.category, + this.deviceForm.controls.name, + this.deviceForm.controls.connectivity, + this.deviceForm.controls.description, + this.deviceForm.controls.location, + this.deviceForm.controls.locationName, + this.deviceForm.controls.locationDescription, + ], + [this.sensorForm.controls.sensors], + [this.sensorForm.controls.sensors], + ]; + + this.subscriptions.push( + this.route.params.subscribe(async (params) => { + if (params.id) { + const device = await this.deviceService.get(params.id).toPromise(); + if (device) { + this.deviceId = params.id; + this.setDevice(device as IDevice); + } + + this.locationService.showLocation(null); + } + }), + ); + } - public ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + public ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/forms/devices/devices.component.ts b/src/app/forms/devices/devices.component.ts index d4e6d53..af1d42b 100644 --- a/src/app/forms/devices/devices.component.ts +++ b/src/app/forms/devices/devices.component.ts @@ -1,324 +1,342 @@ -import { Subject } from 'rxjs'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { FormGroup, FormBuilder, FormArray, Validators } from '@angular/forms'; import { Router } from '@angular/router'; +import { OidcSecurityService } from 'angular-auth-oidc-client'; +import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { IDevice } from '../../model/bodies/device-model'; -import { ModalService } from '../../services/modal.service'; +import { getCategoryTranslation, Category } from '../../model/bodies/sensorTypes'; import { AlertService } from '../../services/alert.service'; -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { LocationService } from '../../services/location.service'; import { ConnectionService } from '../../services/connection.service'; -import { LegalEntityService } from '../../services/legal-entity.service'; -import { FormGroup, FormBuilder, FormArray, Validators } from '@angular/forms'; import { DeviceService, IUpdateDeviceBody } from '../../services/device.service'; -import { getCategoryTranslation, Category } from '../../model/bodies/sensorTypes'; +import { LegalEntityService } from '../../services/legal-entity.service'; +import { LocationService } from '../../services/location.service'; +import { ModalService } from '../../services/modal.service'; @Component({ - selector: 'app-devices', - templateUrl: './devices.component.html', - styleUrls: ['./devices.component.scss'], + selector: 'app-devices', + templateUrl: './devices.component.html', + styleUrls: ['./devices.component.scss'], }) export class DevicesComponent implements OnInit, OnDestroy { - public legalEntity; - public subscriptions = []; - public devices: IDevice[] = []; - public selectedDeviceIds: string[] = []; - - public pageIndex = 0; - public pageSize = 15; - - public sortField = 'name'; - public sortDirections = { ASCENDING: 'ASCENDING', DESCENDING: 'DESCENDING' }; - public sortDirection = this.sortDirections.ASCENDING; - - public sensorCategories = Category; - public getCategoryTranslation = getCategoryTranslation; - - public showLocation = false; - - public canEdit = false; - public canDelete = false; - public canEditLocation = false; - - public devicesTable: FormGroup = this.fb.group({ - tableRows: this.fb.array([]) - }); - - private filterChanged: Subject = new Subject(); - - public confirmTitleString = $localize`:@@confirm.title:Please confirm`; - public formInvalidMessage = $localize`:@@form.register.invalid:The form is invalid`; - public updatedDevicesString = $localize`:@@updated.devices:Successfully updated device(s).`; - public joinOrganizationString = $localize`:@@join.organization:You need to join an organization first.`; - public confirmUpdateString = $localize`:@@update.devices.confirm.body:Do you really want to update the device(s)?`; - public confirmDeleteBodyString = $localize`:@@delete.devices.confirm.body:Do you really want to delete the device(s)?`; - - constructor( - private readonly router: Router, - private readonly fb: FormBuilder, - private readonly modalService: ModalService, - private readonly alertService: AlertService, - private readonly deviceService: DeviceService, - private readonly locationService: LocationService, - private readonly connectionService: ConnectionService, - private readonly legalEntityService: LegalEntityService, - private readonly oidcSecurityService: OidcSecurityService, - ) { } - - getSortClass(sortField) { - let sortClass; - if (this.sortField === sortField) { - if (this.sortDirection === this.sortDirections.ASCENDING) { - sortClass = 'sort-up'; - } else { - sortClass = 'sort-down'; - } - } else { - sortClass = 'sort'; - } + public legalEntity; + public subscriptions = []; + public devices: IDevice[] = []; + public selectedDeviceIds: string[] = []; - return sortClass; - } + public pageIndex = 0; + public pageSize = 15; - public async setSort(sortField) { - if (sortField === this.sortField) { - this.sortDirection = (this.sortDirection === this.sortDirections.ASCENDING ? - this.sortDirections.DESCENDING : this.sortDirections.ASCENDING); - } + public sortField = 'name'; + public sortDirections = { ASCENDING: 'ASCENDING', DESCENDING: 'DESCENDING' }; + public sortDirection = this.sortDirections.ASCENDING; - this.sortField = sortField; - await this.getPage(this.pageIndex); - } + public sensorCategories = Category; + public getCategoryTranslation = getCategoryTranslation; - public async getPreviousPage() { - if (this.pageIndex > 0) { - await this.getPage(this.pageIndex - 1); - } - } + public showLocation = false; - public async getNextPage() { - if (this.devices.length === this.pageSize) { - await this.getPage(this.pageIndex + 1); - } - } - - public async getPage(pageIndex, name?) { - if (this.legalEntity && this.legalEntity._id) { - this.devices = await this.deviceService.getMyDevices(this.legalEntity._id, pageIndex, this.pageSize, - this.sortField, this.sortDirection, name); - } else { - this.devices = []; - } - this.filterSelectedDevices(this.devices.map(x => x._id)); + public canEdit = false; + public canDelete = false; + public canEditLocation = false; - this.devicesTable = this.fb.group({ - tableRows: this.fb.array([]) + public devicesTable: FormGroup = this.fb.group({ + tableRows: this.fb.array([]), }); - const control = this.devicesTable.get('tableRows') as FormArray; - this.devices.map(x => control.push(this.initiateDeviceForm(x))); - - this.pageIndex = pageIndex; - } + private filterChanged: Subject = new Subject(); + + public confirmTitleString = $localize`:@@confirm.title:Please confirm`; + public formInvalidMessage = $localize`:@@form.register.invalid:The form is invalid`; + public updatedDevicesString = $localize`:@@updated.devices:Successfully updated device(s).`; + public joinOrganizationString = $localize`:@@join.organization:You need to join an organization first.`; + public confirmUpdateString = $localize`:@@update.devices.confirm.body:Do you really want to update the device(s)?`; + public confirmDeleteBodyString = $localize`:@@delete.devices.confirm.body:Do you really want to delete the device(s)?`; + + constructor( + private readonly router: Router, + private readonly fb: FormBuilder, + private readonly modalService: ModalService, + private readonly alertService: AlertService, + private readonly deviceService: DeviceService, + private readonly locationService: LocationService, + private readonly connectionService: ConnectionService, + private readonly legalEntityService: LegalEntityService, + private readonly oidcSecurityService: OidcSecurityService, + ) {} + + getSortClass(sortField) { + let sortClass; + if (this.sortField === sortField) { + if (this.sortDirection === this.sortDirections.ASCENDING) { + sortClass = 'sort-up'; + } else { + sortClass = 'sort-down'; + } + } else { + sortClass = 'sort'; + } - public initiateDeviceForm(device): FormGroup { - return this.fb.group({ - id: [device._id], - name: [device.name, [Validators.required, Validators.minLength(6)]], - category: [device.category], - connectivity: [device.connectivity], - description: [device.description], - }); - } + return sortClass; + } - get getFormControls() { - return this.devicesTable.get('tableRows') as FormArray; - } + public async setSort(sortField) { + if (sortField === this.sortField) { + this.sortDirection = + this.sortDirection === this.sortDirections.ASCENDING + ? this.sortDirections.DESCENDING + : this.sortDirections.ASCENDING; + } - public async editSelectedDevice(): Promise { - if (this.selectedDeviceIds) { - const selectedDeviceId = this.selectedDeviceIds[0]; - await this.router.navigate([`/device/${selectedDeviceId}`]); + this.sortField = sortField; + await this.getPage(this.pageIndex); } - } - - public async deleteSelectedDevices(): Promise { - if (this.selectedDeviceIds) { - await this.modalService.confirm(this.confirmTitleString, this.confirmDeleteBodyString) - .then(async () => { - try { - for (const deviceId of this.selectedDeviceIds) { - await this.deviceService.unregister(deviceId); - } - await this.getPage(this.pageIndex); - } catch (e) { - this.alertService.error(e.error.message); - } - }, () => { }); - } - } - - public toggleLocation() { - this.showLocation = !this.showLocation; - } - - public updateActions() { - const selectedDevicesCount = this.selectedDeviceIds.length; - if (selectedDevicesCount > 0) { - this.canDelete = true; - this.canEditLocation = true; - this.canEdit = selectedDevicesCount === 1; - } else { - this.canEdit = false; - this.canDelete = false; - this.canEditLocation = false; + public async getPreviousPage() { + if (this.pageIndex > 0) { + await this.getPage(this.pageIndex - 1); + } } - } - public toggleDevice(sensorId: string) { - if (this.selectedDeviceIds.includes(sensorId)) { - this.selectedDeviceIds = this.selectedDeviceIds.filter(x => x !== sensorId); - } else { - this.selectedDeviceIds.push(sensorId); + public async getNextPage() { + if (this.devices.length === this.pageSize) { + await this.getPage(this.pageIndex + 1); + } } - this.updateActions(); - } + public async getPage(pageIndex, name?) { + if (this.legalEntity && this.legalEntity._id) { + this.devices = await this.deviceService.getMyDevices( + this.legalEntity._id, + pageIndex, + this.pageSize, + this.sortField, + this.sortDirection, + name, + ); + } else { + this.devices = []; + } + this.filterSelectedDevices(this.devices.map((x) => x._id)); - public filterSelectedDevices(deviceIds: string[]) { - this.selectedDeviceIds = this.selectedDeviceIds.filter(x => deviceIds.includes(x)); + this.devicesTable = this.fb.group({ + tableRows: this.fb.array([]), + }); - this.updateActions(); - } + const control = this.devicesTable.get('tableRows') as FormArray; + this.devices.map((x) => control.push(this.initiateDeviceForm(x))); - public selectDevice(sensorId: string) { - if (!this.selectedDeviceIds.includes(sensorId)) { - this.selectedDeviceIds.push(sensorId); + this.pageIndex = pageIndex; } - this.updateActions(); - } - - public async updateDeviceLocation(newLocation) { - await this.modalService.confirm(this.confirmTitleString, this.confirmUpdateString) - .then(async () => { - const promises = []; - for (const deviceId of this.selectedDeviceIds) { - const latitude = newLocation.length > 0 ? newLocation[0] : null; - const longitude = newLocation.length > 1 ? newLocation[1] : null; - const location = [longitude, latitude]; - - for (const device of this.devices) { - if (device._id === deviceId && device.location.coordinates.length > 2) { - location.push(device.location.coordinates[2]); - break; - } - } + public initiateDeviceForm(device): FormGroup { + return this.fb.group({ + id: [device._id], + name: [device.name, [Validators.required, Validators.minLength(6)]], + category: [device.category], + connectivity: [device.connectivity], + description: [device.description], + }); + } - const updateBody: IUpdateDeviceBody = { - location: { location }, - }; + get getFormControls() { + return this.devicesTable.get('tableRows') as FormArray; + } - if (updateBody) { - promises.push(this.deviceService.update(deviceId, updateBody).toPromise()); - } + public async editSelectedDevice(): Promise { + if (this.selectedDeviceIds) { + const selectedDeviceId = this.selectedDeviceIds[0]; + await this.router.navigate([`/device/${selectedDeviceId}`]); } + } - await Promise.all(promises); - this.alertService.success(this.updatedDevicesString); - }, () => { }); - } + public async deleteSelectedDevices(): Promise { + if (this.selectedDeviceIds) { + await this.modalService.confirm(this.confirmTitleString, this.confirmDeleteBodyString).then( + async () => { + try { + for (const deviceId of this.selectedDeviceIds) { + await this.deviceService.unregister(deviceId); + } + + await this.getPage(this.pageIndex); + } catch (e) { + this.alertService.error(e.error.message); + } + }, + () => {}, + ); + } + } - filterInputChanged(name) { - this.filterChanged.next(name); - } + public toggleLocation() { + this.showLocation = !this.showLocation; + } - public async saveDevices() { - if (!this.devicesTable.valid) { - this.alertService.error(this.formInvalidMessage); - return; + public updateActions() { + const selectedDevicesCount = this.selectedDeviceIds.length; + if (selectedDevicesCount > 0) { + this.canDelete = true; + this.canEditLocation = true; + this.canEdit = selectedDevicesCount === 1; + } else { + this.canEdit = false; + this.canDelete = false; + this.canEditLocation = false; + } } - await this.modalService.confirm(this.confirmTitleString, this.confirmUpdateString) - .then(async () => { - const promises = []; - - const devices = this.devicesTable.get('tableRows') as FormArray; - for (const device of devices.controls) { - const deviceId = device.value.id; - const updateBody: IUpdateDeviceBody = {}; - - if (device.get('name').dirty) { - updateBody.name = device.value.name; - } - if (device.get('category').dirty) { - updateBody.category = device.value.category; - } - if (device.get('connectivity').dirty) { - updateBody.connectivity = device.value.connectivity; - } - if (device.get('description').dirty) { - updateBody.description = device.value.description; - } - - if (Object.keys(updateBody).length) { - promises.push(this.deviceService.update(deviceId, updateBody).toPromise()); - } + public toggleDevice(sensorId: string) { + if (this.selectedDeviceIds.includes(sensorId)) { + this.selectedDeviceIds = this.selectedDeviceIds.filter((x) => x !== sensorId); + } else { + this.selectedDeviceIds.push(sensorId); } - await Promise.all(promises); - this.devicesTable.markAsPristine(); + this.updateActions(); + } - this.alertService.success(this.updatedDevicesString); - }, () => { }); - } + public filterSelectedDevices(deviceIds: string[]) { + this.selectedDeviceIds = this.selectedDeviceIds.filter((x) => deviceIds.includes(x)); - public async registerDevice() { - if (this.legalEntity) { - await this.router.navigate(['/device']); - } else { - this.alertService.error(this.joinOrganizationString); + this.updateActions(); } - } - - public async ngOnInit(): Promise { - this.legalEntity = await this.legalEntityService.get().toPromise(); - await this.getPage(0); - - this.subscriptions.push(this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { - if (auth) { - this.connectionService.refreshLegalEntity(); - } - })); - - this.subscriptions.push( - this.locationService.location$.subscribe(location => { - if (this.showLocation && location.coordinates) { - this.updateDeviceLocation(location.coordinates); + + public selectDevice(sensorId: string) { + if (!this.selectedDeviceIds.includes(sensorId)) { + this.selectedDeviceIds.push(sensorId); } - }) - ); - this.subscriptions.push(this.filterChanged - .pipe(debounceTime(750)) - .subscribe(name => this.getPage(this.pageIndex, name))); + this.updateActions(); + } - const { onUpdate, onRemove } = await this.deviceService.subscribe(); + public async updateDeviceLocation(newLocation) { + await this.modalService.confirm(this.confirmTitleString, this.confirmUpdateString).then( + async () => { + const promises = []; + for (const deviceId of this.selectedDeviceIds) { + const latitude = newLocation.length > 0 ? newLocation[0] : null; + const longitude = newLocation.length > 1 ? newLocation[1] : null; + const location = [longitude, latitude]; + + for (const device of this.devices) { + if (device._id === deviceId && device.location.coordinates.length > 2) { + location.push(device.location.coordinates[2]); + break; + } + } + + const updateBody: IUpdateDeviceBody = { + location: { location }, + }; + + if (updateBody) { + promises.push(this.deviceService.update(deviceId, updateBody).toPromise()); + } + } + + await Promise.all(promises); + this.alertService.success(this.updatedDevicesString); + }, + () => {}, + ); + } - this.subscriptions.push(onUpdate.subscribe((updatedDevice: IDevice) => { - for (let i = 0; i < this.devices.length; i++) { - const device = this.devices[i]; - if (device._id === updatedDevice._id) { - this.devices[i] = updatedDevice; + filterInputChanged(name) { + this.filterChanged.next(name); + } + + public async saveDevices() { + if (!this.devicesTable.valid) { + this.alertService.error(this.formInvalidMessage); + return; } - } - })); - this.subscriptions.push(onRemove.subscribe(_ => this.getPage(this.pageIndex))); - } + await this.modalService.confirm(this.confirmTitleString, this.confirmUpdateString).then( + async () => { + const promises = []; + + const devices = this.devicesTable.get('tableRows') as FormArray; + for (const device of devices.controls) { + const deviceId = device.value.id; + const updateBody: IUpdateDeviceBody = {}; + + if (device.get('name').dirty) { + updateBody.name = device.value.name; + } + if (device.get('category').dirty) { + updateBody.category = device.value.category; + } + if (device.get('connectivity').dirty) { + updateBody.connectivity = device.value.connectivity; + } + if (device.get('description').dirty) { + updateBody.description = device.value.description; + } + + if (Object.keys(updateBody).length) { + promises.push(this.deviceService.update(deviceId, updateBody).toPromise()); + } + } + + await Promise.all(promises); + this.devicesTable.markAsPristine(); + + this.alertService.success(this.updatedDevicesString); + }, + () => {}, + ); + } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + public async registerDevice() { + if (this.legalEntity) { + await this.router.navigate(['/device']); + } else { + this.alertService.error(this.joinOrganizationString); + } + } + + public async ngOnInit(): Promise { + this.legalEntity = await this.legalEntityService.get().toPromise(); + await this.getPage(0); + + this.subscriptions.push( + this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { + if (auth) { + this.connectionService.refreshLegalEntity(); + } + }), + ); + + this.subscriptions.push( + this.locationService.location$.subscribe((location) => { + if (this.showLocation && location.coordinates) { + this.updateDeviceLocation(location.coordinates); + } + }), + ); + + this.subscriptions.push( + this.filterChanged.pipe(debounceTime(750)).subscribe((name) => this.getPage(this.pageIndex, name)), + ); + + const { onUpdate, onRemove } = await this.deviceService.subscribe(); + + this.subscriptions.push( + onUpdate.subscribe((updatedDevice: IDevice) => { + for (let i = 0; i < this.devices.length; i++) { + const device = this.devices[i]; + if (device._id === updatedDevice._id) { + this.devices[i] = updatedDevice; + } + } + }), + ); + + this.subscriptions.push(onRemove.subscribe((_) => this.getPage(this.pageIndex))); + } + + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/forms/organization-create/organization-create.component.ts b/src/app/forms/organization-create/organization-create.component.ts index 7d3b7b8..20043b2 100644 --- a/src/app/forms/organization-create/organization-create.component.ts +++ b/src/app/forms/organization-create/organization-create.component.ts @@ -1,75 +1,75 @@ -import { AlertService } from '../../services/alert.service'; import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { LegalEntityId } from '../../model/bodies/legal-entity-id'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { IRegisterLegalEntityBody, LegalEntityService } from '../../services/legal-entity.service'; +import { urlRegex } from '../../helpers/form.helpers'; +import { LegalEntityId } from '../../model/bodies/legal-entity-id'; import { IContactDetails } from '../../model/legalEntity'; +import { AlertService } from '../../services/alert.service'; import { ConnectionService } from '../../services/connection.service'; -import { urlRegex } from '../../helpers/form.helpers'; +import { IRegisterLegalEntityBody, LegalEntityService } from '../../services/legal-entity.service'; import { createOrganizationMailValidator } from '../../validators/organization-mail.validator'; @Component({ - selector: 'app-organization-create', - templateUrl: './organization-create.component.html', + selector: 'app-organization-create', + templateUrl: './organization-create.component.html', }) export class OrganizationCreateComponent implements OnInit, OnDestroy { - @Output() setLegalEntityId = new EventEmitter(); + @Output() setLegalEntityId = new EventEmitter(); - public form: FormGroup; - public submitted = false; - public subscriptions = []; + public form: FormGroup; + public submitted = false; + public subscriptions = []; - public registerFailedMessage = $localize`:@@register.failed:Failed to register. Does the organization exist already?`; + public registerFailedMessage = $localize`:@@register.failed:Failed to register. Does the organization exist already?`; - constructor( - private readonly formBuilder: FormBuilder, - private readonly alertService: AlertService, - private readonly connectionService: ConnectionService, - private readonly legalEntityService: LegalEntityService, - ) { } + constructor( + private readonly formBuilder: FormBuilder, + private readonly alertService: AlertService, + private readonly connectionService: ConnectionService, + private readonly legalEntityService: LegalEntityService, + ) {} - get f() { - return this.form.controls; - } + get f() { + return this.form.controls; + } - public async submit() { - this.submitted = true; - if (this.form.valid) { - try { - const contactDetails: IContactDetails = { - name: this.form.value.contactName, - email: this.form.value.contactEmail, - phone: this.form.value.contactPhone, - }; - const legalEntity: IRegisterLegalEntityBody = { - name: this.form.value.name, - website: this.form.value.website, - contactDetails, - }; + public async submit() { + this.submitted = true; + if (this.form.valid) { + try { + const contactDetails: IContactDetails = { + name: this.form.value.contactName, + email: this.form.value.contactEmail, + phone: this.form.value.contactPhone, + }; + const legalEntity: IRegisterLegalEntityBody = { + name: this.form.value.name, + website: this.form.value.website, + contactDetails, + }; - const result = await this.legalEntityService.register(legalEntity).toPromise() as LegalEntityId; - if (result && result.legalEntityId) { - this.connectionService.updateSocketLegalEntity(result.legalEntityId); - this.setLegalEntityId.emit(result.legalEntityId); + const result = (await this.legalEntityService.register(legalEntity).toPromise()) as LegalEntityId; + if (result && result.legalEntityId) { + this.connectionService.updateSocketLegalEntity(result.legalEntityId); + this.setLegalEntityId.emit(result.legalEntityId); + } + } catch { + this.alertService.error(this.registerFailedMessage); + } + this.submitted = false; } - } catch { - this.alertService.error(this.registerFailedMessage); - } - this.submitted = false; } - } - ngOnInit(): void { - this.form = this.formBuilder.group({ - name: ['', [Validators.required]], - website: ['', [Validators.pattern(urlRegex)]], - contactName: [''], - contactPhone: [''], - contactEmail: ['', [createOrganizationMailValidator()]], - }); - } + ngOnInit(): void { + this.form = this.formBuilder.group({ + name: ['', [Validators.required]], + website: ['', [Validators.pattern(urlRegex)]], + contactName: [''], + contactPhone: [''], + contactEmail: ['', [createOrganizationMailValidator()]], + }); + } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/forms/organization-join/organization-join.component.ts b/src/app/forms/organization-join/organization-join.component.ts index e0730d7..80c8d7d 100644 --- a/src/app/forms/organization-join/organization-join.component.ts +++ b/src/app/forms/organization-join/organization-join.component.ts @@ -1,91 +1,91 @@ -import { AlertService } from '../../services/alert.service'; import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { LegalEntityService } from '../../services/legal-entity.service'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { ILegalEntity } from '../../model/legalEntity'; -import { UserService } from '../../services/user.service'; -import { ConnectionService } from '../../services/connection.service'; import { Subject } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { UserUpdateBody } from '../../model/bodies/user-update'; +import { ILegalEntity } from '../../model/legalEntity'; +import { AlertService } from '../../services/alert.service'; +import { ConnectionService } from '../../services/connection.service'; +import { LegalEntityService } from '../../services/legal-entity.service'; +import { UserService } from '../../services/user.service'; @Component({ - selector: 'app-organization-join', - templateUrl: './organization-join.component.html', + selector: 'app-organization-join', + templateUrl: './organization-join.component.html', }) export class OrganizationJoinComponent implements OnInit, OnDestroy { - @Output() setLegalEntityId = new EventEmitter(); + @Output() setLegalEntityId = new EventEmitter(); - public form: FormGroup; - public submitted = false; + public form: FormGroup; + public submitted = false; - public subscriptions = []; - public legalEntities = []; + public subscriptions = []; + public legalEntities = []; - private filterChanged: Subject = new Subject(); + private filterChanged: Subject = new Subject(); - constructor( - private readonly alertService: AlertService, - private readonly formBuilder: FormBuilder, - private readonly userService: UserService, - private readonly connectionService: ConnectionService, - private readonly legalEntityService: LegalEntityService, - ) { } + constructor( + private readonly alertService: AlertService, + private readonly formBuilder: FormBuilder, + private readonly userService: UserService, + private readonly connectionService: ConnectionService, + private readonly legalEntityService: LegalEntityService, + ) {} - get f() { - return this.form.controls; - } + get f() { + return this.form.controls; + } - async getLegalEntities(name?: string) { - const legalEntities = await this.legalEntityService.getLegalEntities(name).toPromise(); - if (legalEntities) { - this.legalEntities = legalEntities as ILegalEntity[]; - } else { - this.legalEntities = []; + async getLegalEntities(name?: string) { + const legalEntities = await this.legalEntityService.getLegalEntities(name).toPromise(); + if (legalEntities) { + this.legalEntities = legalEntities as ILegalEntity[]; + } else { + this.legalEntities = []; + } } - } - selectLegalEntity(legalEntityId: string) { - if (legalEntityId === this.form.get('legalEntity').value) { - this.form.patchValue({ legalEntity: '' }); - } else { - this.form.patchValue({ legalEntity: legalEntityId }); + selectLegalEntity(legalEntityId: string) { + if (legalEntityId === this.form.get('legalEntity').value) { + this.form.patchValue({ legalEntity: '' }); + } else { + this.form.patchValue({ legalEntity: legalEntityId }); + } } - } - filterInputChanged(name) { - this.filterChanged.next(name); - } + filterInputChanged(name) { + this.filterChanged.next(name); + } - async ngOnInit(): Promise { - this.form = this.formBuilder.group({ - legalEntity: new FormControl('', Validators.required), - }); + async ngOnInit(): Promise { + this.form = this.formBuilder.group({ + legalEntity: new FormControl('', Validators.required), + }); - await this.getLegalEntities(); - this.subscriptions.push(this.filterChanged - .pipe(debounceTime(750)) - .subscribe(name => this.getLegalEntities(name))); - } + await this.getLegalEntities(); + this.subscriptions.push( + this.filterChanged.pipe(debounceTime(750)).subscribe((name) => this.getLegalEntities(name)), + ); + } - public async submit() { - this.submitted = true; - if (this.form.valid) { - try { - const legalEntityId = this.form.value.legalEntity; - const userUpdate: UserUpdateBody = { legalEntityId }; - await this.userService.update(userUpdate).toPromise(); + public async submit() { + this.submitted = true; + if (this.form.valid) { + try { + const legalEntityId = this.form.value.legalEntity; + const userUpdate: UserUpdateBody = { legalEntityId }; + await this.userService.update(userUpdate).toPromise(); - this.connectionService.updateSocketLegalEntity(legalEntityId); - this.setLegalEntityId.emit(legalEntityId); - } catch (e) { - this.alertService.error(e.error.message); - } + this.connectionService.updateSocketLegalEntity(legalEntityId); + this.setLegalEntityId.emit(legalEntityId); + } catch (e) { + this.alertService.error(e.error.message); + } + } + this.submitted = false; } - this.submitted = false; - } - ngOnDestroy(): void { - this.subscriptions.forEach(x => x.unsubscribe()); - } + ngOnDestroy(): void { + this.subscriptions.forEach((x) => x.unsubscribe()); + } } diff --git a/src/app/forms/organization-update/organization-update.component.ts b/src/app/forms/organization-update/organization-update.component.ts index b881760..b088237 100644 --- a/src/app/forms/organization-update/organization-update.component.ts +++ b/src/app/forms/organization-update/organization-update.component.ts @@ -1,143 +1,149 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { ModalService } from 'src/app/services/modal.service'; import { urlRegex } from '../../helpers/form.helpers'; import { UserUpdateBody } from '../../model/bodies/user-update'; import { IContactDetails, ILegalEntity } from '../../model/legalEntity'; import { AlertService } from '../../services/alert.service'; import { ConnectionService } from '../../services/connection.service'; import { LegalEntityService } from '../../services/legal-entity.service'; +import { ModalService } from '../../services/modal.service'; import { UserService } from '../../services/user.service'; import { createOrganizationMailValidator } from '../../validators/organization-mail.validator'; @Component({ - selector: 'app-organization-update', - templateUrl: './organization-update.component.html', + selector: 'app-organization-update', + templateUrl: './organization-update.component.html', }) export class OrganizationUpdateComponent implements OnInit { - @Input() public legalEntity: ILegalEntity; - @Output() setLegalEntityId = new EventEmitter(); - - public form: FormGroup; - public submitted = false; - - public updateSuccessMessage = $localize`:@@organization.update:Updated the organization.`; - - public leaveOrgConfirmTitleString = $localize`:@@leave.organization.confirm.title:Leave Organization`; - public leaveOrgConfirmBodyString = $localize`:@@leave.organization.confirm.body:You are about to leave your organization. Are you sure?`; - - public removeOrgConfirmTitleString = $localize`:@@remove.organization.confirm.title:Remove Organization`; - public removeOrgConfirmBodyString = $localize`:@@remove.organization.confirm.body:You are about to remove your organization. First all devices need to be removed before this will succeed. Are you sure to continue now?`; - - constructor( - private readonly formBuilder: FormBuilder, - private readonly userService: UserService, - private readonly alertService: AlertService, - private readonly connectionService: ConnectionService, - private readonly legalEntityService: LegalEntityService, - private readonly modalService: ModalService, - ) { } - - get f() { - return this.form.controls; - } - - ngOnInit(): void { - let contactName; - let contactPhone; - let contactEmail; - if (this.legalEntity && this.legalEntity.contactDetails && this.legalEntity.contactDetails.length) { - if (this.legalEntity.contactDetails[0].name) { - contactName = this.legalEntity.contactDetails[0].name; - } - if (this.legalEntity.contactDetails[0].phone) { - contactPhone = this.legalEntity.contactDetails[0].phone; - } - if (this.legalEntity.contactDetails[0].email) { - contactEmail = this.legalEntity.contactDetails[0].email; - } + @Input() public legalEntity: ILegalEntity; + @Output() setLegalEntityId = new EventEmitter(); + + public form: FormGroup; + public submitted = false; + + public updateSuccessMessage = $localize`:@@organization.update:Updated the organization.`; + + public leaveOrgConfirmTitleString = $localize`:@@leave.organization.confirm.title:Leave Organization`; + public leaveOrgConfirmBodyString = $localize`:@@leave.organization.confirm.body:You are about to leave your organization. Are you sure?`; + + public removeOrgConfirmTitleString = $localize`:@@remove.organization.confirm.title:Remove Organization`; + public removeOrgConfirmBodyString = $localize`:@@remove.organization.confirm.body:You are about to remove your organization. First all devices need to be removed before this will succeed. Are you sure to continue now?`; + + constructor( + private readonly formBuilder: FormBuilder, + private readonly userService: UserService, + private readonly alertService: AlertService, + private readonly connectionService: ConnectionService, + private readonly legalEntityService: LegalEntityService, + private readonly modalService: ModalService, + ) {} + + get f() { + return this.form.controls; } - this.form = this.formBuilder.group({ - name: [this.legalEntity ? this.legalEntity.name : null, [Validators.required]], - website: [this.legalEntity ? this.legalEntity.website : null, [Validators.pattern(urlRegex)]], - contactName: [contactName], - contactPhone: [contactPhone], - contactEmail: [contactEmail, [createOrganizationMailValidator()]], - }); - } - - public async leave() { - await this.modalService.confirm(this.leaveOrgConfirmTitleString, this.leaveOrgConfirmBodyString) - .then(async () => { - const userUpdate: UserUpdateBody = { - leaveLegalEntity: true, - }; - try { - await this.userService.update(userUpdate).toPromise(); - - this.setLegalEntityId.emit(null); - this.connectionService.updateSocketLegalEntity(null); - } catch (e) { - this.alertService.error(e.error.message); - } - }, () => { }); - } - - public async remove() { - await this.modalService.confirm(this.removeOrgConfirmTitleString, this.removeOrgConfirmBodyString) - .then(async () => { - try { - await this.legalEntityService.delete().toPromise(); - this.alertService.success('Successfully removed your organization!'); - - this.connectionService.updateSocketLegalEntity(null); - this.setLegalEntityId.emit(null); - } catch (e) { - this.alertService.error(e.error.message); - } - }, () => { }); - } - - public async submit() { - this.submitted = true; - if (!this.form.invalid) { - const legalEntityUpdate: Record = {}; - if (this.form.controls.name.dirty) { - legalEntityUpdate.name = this.form.value.name; - } - if (this.form.controls.website.dirty) { - legalEntityUpdate.website = this.form.value.website; - } - - const contactDetailsUpdate: Record = {}; - if (this.form.controls.contactName.dirty) { - contactDetailsUpdate.name = this.form.value.contactName; - } - if (this.form.controls.contactPhone.dirty) { - contactDetailsUpdate.phone = this.form.value.contactPhone; - } - if (this.form.controls.contactEmail.dirty) { - contactDetailsUpdate.email = this.form.value.contactEmail; - } - - try { - if (Object.keys(legalEntityUpdate).length) { - await this.legalEntityService.update(legalEntityUpdate as ILegalEntity).toPromise(); - } - if (Object.keys(contactDetailsUpdate).length) { - let contactDetailsId; - if (this.legalEntity && this.legalEntity.contactDetails && this.legalEntity.contactDetails.length) { - contactDetailsId = this.legalEntity.contactDetails[0]._id; - await this.legalEntityService.updateContactDetails(contactDetailsId, contactDetailsUpdate as IContactDetails).toPromise(); - } + ngOnInit(): void { + let contactName; + let contactPhone; + let contactEmail; + if (this.legalEntity && this.legalEntity.contactDetails && this.legalEntity.contactDetails.length) { + if (this.legalEntity.contactDetails[0].name) { + contactName = this.legalEntity.contactDetails[0].name; + } + if (this.legalEntity.contactDetails[0].phone) { + contactPhone = this.legalEntity.contactDetails[0].phone; + } + if (this.legalEntity.contactDetails[0].email) { + contactEmail = this.legalEntity.contactDetails[0].email; + } } - this.alertService.success(this.updateSuccessMessage); - } catch (e) { - this.alertService.error(e.error.message); - } - this.submitted = false; + this.form = this.formBuilder.group({ + name: [this.legalEntity ? this.legalEntity.name : null, [Validators.required]], + website: [this.legalEntity ? this.legalEntity.website : null, [Validators.pattern(urlRegex)]], + contactName: [contactName], + contactPhone: [contactPhone], + contactEmail: [contactEmail, [createOrganizationMailValidator()]], + }); + } + + public async leave() { + await this.modalService.confirm(this.leaveOrgConfirmTitleString, this.leaveOrgConfirmBodyString).then( + async () => { + const userUpdate: UserUpdateBody = { + leaveLegalEntity: true, + }; + try { + await this.userService.update(userUpdate).toPromise(); + + this.setLegalEntityId.emit(null); + this.connectionService.updateSocketLegalEntity(null); + } catch (e) { + this.alertService.error(e.error.message); + } + }, + () => {}, + ); + } + + public async remove() { + await this.modalService.confirm(this.removeOrgConfirmTitleString, this.removeOrgConfirmBodyString).then( + async () => { + try { + await this.legalEntityService.delete().toPromise(); + this.alertService.success('Successfully removed your organization!'); + + this.connectionService.updateSocketLegalEntity(null); + this.setLegalEntityId.emit(null); + } catch (e) { + this.alertService.error(e.error.message); + } + }, + () => {}, + ); + } + + public async submit() { + this.submitted = true; + if (!this.form.invalid) { + const legalEntityUpdate: Record = {}; + if (this.form.controls.name.dirty) { + legalEntityUpdate.name = this.form.value.name; + } + if (this.form.controls.website.dirty) { + legalEntityUpdate.website = this.form.value.website; + } + + const contactDetailsUpdate: Record = {}; + if (this.form.controls.contactName.dirty) { + contactDetailsUpdate.name = this.form.value.contactName; + } + if (this.form.controls.contactPhone.dirty) { + contactDetailsUpdate.phone = this.form.value.contactPhone; + } + if (this.form.controls.contactEmail.dirty) { + contactDetailsUpdate.email = this.form.value.contactEmail; + } + + try { + if (Object.keys(legalEntityUpdate).length) { + await this.legalEntityService.update(legalEntityUpdate as ILegalEntity).toPromise(); + } + if (Object.keys(contactDetailsUpdate).length) { + let contactDetailsId; + if (this.legalEntity && this.legalEntity.contactDetails && this.legalEntity.contactDetails.length) { + contactDetailsId = this.legalEntity.contactDetails[0]._id; + await this.legalEntityService + .updateContactDetails(contactDetailsId, contactDetailsUpdate as IContactDetails) + .toPromise(); + } + } + + this.alertService.success(this.updateSuccessMessage); + } catch (e) { + this.alertService.error(e.error.message); + } + this.submitted = false; + } } - } } diff --git a/src/app/forms/organization-users/organization-users.component.ts b/src/app/forms/organization-users/organization-users.component.ts index db9507c..a18a4e4 100644 --- a/src/app/forms/organization-users/organization-users.component.ts +++ b/src/app/forms/organization-users/organization-users.component.ts @@ -1,83 +1,83 @@ -import jwt_decode from 'jwt-decode'; import { Component, OnInit } from '@angular/core'; -import { UserService } from '../../services/user.service'; -import { AlertService } from '../../services/alert.service'; -import { OidcSecurityService } from 'angular-auth-oidc-client'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { OidcSecurityService } from 'angular-auth-oidc-client'; +import jwt_decode from 'jwt-decode'; +import { AlertService } from '../../services/alert.service'; +import { UserService } from '../../services/user.service'; @Component({ - selector: 'app-organization-users', - templateUrl: './organization-users.component.html', + selector: 'app-organization-users', + templateUrl: './organization-users.component.html', }) export class OrganizationUsersComponent implements OnInit { - public form: FormGroup; - public submitted = false; + public form: FormGroup; + public submitted = false; - public noUsersSelectedMessage = $localize`:@@no.users:No users have been selected.`; - public updateSuccessMessage = $localize`:@@user.update:Update success.`; + public noUsersSelectedMessage = $localize`:@@no.users:No users have been selected.`; + public updateSuccessMessage = $localize`:@@user.update:Update success.`; - public userId; - public users = []; - public subscriptions = []; + public userId; + public users = []; + public subscriptions = []; - constructor( - private alertService: AlertService, - private readonly formBuilder: FormBuilder, - private readonly userService: UserService, - private readonly oidcSecurityService: OidcSecurityService, - ) {} + constructor( + private alertService: AlertService, + private readonly formBuilder: FormBuilder, + private readonly userService: UserService, + private readonly oidcSecurityService: OidcSecurityService, + ) {} - get f() { - return this.form.controls; - } + get f() { + return this.form.controls; + } - async getUsers() { - this.users = await this.userService.retrieve().toPromise() as Record[]; - } + async getUsers() { + this.users = (await this.userService.retrieve().toPromise()) as Record[]; + } - selectUser(userId: string) { - if (userId === this.form.get('userId').value) { - this.form.patchValue({ userId: '' }); - } else { - this.form.patchValue({ userId }); + selectUser(userId: string) { + if (userId === this.form.get('userId').value) { + this.form.patchValue({ userId: '' }); + } else { + this.form.patchValue({ userId }); + } } - } - setUserRole(user, role) { - user.role = role; - } + setUserRole(user, role) { + user.role = role; + } - async ngOnInit(): Promise { - this.form = this.formBuilder.group({ - userId: new FormControl('', Validators.required), - }); + async ngOnInit(): Promise { + this.form = this.formBuilder.group({ + userId: new FormControl('', Validators.required), + }); - const token = this.oidcSecurityService.getIdToken(); - const decoded = jwt_decode(token) as any; - this.userId = decoded.sub; + const token = this.oidcSecurityService.getIdToken(); + const decoded = jwt_decode(token) as any; + this.userId = decoded.sub; - await this.getUsers(); - } + await this.getUsers(); + } - public async submit() { - this.submitted = true; - if (this.form.valid) { - try { - const userId = this.form.value.userId; - const selectedUsers = this.users.filter(x => x._id === userId); - if (selectedUsers.length) { - for (const user of selectedUsers) { - const userUpdate: Record = {role: Number(user.role)}; - await this.userService.updateById(this.form.value.userId, userUpdate).toPromise(); - } - this.alertService.success(this.updateSuccessMessage); - } else { - this.alertService.error(this.noUsersSelectedMessage); + public async submit() { + this.submitted = true; + if (this.form.valid) { + try { + const userId = this.form.value.userId; + const selectedUsers = this.users.filter((x) => x._id === userId); + if (selectedUsers.length) { + for (const user of selectedUsers) { + const userUpdate: Record = { role: Number(user.role) }; + await this.userService.updateById(this.form.value.userId, userUpdate).toPromise(); + } + this.alertService.success(this.updateSuccessMessage); + } else { + this.alertService.error(this.noUsersSelectedMessage); + } + } catch (e) { + this.alertService.error(e.error.message); + } } - } catch (e) { - this.alertService.error(e.error.message); - } + this.submitted = false; } - this.submitted = false; - } } diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts index d278229..8005563 100644 --- a/src/app/login/login.component.spec.ts +++ b/src/app/login/login.component.spec.ts @@ -3,23 +3,24 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { LoginComponent } from './login.component'; describe('LoginComponent', () => { - let component: LoginComponent; - let fixture: ComponentFixture; + let component: LoginComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ LoginComponent ], - }) - .compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LoginComponent], + }).compileComponents(); + }), + ); - beforeEach(() => { - fixture = TestBed.createComponent(LoginComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - xit('should create', () => { - expect(component).toBeTruthy(); - }); + xit('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 8f795d3..fe99c8c 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -1,33 +1,30 @@ -import { Router } from '@angular/router'; import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { OidcSecurityService } from 'angular-auth-oidc-client'; @Component({ - styleUrls: ['./login.component.scss'], - templateUrl: 'login.component.html', + styleUrls: ['./login.component.scss'], + templateUrl: 'login.component.html', }) export class LoginComponent implements OnInit { - public loading = false; - public submitted = false; - public returnUrl: string; + public loading = false; + public submitted = false; + public returnUrl: string; - public loginFailedMessage = $localize`:@@login.failed:Failed to login: Supply valid credentials.`; + public loginFailedMessage = $localize`:@@login.failed:Failed to login: Supply valid credentials.`; - constructor( - private readonly router: Router, - private readonly oidcSecurityService: OidcSecurityService, - ) { } + constructor(private readonly router: Router, private readonly oidcSecurityService: OidcSecurityService) {} - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { - console.log('is authenticated', auth); - if (auth) { - this.router.navigate(['/viewer']); - } - }); - } + ngOnInit() { + this.oidcSecurityService.checkAuth().subscribe((auth: boolean) => { + console.log('is authenticated', auth); + if (auth) { + this.router.navigate(['/viewer']); + } + }); + } - public login() { - this.oidcSecurityService.authorize(); - } + public login() { + this.oidcSecurityService.authorize(); + } } diff --git a/src/app/model/bodies/datastream-body.ts b/src/app/model/bodies/datastream-body.ts index b297032..0d1c384 100644 --- a/src/app/model/bodies/datastream-body.ts +++ b/src/app/model/bodies/datastream-body.ts @@ -1,15 +1,15 @@ export interface IDatastream { - datastreamId?: string; - name?: string; - reason?: string; - description?: string; - observedProperty?: string; - unitOfMeasurement?: string; - isPublic?: boolean; - isOpenData?: boolean; - isReusable?: boolean; - documentationUrl?: string; - dataLink?: string; - dataFrequency?: number; - dataQuality?: number; + datastreamId?: string; + name?: string; + reason?: string; + description?: string; + observedProperty?: string; + unitOfMeasurement?: string; + isPublic?: boolean; + isOpenData?: boolean; + isReusable?: boolean; + documentationUrl?: string; + dataLink?: string; + dataFrequency?: number; + dataQuality?: number; } diff --git a/src/app/model/bodies/device-model.ts b/src/app/model/bodies/device-model.ts index 34f3d57..933cc94 100644 --- a/src/app/model/bodies/device-model.ts +++ b/src/app/model/bodies/device-model.ts @@ -1,50 +1,50 @@ export interface IDatastream { - _id: string; - sensorId: string; - name: string; - description?: string; - unitOfMeasurement?: Record; - observationArea?: Record; - theme?: string[]; - dataQuality?: string; - isActive?: boolean; - isPublic?: boolean; - isOpenData?: boolean; - containsPersonalInfoData?: boolean; - isReusable?: boolean; - documentation?: string; - dataLink?: string; - observationGoalIds?: string[]; + _id: string; + sensorId: string; + name: string; + description?: string; + unitOfMeasurement?: Record; + observationArea?: Record; + theme?: string[]; + dataQuality?: string; + isActive?: boolean; + isPublic?: boolean; + isOpenData?: boolean; + containsPersonalInfoData?: boolean; + isReusable?: boolean; + documentation?: string; + dataLink?: string; + observationGoalIds?: string[]; } export interface ISensor { - _id: string; - name: string; - description?: string; - type?: string; - manufacturer?: string; - supplier?: string; - documentation?: string; + _id: string; + name: string; + description?: string; + type?: string; + manufacturer?: string; + supplier?: string; + documentation?: string; } export interface ILocationDetails { - _id?: string; - name?: string; - description?: string; + _id?: string; + name?: string; + description?: string; } export interface IDevice { - _id?: string; - name: string; - description?: string; - category?: string; - connectivity?: string; - locationDetails?: ILocationDetails; - location?: { - type: string, - coordinates: number[], - }; - sensors?: ISensor[]; - datastreams?: IDatastream[]; - canEdit?: boolean; + _id?: string; + name: string; + description?: string; + category?: string; + connectivity?: string; + locationDetails?: ILocationDetails; + location?: { + type: string; + coordinates: number[]; + }; + sensors?: ISensor[]; + datastreams?: IDatastream[]; + canEdit?: boolean; } diff --git a/src/app/model/bodies/legal-entity-id.ts b/src/app/model/bodies/legal-entity-id.ts index 6df5fd7..b4b7f2f 100644 --- a/src/app/model/bodies/legal-entity-id.ts +++ b/src/app/model/bodies/legal-entity-id.ts @@ -1,5 +1,3 @@ export class LegalEntityId { - constructor( - public legalEntityId: string, - ) {} + constructor(public legalEntityId: string) {} } diff --git a/src/app/model/bodies/location.ts b/src/app/model/bodies/location.ts index 4daa0b6..c138448 100644 --- a/src/app/model/bodies/location.ts +++ b/src/app/model/bodies/location.ts @@ -1,5 +1,5 @@ export interface ISensorLocation { - type: 'Point'; - /** [latitude, longitude, height] */ - coordinates: number[]; + type: 'Point'; + /** [latitude, longitude, height] */ + coordinates: number[]; } diff --git a/src/app/model/bodies/sensor-body.ts b/src/app/model/bodies/sensor-body.ts index 3259de2..c3fd2d0 100644 --- a/src/app/model/bodies/sensor-body.ts +++ b/src/app/model/bodies/sensor-body.ts @@ -1,21 +1,21 @@ import { ISensorLocation } from './location'; export interface ISensor { - _id: string; - nodeId: string; - organizations?: Array>; - name?: string; - location: ISensorLocation; - baseObjectId?: string; - datastreams?: Array; - aim?: string; - description?: string; - manufacturer?: string; - active: boolean; - observationArea?: object; - documentationUrl?: string; - theme?: Array; - category: string; - typeName: string; - typeDetails?: Record; + _id: string; + nodeId: string; + organizations?: Array>; + name?: string; + location: ISensorLocation; + baseObjectId?: string; + datastreams?: Array; + aim?: string; + description?: string; + manufacturer?: string; + active: boolean; + observationArea?: Record; + documentationUrl?: string; + theme?: Array; + category: string; + typeName: string; + typeDetails?: Record; } diff --git a/src/app/model/bodies/sensorTheme.ts b/src/app/model/bodies/sensorTheme.ts index 8cfd5a4..65ca34d 100644 --- a/src/app/model/bodies/sensorTheme.ts +++ b/src/app/model/bodies/sensorTheme.ts @@ -1,46 +1,45 @@ export enum SensorTheme { - Energy = 'Energy', - Agriculture = 'Agriculture', - Transport = 'Transport', - Waste = 'Waste', - SoilAndUnderground = 'SoilAndUnderground', - Sound = 'Sound', - EnvironmentalChange = 'EnvironmentalChange', - Air = 'Air', - NatureAndLandscape = 'NatureAndLandscape', - Water = 'Water', - Safety = 'Safety', - SpatialPlanning = 'SpatialPlanning', - Watermanagement = 'Watermanagement', - Aviation = 'Aviation', - PublicTransport = 'PublicTransport', - RailAndRoadTraffic = 'RailAndRoadTraffic', - Shipping = 'Shipping', - Construction = 'Construction', - Housing = 'Housing', - Health = 'Health', + Energy = 'Energy', + Agriculture = 'Agriculture', + Transport = 'Transport', + Waste = 'Waste', + SoilAndUnderground = 'SoilAndUnderground', + Sound = 'Sound', + EnvironmentalChange = 'EnvironmentalChange', + Air = 'Air', + NatureAndLandscape = 'NatureAndLandscape', + Water = 'Water', + Safety = 'Safety', + SpatialPlanning = 'SpatialPlanning', + Watermanagement = 'Watermanagement', + Aviation = 'Aviation', + PublicTransport = 'PublicTransport', + RailAndRoadTraffic = 'RailAndRoadTraffic', + Shipping = 'Shipping', + Construction = 'Construction', + Housing = 'Housing', + Health = 'Health', } export const SensorThemeTranslation = { - Energy: $localize`:@@theme.energy:Energy`, - Agriculture: $localize`:@@theme.agriculture:Agriculture`, - Transport: $localize`:@@theme.transport:Transport`, - Waste: $localize`:@@theme.waste:Waste`, - SoilAndUnderground: $localize`:@@theme.soilandunderground:SoilAndUnderground`, - Sound: $localize`:@@theme.sound:Sound`, - EnvironmentalChange: $localize`:@@theme.environmentalchange:EnvironmentalChange`, - Air: $localize`:@@theme.air:Air`, - NatureAndLandscape: $localize`:@@theme.natureandlandscape:NatureAndLandscape`, - Water: $localize`:@@theme.water:Water`, - Safety: $localize`:@@theme.safety:Safety`, - SpatialPlanning: $localize`:@@theme.spatialplanning:SpatialPlanning`, - Watermanagement: $localize`:@@theme.watermanagement:Watermanagement`, - Aviation: $localize`:@@theme.aviation:Aviation`, - PublicTransport: $localize`:@@theme.publictransport:PublicTransport`, - RailAndRoadTraffic: $localize`:@@theme.railandroadtraffic:RailAndRoadTraffic`, - Shipping: $localize`:@@theme.shipping:Shipping`, - Construction: $localize`:@@theme.construction:Construction`, - Housing: $localize`:@@theme.housing:Housing`, - Health: $localize`:@@theme.health:Health`, - + Energy: $localize`:@@theme.energy:Energy`, + Agriculture: $localize`:@@theme.agriculture:Agriculture`, + Transport: $localize`:@@theme.transport:Transport`, + Waste: $localize`:@@theme.waste:Waste`, + SoilAndUnderground: $localize`:@@theme.soilandunderground:SoilAndUnderground`, + Sound: $localize`:@@theme.sound:Sound`, + EnvironmentalChange: $localize`:@@theme.environmentalchange:EnvironmentalChange`, + Air: $localize`:@@theme.air:Air`, + NatureAndLandscape: $localize`:@@theme.natureandlandscape:NatureAndLandscape`, + Water: $localize`:@@theme.water:Water`, + Safety: $localize`:@@theme.safety:Safety`, + SpatialPlanning: $localize`:@@theme.spatialplanning:SpatialPlanning`, + Watermanagement: $localize`:@@theme.watermanagement:Watermanagement`, + Aviation: $localize`:@@theme.aviation:Aviation`, + PublicTransport: $localize`:@@theme.publictransport:PublicTransport`, + RailAndRoadTraffic: $localize`:@@theme.railandroadtraffic:RailAndRoadTraffic`, + Shipping: $localize`:@@theme.shipping:Shipping`, + Construction: $localize`:@@theme.construction:Construction`, + Housing: $localize`:@@theme.housing:Housing`, + Health: $localize`:@@theme.health:Health`, }; diff --git a/src/app/model/bodies/sensorTypes.spec.ts b/src/app/model/bodies/sensorTypes.spec.ts index cf64673..50da1e4 100644 --- a/src/app/model/bodies/sensorTypes.spec.ts +++ b/src/app/model/bodies/sensorTypes.spec.ts @@ -39,6 +39,6 @@ describe('sensorTypes NL translation', () => { }); }); -function testInstanceExists(types: object) { +function testInstanceExists(types: Record) { expect(types).toBeTruthy(); } diff --git a/src/app/model/bodies/sensorTypes.ts b/src/app/model/bodies/sensorTypes.ts index feda3bc..ef32d1d 100644 --- a/src/app/model/bodies/sensorTypes.ts +++ b/src/app/model/bodies/sensorTypes.ts @@ -1,17 +1,17 @@ export enum Category { - Beacon = 'Beacon', - Sensor = 'Sensor', - Camera = 'Camera', + Beacon = 'Beacon', + Sensor = 'Sensor', + Camera = 'Camera', } const CategoryTranslation = { - Beacon: $localize`:@@type.beacon:Beacon`, - Sensor: $localize`:@@type.sensor:Sensor`, - Camera: $localize`:@@type.camera:Camera`, + Beacon: $localize`:@@type.beacon:Beacon`, + Sensor: $localize`:@@type.sensor:Sensor`, + Camera: $localize`:@@type.camera:Camera`, }; export function getCategoryTranslation(category) { - return CategoryTranslation[category] ? CategoryTranslation[category] : category; + return CategoryTranslation[category] ? CategoryTranslation[category] : category; } /* @@ -37,411 +37,419 @@ export function getCategoryTranslation(category) { */ const soundSensorTypesArrayWithTranslations = [ - ['Sound sensor', 'Geluidsensor'], - ['Geophone', 'Geofoon'], - ['Hydrophone', 'Hydrofoon'], - ['Microphone', 'Microfoon'], - ['Seismometer', 'Seismometer'], - ['SoundLevelMeter', 'Geluidsmeter'], - ['Sound locator', 'Geluidszoeker'], + ['Sound sensor', 'Geluidsensor'], + ['Geophone', 'Geofoon'], + ['Hydrophone', 'Hydrofoon'], + ['Microphone', 'Microfoon'], + ['Seismometer', 'Seismometer'], + ['SoundLevelMeter', 'Geluidsmeter'], + ['Sound locator', 'Geluidszoeker'], ]; const chemicalSensorTypesArrayWithTranslations = [ - ['Chemical sensor', 'Chemiesensor'], - ['Carbon dioxide sensor', 'Kooldioxide-sensor'], - ['Carbon monoxide detector', 'Koolmonoxidedetector'], - ['Catalytic bead sensor', 'Katalytische parelsensor'], - ['Chemical field-effect transistor', 'Chemische veldeffecttransistor'], - ['Chemiresistor', 'Chemiresistor'], - ['Electrochemical gas sensor', 'Elektrochemische gassensor'], - ['Electronic nose', 'Elektronische neus'], - ['Electrolyte–insulator–semiconductor sensor', 'Elektrolytisolator - halfgeleidersensor'], - ['Energy-dispersive X-ray spectroscopy', 'Energiedispersieve röntgenspectroscopie'], - ['Fluorescent chloride sensors', 'Fluorescerende chloride-sensoren'], - ['Holographic sensor', 'Holografische sensor'], - ['Hydrocarbon dew point analyzer', 'Koolwaterstof dauwpuntanalysator'], - ['Hydrogen sensor', 'Waterstofsensor'], - ['Hydrogen sulfide sensor', 'Waterstofsulfide-sensor'], - ['Infrared point sensor', 'Infrarood puntsensor'], - ['Ion-selective electrode', 'Ionselectieve elektrode'], - ['ISFET', 'ISFET'], - ['Nondispersive infrared sensor', 'Niet-dispersieve infraroodsensor'], - ['Microwave chemistry sensor', 'Microgolfchemiesensor'], - ['Nitrogen oxide sensor', 'Stikstofoxide sensor'], - ['Nondispersive infrared sensor', 'Niet-dispersieve infraroodsensor'], - ['Olfactometer', 'Olfactometer'], - ['Optode', 'Optode'], - ['Oxygen sensor', 'Zuurstof sensor'], - ['Ozone monitor', 'Ozon-monitor'], - ['Pellistor', 'Pellistor'], - ['pH glass electrode', 'pH-glaselektrode'], - ['Potentiometric sensor', 'Potentiometrische sensor'], - ['Redox electrode', 'Redox-elektrode'], - ['Smoke detector', 'Rookdetector'], - ['Zinc oxide nanorod sensor', 'Zinkoxide nanostaaf sensor'], + ['Chemical sensor', 'Chemiesensor'], + ['Carbon dioxide sensor', 'Kooldioxide-sensor'], + ['Carbon monoxide detector', 'Koolmonoxidedetector'], + ['Catalytic bead sensor', 'Katalytische parelsensor'], + ['Chemical field-effect transistor', 'Chemische veldeffecttransistor'], + ['Chemiresistor', 'Chemiresistor'], + ['Electrochemical gas sensor', 'Elektrochemische gassensor'], + ['Electronic nose', 'Elektronische neus'], + ['Electrolyte–insulator–semiconductor sensor', 'Elektrolytisolator - halfgeleidersensor'], + ['Energy-dispersive X-ray spectroscopy', 'Energiedispersieve röntgenspectroscopie'], + ['Fluorescent chloride sensors', 'Fluorescerende chloride-sensoren'], + ['Holographic sensor', 'Holografische sensor'], + ['Hydrocarbon dew point analyzer', 'Koolwaterstof dauwpuntanalysator'], + ['Hydrogen sensor', 'Waterstofsensor'], + ['Hydrogen sulfide sensor', 'Waterstofsulfide-sensor'], + ['Infrared point sensor', 'Infrarood puntsensor'], + ['Ion-selective electrode', 'Ionselectieve elektrode'], + ['ISFET', 'ISFET'], + ['Nondispersive infrared sensor', 'Niet-dispersieve infraroodsensor'], + ['Microwave chemistry sensor', 'Microgolfchemiesensor'], + ['Nitrogen oxide sensor', 'Stikstofoxide sensor'], + ['Nondispersive infrared sensor', 'Niet-dispersieve infraroodsensor'], + ['Olfactometer', 'Olfactometer'], + ['Optode', 'Optode'], + ['Oxygen sensor', 'Zuurstof sensor'], + ['Ozone monitor', 'Ozon-monitor'], + ['Pellistor', 'Pellistor'], + ['pH glass electrode', 'pH-glaselektrode'], + ['Potentiometric sensor', 'Potentiometrische sensor'], + ['Redox electrode', 'Redox-elektrode'], + ['Smoke detector', 'Rookdetector'], + ['Zinc oxide nanorod sensor', 'Zinkoxide nanostaaf sensor'], ]; const electricCurrentSensorTypesArrayWithTranslations = [ - ['Electric current sensor', 'Electriciteitssensor'], - ['Current sensor', 'Stroomsensor'], - ['Daly detector', 'Daly-detector'], - ['Electroscope', 'Elektroscoop'], - ['Electron multiplier', 'Elektronenvermenigvuldiger'], - ['Faraday cup', 'Faraday beker'], - ['Galvanometer', 'Galvanometer'], - ['Hall effect sensor', 'Hall-effect sensor'], - ['Hall probe', 'Hall-sonde'], - ['Magnetic anomaly detector', 'Magnetische afwijkingsdetector'], - ['Magnetometer', 'Magnetometer'], - ['Magnetoresistance', 'Magnetoweerstand'], - ['MEMS magnetic field sensor', 'MEMS magnetische veldsensor'], - ['Metal detector', 'Metaaldetector'], - ['Planar Hall sensor', 'Planar Hall-sensor'], - ['Radio direction finder', 'Radiorichtingzoeker'], - ['Voltage detector', 'Spanningsdetector'], - + ['Electric current sensor', 'Electriciteitssensor'], + ['Current sensor', 'Stroomsensor'], + ['Daly detector', 'Daly-detector'], + ['Electroscope', 'Elektroscoop'], + ['Electron multiplier', 'Elektronenvermenigvuldiger'], + ['Faraday cup', 'Faraday beker'], + ['Galvanometer', 'Galvanometer'], + ['Hall effect sensor', 'Hall-effect sensor'], + ['Hall probe', 'Hall-sonde'], + ['Magnetic anomaly detector', 'Magnetische afwijkingsdetector'], + ['Magnetometer', 'Magnetometer'], + ['Magnetoresistance', 'Magnetoweerstand'], + ['MEMS magnetic field sensor', 'MEMS magnetische veldsensor'], + ['Metal detector', 'Metaaldetector'], + ['Planar Hall sensor', 'Planar Hall-sensor'], + ['Radio direction finder', 'Radiorichtingzoeker'], + ['Voltage detector', 'Spanningsdetector'], ]; const environmentalSensorWeatherStationTypesArrayWithTranslations = [ - ['Environmental sensor / weather station', 'Klimaatsensor'], - ['Actinometer', 'Actinometer'], - ['Air pollution sensor', 'Luchtvervuilingssensor'], - ['Ceilometer', 'Plafondmeter'], - ['Dew warning', 'Dauw waarschuwing'], - ['Electrochemical gas sensor', 'Elektrochemische gassensor'], - ['Fish counter', 'Visteller'], - ['Frequency domain sensor', 'Frequentiedomeinsensor'], - ['Gas detector', 'Gas detector'], - ['Hook gauge evaporimeter', 'Hook gauge verdampingsmeter'], - ['Humistor', 'Humistor'], - ['Hygrometer', 'Hygrometer'], - ['Leaf sensor', 'Blad sensor'], - ['Lysimeter', 'Lysimeter'], - ['Pyranometer', 'Pyranometer'], - ['Pyrgeometer', 'Pyrgeometer'], - ['Psychrometer', 'Psychrometer'], - ['Rain gauge', 'Regenmeter'], - ['Rain sensor', 'Regensensor'], - ['Seismometer', 'Seismometer'], - ['SNOTEL', 'SNOTEL'], - ['Snow gauge', 'Sneeuwmeter'], - ['Soil moisture sensor', 'Bodemvochtsensor'], - ['Stream gauge', 'Stroommeter'], - ['Tide gauge', 'Getijdenmeter'], - ['Weather radar', 'Weerradar'], + ['Environmental sensor / weather station', 'Klimaatsensor'], + ['Actinometer', 'Actinometer'], + ['Air pollution sensor', 'Luchtvervuilingssensor'], + ['Ceilometer', 'Plafondmeter'], + ['Dew warning', 'Dauw waarschuwing'], + ['Electrochemical gas sensor', 'Elektrochemische gassensor'], + ['Fish counter', 'Visteller'], + ['Frequency domain sensor', 'Frequentiedomeinsensor'], + ['Gas detector', 'Gas detector'], + ['Hook gauge evaporimeter', 'Hook gauge verdampingsmeter'], + ['Humistor', 'Humistor'], + ['Hygrometer', 'Hygrometer'], + ['Leaf sensor', 'Blad sensor'], + ['Lysimeter', 'Lysimeter'], + ['Pyranometer', 'Pyranometer'], + ['Pyrgeometer', 'Pyrgeometer'], + ['Psychrometer', 'Psychrometer'], + ['Rain gauge', 'Regenmeter'], + ['Rain sensor', 'Regensensor'], + ['Seismometer', 'Seismometer'], + ['SNOTEL', 'SNOTEL'], + ['Snow gauge', 'Sneeuwmeter'], + ['Soil moisture sensor', 'Bodemvochtsensor'], + ['Stream gauge', 'Stroommeter'], + ['Tide gauge', 'Getijdenmeter'], + ['Weather radar', 'Weerradar'], ]; const flowSensorTypesArrayWithTranslations = [ - ['Flow sensor', 'Vloeistof- en gaststroomsensor'], - ['Air flow meter', 'Luchtstroommeter'], - ['Anemometer', 'Anemometer'], - ['Flow sensor', 'Stroomsensor'], - ['Gas meter', 'Gas meter'], - ['Mass flow sensor', 'Massastroomsensor'], - ['Water meter', 'Watermeter'], + ['Flow sensor', 'Vloeistof- en gaststroomsensor'], + ['Air flow meter', 'Luchtstroommeter'], + ['Anemometer', 'Anemometer'], + ['Flow sensor', 'Stroomsensor'], + ['Gas meter', 'Gas meter'], + ['Mass flow sensor', 'Massastroomsensor'], + ['Water meter', 'Watermeter'], ]; const positionDisplacementSensorTypesArrayWithTranslations = [ - ['Position and displacement sensor', 'Positie- of verplaatsingsensor'], - ['Accelerometer', 'Versnellingsmeter'], - ['Auxanometer', 'Auxanometer'], - ['Capacitive displacement sensor', 'Capacitieve verplaatsingssensor'], - ['Capacitive sensing', 'Capacitieve detectie'], - ['DetectionLoop', 'Detectielus'], - ['Flex sensor', 'Flex-sensor'], - ['Free fall sensor', 'Vrije val sensor'], - ['Gravimeter', 'Gravimeter'], - ['Gyroscopic sensor', 'Gyroscopische sensor'], - ['HeightDetectionDevice', 'Hoogte sensor'], - ['Impact sensor', 'Impact sensor'], - ['Inclinometer', 'Hellingsmeter'], - ['Incremental encoder', 'Incrementele encoder'], - ['Integrated circuit piezoelectric sensor', 'Piëzo-elektrische sensor met geïntegreerde schakeling'], - ['Laser rangefinder', 'Laser afstandsmeter'], - ['Laser surface velocimeter', 'Laser oppervlaktesnelheidsmeter'], - ['LIDAR', 'LIDAR'], - ['Linear encoder', 'Lineaire encoder'], - ['Linear variable differential transformer (LVDT)', 'Lineaire variabele differentiële transformator (LVDT)'], - ['Liquid capacitive inclinometers', 'Vloeibare capacitieve inclinometers'], - ['Odometer', 'Kilometerteller'], - ['Photoelectric sensor', 'Foto-elektrische sensor'], - ['Piezoelectric accelerometer', 'Piëzo-elektrische versnellingsmeter'], - ['Position sensor', 'Positiesensor'], - ['Position sensitive device', 'Positiegevoelig apparaat'], - ['Angular rate sensor', 'Hoeksensor'], - ['Rotary encoder', 'Draaiknop'], - ['Rotary variable differential transformer', 'Roterende variabele differentiële transformator'], - ['Selsyn', 'Selsyn'], - ['Shock detector', 'Schokdetector'], - ['Shock data logger', 'Datalogger voor schokken'], - ['SpeedDetectionDevice', 'SpeedDetectionDevice'], - ['Sudden Motion Sensor', 'Plotselinge bewegingssensor'], - ['Tilt sensor', 'Kantel sensor'], - ['Tachometer', 'Toerenteller'], - ['Ultrasonic thickness gauge', 'Ultrasone diktemeter'], - ['Ultra-wideband radar', 'Ultrabreedbandradar'], - ['Variable reluctance sensor', 'Variabele reluctantiesensor'], - ['Velocity receiver', 'Snelheidsontvanger'], + ['Position and displacement sensor', 'Positie- of verplaatsingsensor'], + ['Accelerometer', 'Versnellingsmeter'], + ['Auxanometer', 'Auxanometer'], + ['Capacitive displacement sensor', 'Capacitieve verplaatsingssensor'], + ['Capacitive sensing', 'Capacitieve detectie'], + ['DetectionLoop', 'Detectielus'], + ['Flex sensor', 'Flex-sensor'], + ['Free fall sensor', 'Vrije val sensor'], + ['Gravimeter', 'Gravimeter'], + ['Gyroscopic sensor', 'Gyroscopische sensor'], + ['HeightDetectionDevice', 'Hoogte sensor'], + ['Impact sensor', 'Impact sensor'], + ['Inclinometer', 'Hellingsmeter'], + ['Incremental encoder', 'Incrementele encoder'], + ['Integrated circuit piezoelectric sensor', 'Piëzo-elektrische sensor met geïntegreerde schakeling'], + ['Laser rangefinder', 'Laser afstandsmeter'], + ['Laser surface velocimeter', 'Laser oppervlaktesnelheidsmeter'], + ['LIDAR', 'LIDAR'], + ['Linear encoder', 'Lineaire encoder'], + ['Linear variable differential transformer (LVDT)', 'Lineaire variabele differentiële transformator (LVDT)'], + ['Liquid capacitive inclinometers', 'Vloeibare capacitieve inclinometers'], + ['Odometer', 'Kilometerteller'], + ['Photoelectric sensor', 'Foto-elektrische sensor'], + ['Piezoelectric accelerometer', 'Piëzo-elektrische versnellingsmeter'], + ['Position sensor', 'Positiesensor'], + ['Position sensitive device', 'Positiegevoelig apparaat'], + ['Angular rate sensor', 'Hoeksensor'], + ['Rotary encoder', 'Draaiknop'], + ['Rotary variable differential transformer', 'Roterende variabele differentiële transformator'], + ['Selsyn', 'Selsyn'], + ['Shock detector', 'Schokdetector'], + ['Shock data logger', 'Datalogger voor schokken'], + ['SpeedDetectionDevice', 'SpeedDetectionDevice'], + ['Sudden Motion Sensor', 'Plotselinge bewegingssensor'], + ['Tilt sensor', 'Kantel sensor'], + ['Tachometer', 'Toerenteller'], + ['Ultrasonic thickness gauge', 'Ultrasone diktemeter'], + ['Ultra-wideband radar', 'Ultrabreedbandradar'], + ['Variable reluctance sensor', 'Variabele reluctantiesensor'], + ['Velocity receiver', 'Snelheidsontvanger'], ]; const opticalCameraSensorTypesArrayWithTranslations = [ - ['Optical / camerasensor', 'Optische sensor'], - ['CMOS sensor', 'CMOS-sensor'], - ['Colorimeter', 'Colorimeter'], - ['Contact image sensor', 'Contact beeldsensor'], - ['Electro-optical sensor', 'Elektro-optische sensor'], - ['Flame detector', 'Vlam detector'], - ['Infra-red sensor', 'Infrarood sensor'], - ['Kinetic inductance detector', 'Kinetische inductantiedetector'], - ['LED as light sensor', 'LED als lichtsensor'], - ['Light-addressable potentiometric sensor', 'Lichtadresseerbare potentiometrische sensor'], - ['Nichols radiometer', 'Nichols radiometer'], - ['Fiber optic sensors', 'Vezeloptische sensoren'], - ['Optical position sensor', 'Optische positiesensor'], - ['Thermopile laser sensors', 'Thermozuil lasersensoren'], - ['Photodetector', 'Fotodetector'], - ['Photodiode', 'Fotodiode'], - ['Photomultiplier', 'Fotomultiplicator'], - ['Photomultiplier tube', 'Fotomultiplicatorbuis'], - ['Phototransistor', 'Fototransistor'], - ['Photoelectric sensor', 'Foto-elektrische sensor'], - ['Photoionization detector', 'Foto-ionisatiedetector'], - ['Photomultiplier', 'Fotomultiplicator'], - ['Photoresistor', 'Fotoweerstand'], - ['Photoswitch', 'Photoswitch'], - ['Phototube', 'Fototube'], - ['Scintillometer', 'Scintillometer'], - ['Shack–Hartmann wavefront sensor', 'Shack-Hartmann-golffrontsensor'], - ['Single-photon avalanche diode', 'Lawinediode met één foton'], - ['Superconducting nanowire single-photon detector', 'Supergeleidende nanodraad enkelfotondetector'], - ['Transition-edge sensor', 'Overgangsrand sensor'], - ['UVSensor', 'UV-sensor'], - ['Visible Light Photon Counter', 'Zichtbaar licht foton-teller'], - ['Wavefront sensor', 'Wavefront-sensor'], + ['Optical / camerasensor', 'Optische sensor'], + ['CMOS sensor', 'CMOS-sensor'], + ['Colorimeter', 'Colorimeter'], + ['Contact image sensor', 'Contact beeldsensor'], + ['Electro-optical sensor', 'Elektro-optische sensor'], + ['Flame detector', 'Vlam detector'], + ['Infra-red sensor', 'Infrarood sensor'], + ['Kinetic inductance detector', 'Kinetische inductantiedetector'], + ['LED as light sensor', 'LED als lichtsensor'], + ['Light-addressable potentiometric sensor', 'Lichtadresseerbare potentiometrische sensor'], + ['Nichols radiometer', 'Nichols radiometer'], + ['Fiber optic sensors', 'Vezeloptische sensoren'], + ['Optical position sensor', 'Optische positiesensor'], + ['Thermopile laser sensors', 'Thermozuil lasersensoren'], + ['Photodetector', 'Fotodetector'], + ['Photodiode', 'Fotodiode'], + ['Photomultiplier', 'Fotomultiplicator'], + ['Photomultiplier tube', 'Fotomultiplicatorbuis'], + ['Phototransistor', 'Fototransistor'], + ['Photoelectric sensor', 'Foto-elektrische sensor'], + ['Photoionization detector', 'Foto-ionisatiedetector'], + ['Photomultiplier', 'Fotomultiplicator'], + ['Photoresistor', 'Fotoweerstand'], + ['Photoswitch', 'Photoswitch'], + ['Phototube', 'Fototube'], + ['Scintillometer', 'Scintillometer'], + ['Shack–Hartmann wavefront sensor', 'Shack-Hartmann-golffrontsensor'], + ['Single-photon avalanche diode', 'Lawinediode met één foton'], + ['Superconducting nanowire single-photon detector', 'Supergeleidende nanodraad enkelfotondetector'], + ['Transition-edge sensor', 'Overgangsrand sensor'], + ['UVSensor', 'UV-sensor'], + ['Visible Light Photon Counter', 'Zichtbaar licht foton-teller'], + ['Wavefront sensor', 'Wavefront-sensor'], ]; const pressureSensorTypesArrayWithTranslations = [ - ['Pressure sensor', 'Druksensor'], - ['Barograph', 'Barograaf'], - ['Barometer', 'Barometer'], - ['Bourdon gauge', 'Bourdonmeter'], - ['Hot filament ionization gauge', 'Heet filament ionisatie meter'], - ['Ionization gauge', 'Ionisatiemeter'], - ['McLeod gauge', 'McLeod-meter'], - ['Oscillating U-tube', 'Oscillerende U-buis'], - ['Piezometer', 'Piëzometer'], - ['Pirani gauge', 'Pirani-meter'], - ['Pressure sensor', 'Druksensor'], - ['Pressure gauge', 'Drukmeter'], - ['Tactile sensor', 'Tastsensor'], - ['Time pressure gauge', 'Tijd manometer'], + ['Pressure sensor', 'Druksensor'], + ['Barograph', 'Barograaf'], + ['Barometer', 'Barometer'], + ['Bourdon gauge', 'Bourdonmeter'], + ['Hot filament ionization gauge', 'Heet filament ionisatie meter'], + ['Ionization gauge', 'Ionisatiemeter'], + ['McLeod gauge', 'McLeod-meter'], + ['Oscillating U-tube', 'Oscillerende U-buis'], + ['Piezometer', 'Piëzometer'], + ['Pirani gauge', 'Pirani-meter'], + ['Pressure sensor', 'Druksensor'], + ['Pressure gauge', 'Drukmeter'], + ['Tactile sensor', 'Tastsensor'], + ['Time pressure gauge', 'Tijd manometer'], ]; const densitySensorTypesArrayWithTranslations = [ - ['Density sensor', 'Dichtheidsensor'], - ['Bhangmeter', 'Bhangmeter'], - ['Hydrometer', 'Hydrometer'], - ['Force gauge / sensor', 'Krachtmeter/-sensor'], - ['Level sensor', 'Niveausensor'], - ['Magnetic level gauge', 'Magnetische niveaumeter'], - ['Piezocapacitive pressure sensor', 'Piëzocapacitieve druksensor'], - ['Piezoelectric sensor', 'Piëzo-elektrische sensor'], - ['Strain gauge', 'Spanningsmeter'], - ['Torque sensor', 'Koppel sensor'], - ['Viscometer', 'Viscometer'], + ['Density sensor', 'Dichtheidsensor'], + ['Bhangmeter', 'Bhangmeter'], + ['Hydrometer', 'Hydrometer'], + ['Force gauge / sensor', 'Krachtmeter/-sensor'], + ['Level sensor', 'Niveausensor'], + ['Magnetic level gauge', 'Magnetische niveaumeter'], + ['Piezocapacitive pressure sensor', 'Piëzocapacitieve druksensor'], + ['Piezoelectric sensor', 'Piëzo-elektrische sensor'], + ['Strain gauge', 'Spanningsmeter'], + ['Torque sensor', 'Koppel sensor'], + ['Viscometer', 'Viscometer'], ]; const temperatureSensorTypesArrayWithTranslations = [ - ['Temperature sensor', 'Temperatuursensor'], - ['Bolometer', 'Bolometer'], - ['Flame detection', 'Vlamdetectie'], - ['Gardon gauge', 'Gardon-meter'], - ['Golay cell', 'Golay-cel'], - ['Heat flux sensor', 'Warmtefluxsensor'], - ['Infrared thermometer', 'Infrarood thermometer'], - ['Microwave radiometer', 'Microgolfmeter'], - ['Net radiometer', 'Netto radiometer'], - ['Quartz thermometer', 'Kwarts thermometer'], - ['Resistance thermometer', 'Weerstandsthermometer'], - ['Silicon bandgap temperature sensor', 'Silicium bandgap temperatuursensor'], - ['Special sensor microwave/imager', 'Speciale sensor microgolf / imager'], - ['Temperature gauge', 'Temperatuurmeter'], - ['Thermistor', 'Thermistor'], - ['Thermocouple', 'Thermokoppel'], - ['Thermometer', 'Thermometer'], - ['Pyrometer', 'Pyrometer'], + ['Temperature sensor', 'Temperatuursensor'], + ['Bolometer', 'Bolometer'], + ['Flame detection', 'Vlamdetectie'], + ['Gardon gauge', 'Gardon-meter'], + ['Golay cell', 'Golay-cel'], + ['Heat flux sensor', 'Warmtefluxsensor'], + ['Infrared thermometer', 'Infrarood thermometer'], + ['Microwave radiometer', 'Microgolfmeter'], + ['Net radiometer', 'Netto radiometer'], + ['Quartz thermometer', 'Kwarts thermometer'], + ['Resistance thermometer', 'Weerstandsthermometer'], + ['Silicon bandgap temperature sensor', 'Silicium bandgap temperatuursensor'], + ['Special sensor microwave/imager', 'Speciale sensor microgolf / imager'], + ['Temperature gauge', 'Temperatuurmeter'], + ['Thermistor', 'Thermistor'], + ['Thermocouple', 'Thermokoppel'], + ['Thermometer', 'Thermometer'], + ['Pyrometer', 'Pyrometer'], ]; const proximitySensorTypesArrayWithTranslations = [ - ['Proximity sensor', 'Aanwezigheid of nabijheidsensor'], - ['Alarm sensor', 'Alarmsensor'], - ['Doppler radar', 'Doppler-radar'], - ['Motion detector', 'Bewegingsdetector'], - ['Occupancy sensor', 'Aanwezigheidssensor'], - ['Proximity sensor', 'Nabijheidssensor'], - ['Passive infrared sensor', 'Passieve infraroodsensor'], - ['Reed switch', 'Reed-schakelaar'], - ['Stud finder', 'Stud finder'], - ['Triangulation sensor', 'Triangulatie sensor'], - ['Touch switch', 'Touch schakelaar'], - ['BioFET', 'BioFET'], - ['Biochip', 'Biochip'], - ['Biosensor', 'Biosensor'], - ['Capacitance probe', 'Capaciteit sonde'], - ['Capacitance sensor', 'Capaciteitssensor'], - ['Catadioptric sensor', 'Catadioptrische sensor'], - ['Carbon paste electrode', 'Koolstofpasta-elektrode'], - ['Digital sensors', 'Digitale sensoren'], - ['Displacement receiver', 'Verplaatsingsontvanger'], - ['Electromechanical film', 'Elektromechanische film'], - ['Electro-optical sensor', 'Elektro-optische sensor'], - ['Electrochemical fatigue crack sensor', 'Elektrochemische vermoeidheidsscheursensor'], - ['Fabry–Pérot interferometer', 'Fabry-Pérot-interferometer'], - ['Fisheries acoustics', 'Akoestiek in de visserij'], - ['Image sensor', 'Beeldsensor'], - ['Image sensor format', 'Formaat beeldsensor'], - ['Inductive sensor', 'Inductie sensor'], - ['Intelligent sensor', 'Intelligente sensor'], - ['Lab-on-a-chip', 'Lab-op-een-chip'], - ['Leaf sensor', 'Blad sensor'], - ['Machine vision', 'Machine visie'], - ['Microelectromechanical systems', 'Micro-elektromechanische systemen'], - ['MOSFET', 'MOSFET'], - ['Photoelasticity', 'Foto-elasticiteit'], - ['Quantum sensor', 'Quantumsensor'], - ['Radar', 'Radar'], - ['Ground-penetrating radar', 'Gronddoordringende radar'], - ['Synthetic aperture radar', 'Synthetische diafragma-radar'], - ['Radar tracker', 'Radartracker'], - ['Stretch sensor', 'Uitrekbare sensor'], - ['Sensor array', 'Sensorreeks'], - ['Sensor fusion', 'Sensor fusie'], - ['Sensor grid', 'Sensor rooster'], - ['Sensor node', 'Sensorknooppunt'], - ['Soft sensor', 'Zachte sensor'], - ['Sonar', 'Sonar'], - ['Staring array', 'Starende array'], - ['Transducer', 'Omvormer'], - ['Ultrasonic sensor', 'Ultrasoon sensor'], - ['Video sensor', 'Videosensor'], - ['Visual sensor network', 'Visueel sensornetwerk'], - ['Wheatstone bridge', 'Wheatstone-brug'], - ['Wireless sensor network', 'Draadloos sensornetwerk'], + ['Proximity sensor', 'Aanwezigheid of nabijheidsensor'], + ['Alarm sensor', 'Alarmsensor'], + ['Doppler radar', 'Doppler-radar'], + ['Motion detector', 'Bewegingsdetector'], + ['Occupancy sensor', 'Aanwezigheidssensor'], + ['Proximity sensor', 'Nabijheidssensor'], + ['Passive infrared sensor', 'Passieve infraroodsensor'], + ['Reed switch', 'Reed-schakelaar'], + ['Stud finder', 'Stud finder'], + ['Triangulation sensor', 'Triangulatie sensor'], + ['Touch switch', 'Touch schakelaar'], + ['BioFET', 'BioFET'], + ['Biochip', 'Biochip'], + ['Biosensor', 'Biosensor'], + ['Capacitance probe', 'Capaciteit sonde'], + ['Capacitance sensor', 'Capaciteitssensor'], + ['Catadioptric sensor', 'Catadioptrische sensor'], + ['Carbon paste electrode', 'Koolstofpasta-elektrode'], + ['Digital sensors', 'Digitale sensoren'], + ['Displacement receiver', 'Verplaatsingsontvanger'], + ['Electromechanical film', 'Elektromechanische film'], + ['Electro-optical sensor', 'Elektro-optische sensor'], + ['Electrochemical fatigue crack sensor', 'Elektrochemische vermoeidheidsscheursensor'], + ['Fabry–Pérot interferometer', 'Fabry-Pérot-interferometer'], + ['Fisheries acoustics', 'Akoestiek in de visserij'], + ['Image sensor', 'Beeldsensor'], + ['Image sensor format', 'Formaat beeldsensor'], + ['Inductive sensor', 'Inductie sensor'], + ['Intelligent sensor', 'Intelligente sensor'], + ['Lab-on-a-chip', 'Lab-op-een-chip'], + ['Leaf sensor', 'Blad sensor'], + ['Machine vision', 'Machine visie'], + ['Microelectromechanical systems', 'Micro-elektromechanische systemen'], + ['MOSFET', 'MOSFET'], + ['Photoelasticity', 'Foto-elasticiteit'], + ['Quantum sensor', 'Quantumsensor'], + ['Radar', 'Radar'], + ['Ground-penetrating radar', 'Gronddoordringende radar'], + ['Synthetic aperture radar', 'Synthetische diafragma-radar'], + ['Radar tracker', 'Radartracker'], + ['Stretch sensor', 'Uitrekbare sensor'], + ['Sensor array', 'Sensorreeks'], + ['Sensor fusion', 'Sensor fusie'], + ['Sensor grid', 'Sensor rooster'], + ['Sensor node', 'Sensorknooppunt'], + ['Soft sensor', 'Zachte sensor'], + ['Sonar', 'Sonar'], + ['Staring array', 'Starende array'], + ['Transducer', 'Omvormer'], + ['Ultrasonic sensor', 'Ultrasoon sensor'], + ['Video sensor', 'Videosensor'], + ['Visual sensor network', 'Visueel sensornetwerk'], + ['Wheatstone bridge', 'Wheatstone-brug'], + ['Wireless sensor network', 'Draadloos sensornetwerk'], ]; const otherTypesArrayWithTranslations = [ - ['Other', 'Overig'], - ['Actigraphy', 'Actigrafie'], - ['Air pollution sensor', 'Luchtvervuilingssensor'], - ['Analog image processing', 'Analoge beeldverwerking'], - ['Atomic force microscopy', 'Atoomkrachtmicroscopie'], - ['Atomic Gravitational Wave Interferometric Sensor', 'Atoomzwaartekrachtgolf interferometrische sensor'], - ['Catadioptric sensor', 'Catadioptrische sensor'], - ['Chemoreceptor', 'Chemoreceptor'], - ['Compressive sensing', 'Compressieve detectie'], - ['Cryogenic particle detectors', 'Cryogene deeltjesdetectoren'], - ['Dew warning', 'Dauw waarschuwing'], - ['Diffusion tensor imaging', 'Diffusie tensor beeldvorming'], - ['Digital holography', 'Digitale holografie'], - ['Electronic tongue', 'Elektronische tong'], - ['Fine Guidance Sensor', 'Fijne geleidingssensor'], - ['FineDustSensor', 'Fijnstofsensor'], - ['Flat panel detector', 'Flat panel detector'], - ['Functional magnetic resonance imaging', 'Functionele magnetische resonantiebeeldvorming'], - ['Glass break detector', 'Glasbreukmelder'], - ['Heartbeat sensor', 'Hartslagsensor'], - ['Hyperspectral sensors', 'Hyperspectrale sensoren'], - ['IRIS (Biosensor), Interferometric Reflectance Imaging Sensor', 'IRIS (Biosensor), interferometrische reflectie-beeldsensor'], - ['Laser beam profiler', 'Laserstraal profiler'], - ['Littoral Airborne Sensor/Hyperspectral', 'Littoral Airborne Sensor / Hyperspectraal'], - ['LORROS', 'LORROS'], - ['Millimeter wave scanner', 'Millimetergolfscanner'], - ['Magnetic resonance imaging', 'Magnetische resonantie beeldvorming'], - ['Moire deflectometry', 'Moiré-deflectometrie'], - ['Molecular sensor', 'Moleculaire sensor'], - ['Nanosensor', 'Nanosensor'], - ['Nano-tetherball Sensor', 'Nano-tetherball-sensor'], - ['Omnidirectional camera', 'Omnidirectionele camera'], - ['Organoleptic sensors', 'Organoleptische sensoren'], - ['Optical coherence tomography', 'Optische coherentietomografie'], - ['Phase unwrapping techniques', 'Fase-uitpaktechnieken'], - ['Polygraph Truth Detection', 'Polygraaf waarheidsdetectie'], - ['Positron emission tomography', 'Positron-emissietomografie'], - ['Push broom scanner', 'Duw de bezemscanner'], - ['Quantization (signal processing)', 'Kwantisering (signaalverwerking)'], - ['Range imaging', 'Bereik beeldvorming'], - ['Single-Photon Emission Computed Tomography (SPECT)', 'Single-Photon Emission Computed Tomography (SPECT)'], - ['Smartdust', 'Slimme stof'], - ['SQUID, Superconducting quantum interference device', 'SQUID, supergeleidend kwantuminterferentie-apparaat'], - ['SSIES, Special Sensors-Ions, Electrons, and Scintillation thermal plasma analysis package', 'SSIES, speciale sensoren-ionen, elektronen en scintillatie thermisch plasma-analysepakket'], - ['SSMIS, Special Sensor Microwave Imager / Sounder', 'SSMIS, speciale sensor-microgolf-imager / -sounder'], - ['Structured-light 3D scanner', 'Gestructureerd licht 3D-scanner'], - ['Sun sensor, Attitude control (spacecraft)', 'Zonnesensor, Attitude control (ruimtevaartuig)'], - ['Superconducting nanowire single-photon detector', 'Supergeleidende nanodraad enkelfotondetector'], - ['Thin-film thickness monitor', 'Monitor voor dunne filmdikte'], - ['Time-of-flight camera', 'Time-of-flight camera'], - ['TriDAR, Triangulation and LIDAR Automated Rendezvous and Docking', 'TriDAR, Triangulatie en LIDAR Geautomatiseerde Rendez-vous en Docking'], - ['Unattended Ground Sensors', 'Onbeheerde grondsensoren'], + ['Other', 'Overig'], + ['Actigraphy', 'Actigrafie'], + ['Air pollution sensor', 'Luchtvervuilingssensor'], + ['Analog image processing', 'Analoge beeldverwerking'], + ['Atomic force microscopy', 'Atoomkrachtmicroscopie'], + ['Atomic Gravitational Wave Interferometric Sensor', 'Atoomzwaartekrachtgolf interferometrische sensor'], + ['Catadioptric sensor', 'Catadioptrische sensor'], + ['Chemoreceptor', 'Chemoreceptor'], + ['Compressive sensing', 'Compressieve detectie'], + ['Cryogenic particle detectors', 'Cryogene deeltjesdetectoren'], + ['Dew warning', 'Dauw waarschuwing'], + ['Diffusion tensor imaging', 'Diffusie tensor beeldvorming'], + ['Digital holography', 'Digitale holografie'], + ['Electronic tongue', 'Elektronische tong'], + ['Fine Guidance Sensor', 'Fijne geleidingssensor'], + ['FineDustSensor', 'Fijnstofsensor'], + ['Flat panel detector', 'Flat panel detector'], + ['Functional magnetic resonance imaging', 'Functionele magnetische resonantiebeeldvorming'], + ['Glass break detector', 'Glasbreukmelder'], + ['Heartbeat sensor', 'Hartslagsensor'], + ['Hyperspectral sensors', 'Hyperspectrale sensoren'], + [ + 'IRIS (Biosensor), Interferometric Reflectance Imaging Sensor', + 'IRIS (Biosensor), interferometrische reflectie-beeldsensor', + ], + ['Laser beam profiler', 'Laserstraal profiler'], + ['Littoral Airborne Sensor/Hyperspectral', 'Littoral Airborne Sensor / Hyperspectraal'], + ['LORROS', 'LORROS'], + ['Millimeter wave scanner', 'Millimetergolfscanner'], + ['Magnetic resonance imaging', 'Magnetische resonantie beeldvorming'], + ['Moire deflectometry', 'Moiré-deflectometrie'], + ['Molecular sensor', 'Moleculaire sensor'], + ['Nanosensor', 'Nanosensor'], + ['Nano-tetherball Sensor', 'Nano-tetherball-sensor'], + ['Omnidirectional camera', 'Omnidirectionele camera'], + ['Organoleptic sensors', 'Organoleptische sensoren'], + ['Optical coherence tomography', 'Optische coherentietomografie'], + ['Phase unwrapping techniques', 'Fase-uitpaktechnieken'], + ['Polygraph Truth Detection', 'Polygraaf waarheidsdetectie'], + ['Positron emission tomography', 'Positron-emissietomografie'], + ['Push broom scanner', 'Duw de bezemscanner'], + ['Quantization (signal processing)', 'Kwantisering (signaalverwerking)'], + ['Range imaging', 'Bereik beeldvorming'], + ['Single-Photon Emission Computed Tomography (SPECT)', 'Single-Photon Emission Computed Tomography (SPECT)'], + ['Smartdust', 'Slimme stof'], + ['SQUID, Superconducting quantum interference device', 'SQUID, supergeleidend kwantuminterferentie-apparaat'], + [ + 'SSIES, Special Sensors-Ions, Electrons, and Scintillation thermal plasma analysis package', + 'SSIES, speciale sensoren-ionen, elektronen en scintillatie thermisch plasma-analysepakket', + ], + ['SSMIS, Special Sensor Microwave Imager / Sounder', 'SSMIS, speciale sensor-microgolf-imager / -sounder'], + ['Structured-light 3D scanner', 'Gestructureerd licht 3D-scanner'], + ['Sun sensor, Attitude control (spacecraft)', 'Zonnesensor, Attitude control (ruimtevaartuig)'], + ['Superconducting nanowire single-photon detector', 'Supergeleidende nanodraad enkelfotondetector'], + ['Thin-film thickness monitor', 'Monitor voor dunne filmdikte'], + ['Time-of-flight camera', 'Time-of-flight camera'], + [ + 'TriDAR, Triangulation and LIDAR Automated Rendezvous and Docking', + 'TriDAR, Triangulatie en LIDAR Geautomatiseerde Rendez-vous en Docking', + ], + ['Unattended Ground Sensors', 'Onbeheerde grondsensoren'], ]; let sensorTypesEN = null; let sensorTypesNL = null; -export function getSensorTypesTranslation(locale: string = 'en') { - const currentLocale = locale != null ? locale : 'en'; +export function getSensorTypesTranslation(locale = 'en') { + const currentLocale = locale != null ? locale : 'en'; - switch (currentLocale) { - case 'nl': - buildNL(); - return sensorTypesNL; - case 'en': - default: - buildEN(); - return sensorTypesEN; - } + switch (currentLocale) { + case 'nl': + buildNL(); + return sensorTypesNL; + case 'en': + default: + buildEN(); + return sensorTypesEN; + } } function buildEN() { - if (sensorTypesEN == null) { - sensorTypesEN = buildSensorTypesObject(0); - } - return sensorTypesEN; + if (sensorTypesEN == null) { + sensorTypesEN = buildSensorTypesObject(0); + } + return sensorTypesEN; } function buildNL() { - if (sensorTypesNL == null) { - sensorTypesNL = buildSensorTypesObject(1); - } - return sensorTypesNL; + if (sensorTypesNL == null) { + sensorTypesNL = buildSensorTypesObject(1); + } + return sensorTypesNL; } function buildSensorTypesObject(translationColumn: number): Record[] { - return [ - fromArrayToSensorTypesObject(soundSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(chemicalSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(electricCurrentSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(environmentalSensorWeatherStationTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(flowSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(positionDisplacementSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(opticalCameraSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(pressureSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(densitySensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(temperatureSensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(proximitySensorTypesArrayWithTranslations, translationColumn), - fromArrayToSensorTypesObject(otherTypesArrayWithTranslations, translationColumn), - ]; + return [ + fromArrayToSensorTypesObject(soundSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(chemicalSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(electricCurrentSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(environmentalSensorWeatherStationTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(flowSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(positionDisplacementSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(opticalCameraSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(pressureSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(densitySensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(temperatureSensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(proximitySensorTypesArrayWithTranslations, translationColumn), + fromArrayToSensorTypesObject(otherTypesArrayWithTranslations, translationColumn), + ]; } -function fromArrayToSensorTypesObject(ar: string[][], languageColumn: number = 0): Record { - const obj = { - key: ar[0][0], - value: ar[0][languageColumn], - types: [] - }; +function fromArrayToSensorTypesObject(ar: string[][], languageColumn = 0): Record { + const obj = { + key: ar[0][0], + value: ar[0][languageColumn], + types: [], + }; - for (let index = 1; index < ar.length; index++) { - const element = ar[index]; - obj.types.push({key: element[0], value: element[languageColumn]}); - } - return obj; + for (let index = 1; index < ar.length; index++) { + const element = ar[index]; + obj.types.push({ key: element[0], value: element[languageColumn] }); + } + return obj; } diff --git a/src/app/model/bodies/user-update.ts b/src/app/model/bodies/user-update.ts index ca3f793..714e45e 100644 --- a/src/app/model/bodies/user-update.ts +++ b/src/app/model/bodies/user-update.ts @@ -1,7 +1,3 @@ export class UserUpdateBody { - constructor( - public password?: string, - public legalEntityId?: string, - public leaveLegalEntity?: boolean, - ) { } + constructor(public password?: string, public legalEntityId?: string, public leaveLegalEntity?: boolean) {} } diff --git a/src/app/model/events/event-type.ts b/src/app/model/events/event-type.ts index 4d788ad..b107168 100644 --- a/src/app/model/events/event-type.ts +++ b/src/app/model/events/event-type.ts @@ -1,13 +1,13 @@ export enum EventType { - DeviceLocated = 'DeviceLocated', - DeviceRelocated = 'DeviceRelocated', - DeviceRegistered = 'DeviceRegistered', - DeviceUpdated = 'DeviceUpdated', - DeviceRemoved = 'DeviceRemoved', - OrganizationRegistered = 'OrganizationRegistered', - OrganizationUpdated = 'OrganizationUpdated', - LegalEntityRemoved = 'LegalEntityRemoved', - PublicContactDetailsAdded = 'PublicContactDetailsAdded', - ContactDetailsUpdated = 'ContactDetailsUpdated', - ContactDetailsRemoved = 'ContactDetailsRemoved', + DeviceLocated = 'DeviceLocated', + DeviceRelocated = 'DeviceRelocated', + DeviceRegistered = 'DeviceRegistered', + DeviceUpdated = 'DeviceUpdated', + DeviceRemoved = 'DeviceRemoved', + OrganizationRegistered = 'OrganizationRegistered', + OrganizationUpdated = 'OrganizationUpdated', + LegalEntityRemoved = 'LegalEntityRemoved', + PublicContactDetailsAdded = 'PublicContactDetailsAdded', + ContactDetailsUpdated = 'ContactDetailsUpdated', + ContactDetailsRemoved = 'ContactDetailsRemoved', } diff --git a/src/app/model/legalEntity.ts b/src/app/model/legalEntity.ts index 31507a8..ae4f19c 100644 --- a/src/app/model/legalEntity.ts +++ b/src/app/model/legalEntity.ts @@ -1,13 +1,13 @@ export interface IContactDetails { - _id?: string; - name?: string; - email?: string; - phone?: string; + _id?: string; + name?: string; + email?: string; + phone?: string; } export interface ILegalEntity { - _id?: string; - name: string; - website?: string; - contactDetails?: IContactDetails[]; + _id?: string; + name: string; + website?: string; + contactDetails?: IContactDetails[]; } diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index acbc17d..25451cb 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -1,23 +1,21 @@ -import { Router } from '@angular/router'; import { Component, Input } from '@angular/core'; +import { Router } from '@angular/router'; @Component({ - selector: 'app-navbar', - templateUrl: './navbar.component.html', - styleUrls: ['./navbar.component.scss'], + selector: 'app-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'], }) export class NavBarComponent { - @Input() locationClass; + @Input() locationClass; - constructor( - private router: Router, - ) {} + constructor(private router: Router) {} - public async toggleMenu() { - if (this.router.url === '/viewer') { - await this.router.navigate(['/devices']); - } else { - await this.router.navigate(['/viewer']); + public async toggleMenu() { + if (this.router.url === '/viewer') { + await this.router.navigate(['/devices']); + } else { + await this.router.navigate(['/viewer']); + } } - } } diff --git a/src/app/services/alert.service.ts b/src/app/services/alert.service.ts index e91b53e..58af77d 100644 --- a/src/app/services/alert.service.ts +++ b/src/app/services/alert.service.ts @@ -1,60 +1,66 @@ import { Injectable } from '@angular/core'; -import { Observable, Subject } from 'rxjs'; import { NavigationStart, Router } from '@angular/router'; +import { Observable, Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AlertService { - private subject = new Subject(); - private keepAfterRouteChange = false; - - constructor(private router: Router) { - // clear alert messages on route change unless 'keepAfterRouteChange' flag is true - this.router.events.subscribe((event) => { - if (event instanceof NavigationStart) { - if (this.keepAfterRouteChange) { - // only keep for a single route change - this.keepAfterRouteChange = false; - } else { - // clear alert message - this.clear(); - } - } - }); - } + private subject = new Subject(); + private keepAfterRouteChange = false; + + constructor(private router: Router) { + // clear alert messages on route change unless 'keepAfterRouteChange' flag is true + this.router.events.subscribe((event) => { + if (event instanceof NavigationStart) { + if (this.keepAfterRouteChange) { + // only keep for a single route change + this.keepAfterRouteChange = false; + } else { + // clear alert message + this.clear(); + } + } + }); + } - public getAlert(): Observable { - return this.subject.asObservable(); - } + public getAlert(): Observable { + return this.subject.asObservable(); + } - public success(message: string, keepAfterRouteChange= false, timeoutDuration= 4000) { - this.keepAfterRouteChange = keepAfterRouteChange; - this.subject.next({ type: 'success', text: message }); + public success(message: string, keepAfterRouteChange = false, timeoutDuration = 4000) { + this.keepAfterRouteChange = keepAfterRouteChange; + this.subject.next({ type: 'success', text: message }); - if (timeoutDuration) { - setTimeout(() => { this.clear(); }, timeoutDuration); + if (timeoutDuration) { + setTimeout(() => { + this.clear(); + }, timeoutDuration); + } } - } - public warning(message: string, keepAfterRouteChange = false, timeoutDuration = 4000) { - this.keepAfterRouteChange = keepAfterRouteChange; - this.subject.next({ type: 'warning', text: message }); + public warning(message: string, keepAfterRouteChange = false, timeoutDuration = 4000) { + this.keepAfterRouteChange = keepAfterRouteChange; + this.subject.next({ type: 'warning', text: message }); - if (timeoutDuration) { - setTimeout(() => { this.clear(); }, timeoutDuration); + if (timeoutDuration) { + setTimeout(() => { + this.clear(); + }, timeoutDuration); + } } - } - public error(message: string, keepAfterRouteChange = false, timeoutDuration = 4000) { - this.keepAfterRouteChange = keepAfterRouteChange; - this.subject.next({ type: 'error', text: message }); + public error(message: string, keepAfterRouteChange = false, timeoutDuration = 4000) { + this.keepAfterRouteChange = keepAfterRouteChange; + this.subject.next({ type: 'error', text: message }); - if (timeoutDuration) { - setTimeout(() => { this.clear(); }, timeoutDuration); + if (timeoutDuration) { + setTimeout(() => { + this.clear(); + }, timeoutDuration); + } } - } - public clear() { - // clear by calling subject.next() without parameters - this.subject.next(); - } + public clear() { + // clear by calling subject.next() without parameters + this.subject.next(); + } } diff --git a/src/app/services/connection.service.ts b/src/app/services/connection.service.ts index 83d6462..300e2dd 100644 --- a/src/app/services/connection.service.ts +++ b/src/app/services/connection.service.ts @@ -1,149 +1,146 @@ -import * as io from 'socket.io-client'; -import { Router } from '@angular/router'; -import { Injectable } from '@angular/core'; -import { EnvService } from './env.service'; import { HttpClient } from '@angular/common/http'; -import { ILegalEntity } from '../model/legalEntity'; -import { BehaviorSubject, Subject, Observable, Subscriber } from 'rxjs'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { OidcSecurityService } from 'angular-auth-oidc-client'; +import { BehaviorSubject, Subject, Observable, Subscriber } from 'rxjs'; +import * as io from 'socket.io-client'; +import { ILegalEntity } from '../model/legalEntity'; +import { EnvService } from './env.service'; export class SocketEvent { - constructor( - public namespace?: string, - public event?: any, - ) { } + constructor(public namespace?: string, public event?: any) {} } @Injectable({ providedIn: 'root' }) export class ConnectionService { - private socket: SocketIOClient.Socket; - - private legalEntitySubject: BehaviorSubject = new BehaviorSubject(null); - public legalEntity$: Observable = this.legalEntitySubject.asObservable(); - - // Routing the events using a separate observable is necessary because a socket connection may not exist at the - // time some component tries to subscribe to an endpoint. - private eventReceiver: Subject = new Subject(); - private event$: Observable = this.eventReceiver.asObservable(); - private nameSpaceObservables: Record> = {}; - - constructor( - private readonly router: Router, - private readonly env: EnvService, - private readonly http: HttpClient, - private readonly oidcSecurityService: OidcSecurityService, - ) { - this.legalEntity$.subscribe(legalEntity => { - if (!this.socket && legalEntity) { - this.connectSocket(); - } - }); - } - - public get currentLegalEntity(): ILegalEntity { - return this.legalEntitySubject.value; - } - - public clearLegalEntity() { - this.legalEntitySubject.next(null); - } - - public async refreshLegalEntity(): Promise { - let response; - try { - response = await this.http.get(`${this.env.apiUrl}/legalentity`).toPromise() as ILegalEntity; - } catch { - return this.logoutRedirect(); + private socket: SocketIOClient.Socket; + + private legalEntitySubject: BehaviorSubject = new BehaviorSubject(null); + public legalEntity$: Observable = this.legalEntitySubject.asObservable(); + + // Routing the events using a separate observable is necessary because a socket connection may not exist at the + // time some component tries to subscribe to an endpoint. + private eventReceiver: Subject = new Subject(); + private event$: Observable = this.eventReceiver.asObservable(); + private nameSpaceObservables: Record> = {}; + + constructor( + private readonly router: Router, + private readonly env: EnvService, + private readonly http: HttpClient, + private readonly oidcSecurityService: OidcSecurityService, + ) { + this.legalEntity$.subscribe((legalEntity) => { + if (!this.socket && legalEntity) { + this.connectSocket(); + } + }); } - if (response) { - this.legalEntitySubject.next(response); + public get currentLegalEntity(): ILegalEntity { + return this.legalEntitySubject.value; } - } - - public async logout() { - this.clearLegalEntity(); - this.oidcSecurityService.logoffLocal(); - } - - public async logoutRedirect() { - await this.disconnectSocket(); - await this.logout(); - await this.router.navigate(['/login']); - } - - public connectSocket() { - if (!this.socket) { - const namespace = 'sensrnet'; - const host = this.env.apiUrl.substring(0, this.env.apiUrl.lastIndexOf('/')); // strip the /api part - - const connectionOptions = { - path: '/api/socket.io', - transportOptions: undefined, - }; - - const token = this.oidcSecurityService.getIdToken(); - connectionOptions.transportOptions = { - polling: { - extraHeaders: { - Authorization: `Bearer ${token}`, - } - } - }; - const url = `${host}/${namespace}`; - this.socket = io(url, connectionOptions); + public clearLegalEntity() { + this.legalEntitySubject.next(null); + } - this.socket.on('connect', () => { - console.log('Socket.io connected.'); + public async refreshLegalEntity(): Promise { + let response; + try { + response = (await this.http.get(`${this.env.apiUrl}/legalentity`).toPromise()) as ILegalEntity; + } catch { + return this.logoutRedirect(); + } - for (const ns of Object.keys(this.nameSpaceObservables)) { - this.subscribeSocket(ns); + if (response) { + this.legalEntitySubject.next(response); } - }); + } - this.socket.on('disconnect', async () => { - console.log('Socket.io disconnected.'); + public async logout() { + this.clearLegalEntity(); + this.oidcSecurityService.logoffLocal(); + } - await this.logoutRedirect(); - }); + public async logoutRedirect() { + await this.disconnectSocket(); + await this.logout(); + await this.router.navigate(['/login']); } - } - public async disconnectSocket() { - if (this.socket) { - await this.socket.close(); - this.socket = null; + public connectSocket() { + if (!this.socket) { + const namespace = 'sensrnet'; + const host = this.env.apiUrl.substring(0, this.env.apiUrl.lastIndexOf('/')); // strip the /api part + + const connectionOptions = { + path: '/api/socket.io', + transportOptions: undefined, + }; + + const token = this.oidcSecurityService.getIdToken(); + connectionOptions.transportOptions = { + polling: { + extraHeaders: { + Authorization: `Bearer ${token}`, + }, + }, + }; + + const url = `${host}/${namespace}`; + this.socket = io(url, connectionOptions); + + this.socket.on('connect', () => { + console.log('Socket.io connected.'); + + for (const ns of Object.keys(this.nameSpaceObservables)) { + this.subscribeSocket(ns); + } + }); + + this.socket.on('disconnect', async () => { + console.log('Socket.io disconnected.'); + + await this.logoutRedirect(); + }); + } } - } - public updateSocketLegalEntity(legalEntityId: string) { - if (this.socket) { - this.socket.emit('LegalEntityUpdated', { legalEntityId }); + public async disconnectSocket() { + if (this.socket) { + await this.socket.close(); + this.socket = null; + } } - } - public subscribeSocket(namespace: string) { - if (this.socket) { - this.socket.on(namespace, (event) => { - this.eventReceiver.next(new SocketEvent(namespace, event)); - }); + public updateSocketLegalEntity(legalEntityId: string) { + if (this.socket) { + this.socket.emit('LegalEntityUpdated', { legalEntityId }); + } } - } - - public subscribeTo(namespace: string = '/'): Observable { - if (!this.nameSpaceObservables[namespace]) { - this.nameSpaceObservables[namespace] = new Observable((observer: Subscriber) => { - this.event$.subscribe((event: SocketEvent) => { - if (event.namespace === namespace) { - observer.next(event.event); - } - }); - }); - this.subscribeSocket(namespace); + public subscribeSocket(namespace: string) { + if (this.socket) { + this.socket.on(namespace, (event) => { + this.eventReceiver.next(new SocketEvent(namespace, event)); + }); + } } - return this.nameSpaceObservables[namespace]; - } + public subscribeTo(namespace = '/'): Observable { + if (!this.nameSpaceObservables[namespace]) { + this.nameSpaceObservables[namespace] = new Observable((observer: Subscriber) => { + this.event$.subscribe((event: SocketEvent) => { + if (event.namespace === namespace) { + observer.next(event.event); + } + }); + }); + + this.subscribeSocket(namespace); + } + + return this.nameSpaceObservables[namespace]; + } } diff --git a/src/app/services/device.service.ts b/src/app/services/device.service.ts index fbab45d..19e1440 100644 --- a/src/app/services/device.service.ts +++ b/src/app/services/device.service.ts @@ -1,236 +1,242 @@ -import { EnvService } from './env.service'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, Subscriber } from 'rxjs'; +import { IDevice } from '../model/bodies/device-model'; import { ISensor } from '../model/bodies/sensor-body'; import { EventType } from '../model/events/event-type'; import { ConnectionService } from './connection.service'; -import { HttpClient, HttpParams } from '@angular/common/http'; -import {IDevice} from '../model/bodies/device-model'; +import { EnvService } from './env.service'; export interface IRegisterLocationBody { - location: number[]; - name?: string; - description?: string; + location: number[]; + name?: string; + description?: string; } export interface IUpdateLocationBody { - location?: number[]; - name?: string; - description?: string; + location?: number[]; + name?: string; + description?: string; } export interface IRegisterDeviceBody { - _id?: string; - name: string; - description?: string; - category?: string; - connectivity?: string; - location: IRegisterLocationBody; + _id?: string; + name: string; + description?: string; + category?: string; + connectivity?: string; + location: IRegisterLocationBody; } export interface IUpdateDeviceBody { - _id?: string; - name?: string; - description?: string; - category?: string; - connectivity?: string; - location?: IUpdateLocationBody; + _id?: string; + name?: string; + description?: string; + category?: string; + connectivity?: string; + location?: IUpdateLocationBody; } export interface IRegisterSensorBody { - _id?: string; - name: string; - description?: string; - type?: string; - manufacturer?: string; - supplier?: string; - documentation?: string; + _id?: string; + name: string; + description?: string; + type?: string; + manufacturer?: string; + supplier?: string; + documentation?: string; } export interface IUpdateSensorBody { - _id?: string; - name?: string; - description?: string; - type?: string; - manufacturer?: string; - supplier?: string; - documentation?: string; + _id?: string; + name?: string; + description?: string; + type?: string; + manufacturer?: string; + supplier?: string; + documentation?: string; } export interface IRegisterDatastreamBody { - _id?: string; - name: string; - description?: string; - unitOfMeasurement?: Record; - observedArea?: Record; - theme?: string[]; - dataQuality?: string; - isActive?: boolean; - isPublic?: boolean; - isOpenData?: boolean; - containsPersonalInfoData?: boolean; - isReusable?: boolean; - documentation?: string; - dataLink?: string; + _id?: string; + name: string; + description?: string; + unitOfMeasurement?: Record; + observedArea?: Record; + theme?: string[]; + dataQuality?: string; + isActive?: boolean; + isPublic?: boolean; + isOpenData?: boolean; + containsPersonalInfoData?: boolean; + isReusable?: boolean; + documentation?: string; + dataLink?: string; } export interface IUpdateDatastreamBody { - _id?: string; - name?: string; - description?: string; - unitOfMeasurement?: Record; - observedArea?: Record; - theme?: string[]; - dataQuality?: string; - isActive?: boolean; - isPublic?: boolean; - isOpenData?: boolean; - containsPersonalInfoData?: boolean; - isReusable?: boolean; - documentation?: string; - dataLink?: string; + _id?: string; + name?: string; + description?: string; + unitOfMeasurement?: Record; + observedArea?: Record; + theme?: string[]; + dataQuality?: string; + isActive?: boolean; + isPublic?: boolean; + isOpenData?: boolean; + containsPersonalInfoData?: boolean; + isReusable?: boolean; + documentation?: string; + dataLink?: string; } @Injectable({ providedIn: 'root' }) export class DeviceService { - private deviceLocated$: Observable; - private deviceUpdated$: Observable; - private deviceRemoved$: Observable; - - constructor( - private http: HttpClient, - private env: EnvService, - private connectionService: ConnectionService, - ) {} - - public async subscribe() { - // This way multiple calls to subscribe do not create new observables. - if (!this.deviceLocated$ || !this.deviceUpdated$ || !this.deviceRemoved$) { - const deviceUpdated$ = this.connectionService.subscribeTo(EventType.DeviceUpdated); - const deviceRemoved$ = this.connectionService.subscribeTo(EventType.DeviceRemoved); - const deviceLocated$ = this.connectionService.subscribeTo(EventType.DeviceLocated); - const deviceRelocated$ = this.connectionService.subscribeTo(EventType.DeviceRelocated); - - this.deviceLocated$ = new Observable((observer: Subscriber) => { - deviceLocated$.subscribe((sensor: IDevice) => { - observer.next(sensor); - }); - }); - - this.deviceUpdated$ = new Observable((observer: Subscriber) => { - const updateFunction = (sensor: IDevice) => observer.next(sensor); - - deviceUpdated$.subscribe(updateFunction); - deviceRelocated$.subscribe(updateFunction); - }); - - this.deviceRemoved$ = new Observable((observer: Subscriber) => { - deviceRemoved$.subscribe((sensor: IDevice) => { - observer.next(sensor); - }); - }); + private deviceLocated$: Observable; + private deviceUpdated$: Observable; + private deviceRemoved$: Observable; + + constructor(private http: HttpClient, private env: EnvService, private connectionService: ConnectionService) {} + + public async subscribe() { + // This way multiple calls to subscribe do not create new observables. + if (!this.deviceLocated$ || !this.deviceUpdated$ || !this.deviceRemoved$) { + const deviceUpdated$ = this.connectionService.subscribeTo(EventType.DeviceUpdated); + const deviceRemoved$ = this.connectionService.subscribeTo(EventType.DeviceRemoved); + const deviceLocated$ = this.connectionService.subscribeTo(EventType.DeviceLocated); + const deviceRelocated$ = this.connectionService.subscribeTo(EventType.DeviceRelocated); + + this.deviceLocated$ = new Observable((observer: Subscriber) => { + deviceLocated$.subscribe((sensor: IDevice) => { + observer.next(sensor); + }); + }); + + this.deviceUpdated$ = new Observable((observer: Subscriber) => { + const updateFunction = (sensor: IDevice) => observer.next(sensor); + + deviceUpdated$.subscribe(updateFunction); + deviceRelocated$.subscribe(updateFunction); + }); + + this.deviceRemoved$ = new Observable((observer: Subscriber) => { + deviceRemoved$.subscribe((sensor: IDevice) => { + observer.next(sensor); + }); + }); + } + + return { onLocate: this.deviceLocated$, onUpdate: this.deviceUpdated$, onRemove: this.deviceRemoved$ }; + } + + /** Register device */ + public register(device: IRegisterDeviceBody) { + return this.http.post(`${this.env.apiUrl}/device`, device); + } + + public update(deviceId: string, device: IUpdateDeviceBody) { + return this.http.put(`${this.env.apiUrl}/device/${deviceId}`, device); + } + + public registerSensor(deviceId: string, sensor: IRegisterSensorBody) { + return this.http.post(`${this.env.apiUrl}/device/${deviceId}/sensor`, sensor); } - return { onLocate: this.deviceLocated$, onUpdate: this.deviceUpdated$, onRemove: this.deviceRemoved$ }; - } - - /** Register device */ - public register(device: IRegisterDeviceBody) { - return this.http.post(`${this.env.apiUrl}/device`, device); - } - - public update(deviceId: string, device: IUpdateDeviceBody) { - return this.http.put(`${this.env.apiUrl}/device/${deviceId}`, device); - } - - public registerSensor(deviceId: string, sensor: IRegisterSensorBody) { - return this.http.post(`${this.env.apiUrl}/device/${deviceId}/sensor`, sensor); - } - - public updateSensor(deviceId: string, sensorId: string, sensor: IUpdateSensorBody) { - return this.http.put(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}`, sensor); - } - - public removeSensor(deviceId: string, sensorId: string) { - return this.http.delete(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}`); - } - - public registerDatastream(deviceId: string, sensorId: string, datastream: IRegisterDatastreamBody) { - return this.http.post(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream`, datastream); - } - - public updateDatastream(deviceId: string, sensorId: string, datastreamId: string, - datastream: IUpdateDatastreamBody) { - return this.http.put(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}`, - datastream); - } - - public removeDatastream(deviceId: string, sensorId: string, datastreamId: string) { - return this.http.delete(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}`); - } - - public async getDevices(bottomLeftLongitude?: string, bottomLeftLatitude?: string, upperRightLongitude?: string, - upperRightLatitude?: string) { - let params = new HttpParams(); - if (bottomLeftLongitude) { - params = params.set('bottomLeftLongitude', bottomLeftLongitude); + public updateSensor(deviceId: string, sensorId: string, sensor: IUpdateSensorBody) { + return this.http.put(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}`, sensor); } - if (bottomLeftLatitude) { - params = params.set('bottomLeftLatitude', bottomLeftLatitude); + + public removeSensor(deviceId: string, sensorId: string) { + return this.http.delete(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}`); } - if (upperRightLongitude) { - params = params.set('upperRightLongitude', upperRightLongitude); + + public registerDatastream(deviceId: string, sensorId: string, datastream: IRegisterDatastreamBody) { + return this.http.post(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream`, datastream); } - if (upperRightLatitude) { - params = params.set('upperRightLatitude', upperRightLatitude); + + public updateDatastream( + deviceId: string, + sensorId: string, + datastreamId: string, + datastream: IUpdateDatastreamBody, + ) { + return this.http.put( + `${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}`, + datastream, + ); + } + + public removeDatastream(deviceId: string, sensorId: string, datastreamId: string) { + return this.http.delete(`${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}`); } - const url = `${this.env.apiUrl}/device?${params.toString()}`; - const devicePromise = this.http.get(url).toPromise(); - - return await devicePromise as IDevice[]; - } - - public async getMyDevices(legalEntityId, pageIndex, pageSize, sortField, sortDirection, name?) { - let devices; - if (legalEntityId) { - let params = new HttpParams(); - params = params.set('pageSize', pageSize); - params = params.set('pageIndex', pageIndex); - params = params.set('sortField', sortField); - params = params.set('sortDirection', sortDirection); - params = params.set('legalEntityId', legalEntityId); - - if (name) { - params = params.set('name', name); - } - - const url = `${this.env.apiUrl}/device?${params.toString()}`; - devices = await this.http.get(url).toPromise() as ISensor[]; - } else { - devices = []; + public async getDevices( + bottomLeftLongitude?: string, + bottomLeftLatitude?: string, + upperRightLongitude?: string, + upperRightLatitude?: string, + ) { + let params = new HttpParams(); + if (bottomLeftLongitude) { + params = params.set('bottomLeftLongitude', bottomLeftLongitude); + } + if (bottomLeftLatitude) { + params = params.set('bottomLeftLatitude', bottomLeftLatitude); + } + if (upperRightLongitude) { + params = params.set('upperRightLongitude', upperRightLongitude); + } + if (upperRightLatitude) { + params = params.set('upperRightLatitude', upperRightLatitude); + } + + const url = `${this.env.apiUrl}/device?${params.toString()}`; + const devicePromise = this.http.get(url).toPromise(); + + return (await devicePromise) as IDevice[]; } - return devices; - } + public async getMyDevices(legalEntityId, pageIndex, pageSize, sortField, sortDirection, name?) { + let devices; + if (legalEntityId) { + let params = new HttpParams(); + params = params.set('pageSize', pageSize); + params = params.set('pageIndex', pageIndex); + params = params.set('sortField', sortField); + params = params.set('sortDirection', sortDirection); + params = params.set('legalEntityId', legalEntityId); + + if (name) { + params = params.set('name', name); + } + + const url = `${this.env.apiUrl}/device?${params.toString()}`; + devices = (await this.http.get(url).toPromise()) as ISensor[]; + } else { + devices = []; + } + + return devices; + } - /** Unregister a sensor */ - public unregister(id: string) { - return this.http.delete(`${this.env.apiUrl}/device/${id}`).toPromise(); - } + /** Unregister a sensor */ + public unregister(id: string) { + return this.http.delete(`${this.env.apiUrl}/device/${id}`).toPromise(); + } - /** Retrieve a single device */ - public get(id: string) { - return this.http.get(`${this.env.apiUrl}/device/${id}`); - } + /** Retrieve a single device */ + public get(id: string) { + return this.http.get(`${this.env.apiUrl}/device/${id}`); + } - public linkObservationGoal(deviceId: string, sensorId: string, datastreamId: string, observationGoalId: string) { - const url = `${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}/linkgoal`; - return this.http.put(url, {observationGoalId}); - } + public linkObservationGoal(deviceId: string, sensorId: string, datastreamId: string, observationGoalId: string) { + const url = `${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}/linkgoal`; + return this.http.put(url, { observationGoalId }); + } - public unlinkObservationGoal(deviceId: string, sensorId: string, datastreamId: string, observationGoalId: string) { - const url = `${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}/unlinkgoal`; - return this.http.put(url, {observationGoalId}); - } + public unlinkObservationGoal(deviceId: string, sensorId: string, datastreamId: string, observationGoalId: string) { + const url = `${this.env.apiUrl}/device/${deviceId}/sensor/${sensorId}/datastream/${datastreamId}/unlinkgoal`; + return this.http.put(url, { observationGoalId }); + } } diff --git a/src/app/services/env.service.provider.ts b/src/app/services/env.service.provider.ts index cb6bea4..98f1783 100644 --- a/src/app/services/env.service.provider.ts +++ b/src/app/services/env.service.provider.ts @@ -1,29 +1,29 @@ import { EnvService } from './env.service'; export const EnvServiceFactory = () => { - // Create env - const env = new EnvService(); + // Create env + const env = new EnvService(); - // Read environment variables from browser window - const browserWindow = window || {}; - // tslint:disable-next-line: no-string-literal - const browserWindowEnv = browserWindow['__env'] || {}; + // Read environment variables from browser window + const browserWindow = window || {}; + // tslint:disable-next-line: no-string-literal + const browserWindowEnv = browserWindow['__env'] || {}; - // Assign environment variables from browser window to env - // In the current implementation, properties from env.js overwrite defaults from the EnvService. - // If needed, a deep merge can be performed here to merge properties instead of overwriting them. - for (const key in browserWindowEnv) { - if (browserWindowEnv.hasOwnProperty(key)) { - // tslint:disable-next-line: no-string-literal - env[key] = window['__env'][key]; + // Assign environment variables from browser window to env + // In the current implementation, properties from env.js overwrite defaults from the EnvService. + // If needed, a deep merge can be performed here to merge properties instead of overwriting them. + for (const key in browserWindowEnv) { + if (Object.prototype.hasOwnProperty.call(browserWindowEnv, key)) { + // tslint:disable-next-line: no-string-literal + env[key] = window['__env'][key]; + } } - } - return env; + return env; }; export const EnvServiceProvider = { - provide: EnvService, - useFactory: EnvServiceFactory, - deps: [], + provide: EnvService, + useFactory: EnvServiceFactory, + deps: [], }; diff --git a/src/app/services/env.service.ts b/src/app/services/env.service.ts index 323d4b5..3f8e840 100644 --- a/src/app/services/env.service.ts +++ b/src/app/services/env.service.ts @@ -1,18 +1,15 @@ export class EnvService { + // The values that are defined here are the default values that can + // be overridden by env.js - // The values that are defined here are the default values that can - // be overridden by env.js + // API url + public apiUrl = ''; - // API url - public apiUrl = ''; + // issuer endpoint + public oidcIssuer = ''; - // issuer endpoint - public oidcIssuer = ''; - - // client name - public oidcClientId = ''; - - constructor() { - } + // client name + public oidcClientId = ''; + constructor() {} } diff --git a/src/app/services/legal-entity.service.ts b/src/app/services/legal-entity.service.ts index a1f5cc0..cd0ba3d 100644 --- a/src/app/services/legal-entity.service.ts +++ b/src/app/services/legal-entity.service.ts @@ -1,85 +1,85 @@ -import { Injectable } from '@angular/core'; -import { EnvService } from './env.service'; import { HttpClient, HttpParams } from '@angular/common/http'; -import { IContactDetails, ILegalEntity } from '../model/legalEntity'; +import { Injectable } from '@angular/core'; import { Observable, Subscriber } from 'rxjs'; import { EventType } from '../model/events/event-type'; +import { IContactDetails, ILegalEntity } from '../model/legalEntity'; import { ConnectionService } from './connection.service'; +import { EnvService } from './env.service'; export interface IRegisterLegalEntityBody { - _id?: string; - name: string; - website?: string; - contactDetails?: IContactDetails; + _id?: string; + name: string; + website?: string; + contactDetails?: IContactDetails; } @Injectable({ providedIn: 'root' }) export class LegalEntityService { - private legalEntityUpdated$: Observable; - private legalEntityRemoved$: Observable; - private legalEntityRegistered$: Observable; + private legalEntityUpdated$: Observable; + private legalEntityRemoved$: Observable; + private legalEntityRegistered$: Observable; - constructor( - private env: EnvService, - private http: HttpClient, - private connectionService: ConnectionService, - ) {} + constructor(private env: EnvService, private http: HttpClient, private connectionService: ConnectionService) {} - public async subscribe() { - // This way multiple calls to subscribe do not create new observables. - if (!this.legalEntityUpdated$ || !this.legalEntityRemoved$ || !this.legalEntityRegistered$) { - const legalEntityUpdated$ = this.connectionService.subscribeTo(EventType.OrganizationUpdated); - const publicContactDetailsAdded$ = this.connectionService.subscribeTo(EventType.PublicContactDetailsAdded); - const contactDetailsUpdated$ = this.connectionService.subscribeTo(EventType.ContactDetailsUpdated); - const contactDetailsRemoved$ = this.connectionService.subscribeTo(EventType.ContactDetailsRemoved); - const legalEntityRemoved$ = this.connectionService.subscribeTo(EventType.LegalEntityRemoved); - const legalEntityRegistered$ = this.connectionService.subscribeTo(EventType.OrganizationRegistered); + public async subscribe() { + // This way multiple calls to subscribe do not create new observables. + if (!this.legalEntityUpdated$ || !this.legalEntityRemoved$ || !this.legalEntityRegistered$) { + const legalEntityUpdated$ = this.connectionService.subscribeTo(EventType.OrganizationUpdated); + const publicContactDetailsAdded$ = this.connectionService.subscribeTo(EventType.PublicContactDetailsAdded); + const contactDetailsUpdated$ = this.connectionService.subscribeTo(EventType.ContactDetailsUpdated); + const contactDetailsRemoved$ = this.connectionService.subscribeTo(EventType.ContactDetailsRemoved); + const legalEntityRemoved$ = this.connectionService.subscribeTo(EventType.LegalEntityRemoved); + const legalEntityRegistered$ = this.connectionService.subscribeTo(EventType.OrganizationRegistered); - this.legalEntityUpdated$ = new Observable((observer: Subscriber) => { - const updateFn = (legalEntity: ILegalEntity) => observer.next(legalEntity); - legalEntityUpdated$.subscribe(updateFn); - publicContactDetailsAdded$.subscribe(updateFn); - contactDetailsUpdated$.subscribe(updateFn); - contactDetailsRemoved$.subscribe(updateFn); - }); + this.legalEntityUpdated$ = new Observable((observer: Subscriber) => { + const updateFn = (legalEntity: ILegalEntity) => observer.next(legalEntity); + legalEntityUpdated$.subscribe(updateFn); + publicContactDetailsAdded$.subscribe(updateFn); + contactDetailsUpdated$.subscribe(updateFn); + contactDetailsRemoved$.subscribe(updateFn); + }); - this.legalEntityRemoved$ = new Observable((observer: Subscriber) => { - legalEntityRemoved$.subscribe((legalEntity: ILegalEntity) => observer.next(legalEntity)); - }); + this.legalEntityRemoved$ = new Observable((observer: Subscriber) => { + legalEntityRemoved$.subscribe((legalEntity: ILegalEntity) => observer.next(legalEntity)); + }); - this.legalEntityRegistered$ = new Observable((observer: Subscriber) => { - legalEntityRegistered$.subscribe((legalEntity: ILegalEntity) => observer.next(legalEntity)); - }); - } + this.legalEntityRegistered$ = new Observable((observer: Subscriber) => { + legalEntityRegistered$.subscribe((legalEntity: ILegalEntity) => observer.next(legalEntity)); + }); + } - return { onRegister: this.legalEntityRegistered$, onUpdate: this.legalEntityUpdated$, onRemove: this.legalEntityRemoved$ }; - } + return { + onRegister: this.legalEntityRegistered$, + onUpdate: this.legalEntityUpdated$, + onRemove: this.legalEntityRemoved$, + }; + } - public register(legalEntity: IRegisterLegalEntityBody) { - return this.http.post(`${this.env.apiUrl}/legalentity/organization`, legalEntity); - } + public register(legalEntity: IRegisterLegalEntityBody) { + return this.http.post(`${this.env.apiUrl}/legalentity/organization`, legalEntity); + } - public get() { - return this.http.get(`${this.env.apiUrl}/legalentity`); - } + public get() { + return this.http.get(`${this.env.apiUrl}/legalentity`); + } - public getLegalEntities(name?: string) { - let params = new HttpParams(); - if (name) { - params = params.set('name', name); + public getLegalEntities(name?: string) { + let params = new HttpParams(); + if (name) { + params = params.set('name', name); + } + return this.http.get(`${this.env.apiUrl}/legalentities?${params.toString()}`); } - return this.http.get(`${this.env.apiUrl}/legalentities?${params.toString()}`); - } - public update(legalEntity: ILegalEntity) { - return this.http.put(`${this.env.apiUrl}/legalentity/organization`, legalEntity); - } + public update(legalEntity: ILegalEntity) { + return this.http.put(`${this.env.apiUrl}/legalentity/organization`, legalEntity); + } - public updateContactDetails(contactDetailsId, contactDetails: IContactDetails) { - return this.http.put(`${this.env.apiUrl}/legalentity/contactdetails/${contactDetailsId}`, contactDetails); - } + public updateContactDetails(contactDetailsId, contactDetails: IContactDetails) { + return this.http.put(`${this.env.apiUrl}/legalentity/contactdetails/${contactDetailsId}`, contactDetails); + } - public delete() { - return this.http.delete(`${this.env.apiUrl}/legalentity`); - } + public delete() { + return this.http.delete(`${this.env.apiUrl}/legalentity`); + } } diff --git a/src/app/services/location.service.ts b/src/app/services/location.service.ts index 25b5e2e..7e30c7b 100644 --- a/src/app/services/location.service.ts +++ b/src/app/services/location.service.ts @@ -4,43 +4,43 @@ import { ISensorLocation } from '../model/bodies/location'; @Injectable({ providedIn: 'root' }) export class LocationService { - private location: BehaviorSubject = new BehaviorSubject({ - type: 'Point', - coordinates: [0, 0, 0] - }); + private location: BehaviorSubject = new BehaviorSubject({ + type: 'Point', + coordinates: [0, 0, 0], + }); - private locationMarker: BehaviorSubject = new BehaviorSubject({ - type: 'Point', - coordinates: [0, 0, 0] - }); + private locationMarker: BehaviorSubject = new BehaviorSubject({ + type: 'Point', + coordinates: [0, 0, 0], + }); - private locationHighlight: BehaviorSubject = new BehaviorSubject(null); + private locationHighlight: BehaviorSubject = new BehaviorSubject(null); - location$: Observable = this.location.asObservable(); - showLocation$: Observable = this.locationMarker.asObservable(); - locationHighlight$: Observable = this.locationHighlight.asObservable(); + location$: Observable = this.location.asObservable(); + showLocation$: Observable = this.locationMarker.asObservable(); + locationHighlight$: Observable = this.locationHighlight.asObservable(); - setLocation(location: ISensorLocation) { - this.location.next(location); - } + setLocation(location: ISensorLocation) { + this.location.next(location); + } - unsetLocation() { - this.location.next(null); - } + unsetLocation() { + this.location.next(null); + } - showLocation(location: ISensorLocation) { - this.locationMarker.next(location); - } + showLocation(location: ISensorLocation) { + this.locationMarker.next(location); + } - hideLocationMarker() { - this.locationMarker.next(null); - } + hideLocationMarker() { + this.locationMarker.next(null); + } - highlightLocation(location: ISensorLocation) { - this.locationHighlight.next(location); - } + highlightLocation(location: ISensorLocation) { + this.locationHighlight.next(location); + } - hideLocationHighlight() { - this.locationHighlight.next(null); - } + hideLocationHighlight() { + this.locationHighlight.next(null); + } } diff --git a/src/app/services/modal.service.ts b/src/app/services/modal.service.ts index 9487cc7..7e77edd 100644 --- a/src/app/services/modal.service.ts +++ b/src/app/services/modal.service.ts @@ -1,37 +1,37 @@ import { Injectable } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NavigationStart, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmationModalComponent } from '../components/confirmation-modal/confirmation-modal.component'; @Injectable({ providedIn: 'root' }) export class ModalService { - public btnOkText = $localize`:@@modal.accept:OK`; - public btnCancelText = $localize`:@@modal.decline:Cancel`; + public btnOkText = $localize`:@@modal.accept:OK`; + public btnCancelText = $localize`:@@modal.decline:Cancel`; - constructor(private router: Router, private modalService: NgbModal) { - this.router.events.subscribe((event) => { - if (event instanceof NavigationStart) { - this.modalService.dismissAll('Route changed.'); - } - }); - } + constructor(private router: Router, private modalService: NgbModal) { + this.router.events.subscribe((event) => { + if (event instanceof NavigationStart) { + this.modalService.dismissAll('Route changed.'); + } + }); + } - public async confirm( - title: string, - message: string, - btnOkText: string = this.btnOkText, - btnCancelText: string = this.btnCancelText, - dialogSize: 'sm' | 'lg' = 'sm' - ): Promise { - const modalRef = this.modalService.open(ConfirmationModalComponent, { - size: dialogSize, - windowClass: 'modal-window', - }); - modalRef.componentInstance.title = title; - modalRef.componentInstance.message = message; - modalRef.componentInstance.btnOkText = btnOkText; - modalRef.componentInstance.btnCancelText = btnCancelText; + public async confirm( + title: string, + message: string, + btnOkText: string = this.btnOkText, + btnCancelText: string = this.btnCancelText, + dialogSize: 'sm' | 'lg' = 'sm', + ): Promise { + const modalRef = this.modalService.open(ConfirmationModalComponent, { + size: dialogSize, + windowClass: 'modal-window', + }); + modalRef.componentInstance.title = title; + modalRef.componentInstance.message = message; + modalRef.componentInstance.btnOkText = btnOkText; + modalRef.componentInstance.btnCancelText = btnCancelText; - return modalRef.result; - } + return modalRef.result; + } } diff --git a/src/app/services/observation-goal.service.ts b/src/app/services/observation-goal.service.ts index 74d3003..009182c 100644 --- a/src/app/services/observation-goal.service.ts +++ b/src/app/services/observation-goal.service.ts @@ -1,66 +1,62 @@ -import { EnvService } from './env.service'; -import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { EnvService } from './env.service'; export interface IObservationGoal { - _id: string; - name: string; - description: string; - legalGround?: string; - legalGroundLink?: string; - canEdit?: boolean; + _id: string; + name: string; + description: string; + legalGround?: string; + legalGroundLink?: string; + canEdit?: boolean; } export interface IRegisterObservationGoalBody { - name: string; - description: string; - legalGround?: string; - legalGroundLink?: string; + name: string; + description: string; + legalGround?: string; + legalGroundLink?: string; } export interface IUpdateObservationGoalBody { - name?: string; - description?: string; - legalGround?: string; - legalGroundLink?: string; + name?: string; + description?: string; + legalGround?: string; + legalGroundLink?: string; } @Injectable({ providedIn: 'root' }) export class ObservationGoalService { + constructor(private http: HttpClient, private env: EnvService) {} - constructor( - private http: HttpClient, - private env: EnvService, - ) {} - - public register(observationGoal: IRegisterObservationGoalBody) { - return this.http.post(`${this.env.apiUrl}/observationgoal`, observationGoal); - } - - public update(observationGoalId: string, observationGoal: IUpdateObservationGoalBody) { - return this.http.put(`${this.env.apiUrl}/observationgoal/${observationGoalId}`, observationGoal); - } - - public delete(observationGoalId: string) { - return this.http.delete(`${this.env.apiUrl}/observationgoal/${observationGoalId}`); - } - - public get(observationGoalId: string) { - return this.http.get(`${this.env.apiUrl}/observationgoal/${observationGoalId}`); - } + public register(observationGoal: IRegisterObservationGoalBody) { + return this.http.post(`${this.env.apiUrl}/observationgoal`, observationGoal); + } - public getObservationGoals(args: Record) { - let url = `${this.env.apiUrl}/observationgoal?pageIndex=${args.pageIndex}&pageSize=${args.pageSize}`; - if (args.name) { - url += `&name=${args.name}`; + public update(observationGoalId: string, observationGoal: IUpdateObservationGoalBody) { + return this.http.put(`${this.env.apiUrl}/observationgoal/${observationGoalId}`, observationGoal); } - if (args.sortField) { - url += `&sortField=${args.sortField}`; + + public delete(observationGoalId: string) { + return this.http.delete(`${this.env.apiUrl}/observationgoal/${observationGoalId}`); } - if (args.sortDirection) { - url += `&sortDirection=${args.sortDirection}`; + + public get(observationGoalId: string) { + return this.http.get(`${this.env.apiUrl}/observationgoal/${observationGoalId}`); } - return this.http.get(url); - } + public getObservationGoals(args: Record) { + let url = `${this.env.apiUrl}/observationgoal?pageIndex=${args.pageIndex}&pageSize=${args.pageSize}`; + if (args.name) { + url += `&name=${args.name}`; + } + if (args.sortField) { + url += `&sortField=${args.sortField}`; + } + if (args.sortDirection) { + url += `&sortDirection=${args.sortDirection}`; + } + + return this.http.get(url); + } } diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts index 2c9307f..d247b32 100644 --- a/src/app/services/user.service.ts +++ b/src/app/services/user.service.ts @@ -1,24 +1,21 @@ -import { Injectable } from '@angular/core'; -import { EnvService } from './env.service'; import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { UserUpdateBody } from '../model/bodies/user-update'; +import { EnvService } from './env.service'; @Injectable({ providedIn: 'root' }) export class UserService { - constructor( - private readonly http: HttpClient, - private readonly env: EnvService, - ) {} + constructor(private readonly http: HttpClient, private readonly env: EnvService) {} - public retrieve() { - return this.http.get(`${this.env.apiUrl}/user`); - } + public retrieve() { + return this.http.get(`${this.env.apiUrl}/user`); + } - public update(user: UserUpdateBody) { - return this.http.put(`${this.env.apiUrl}/user`, user); - } + public update(user: UserUpdateBody) { + return this.http.put(`${this.env.apiUrl}/user`, user); + } - public updateById(userId: string, user: Record) { - return this.http.put(`${this.env.apiUrl}/user/${userId}`, user); - } + public updateById(userId: string, user: Record) { + return this.http.put(`${this.env.apiUrl}/user/${userId}`, user); + } } diff --git a/src/app/sidebar/sidebar.component.ts b/src/app/sidebar/sidebar.component.ts index 2112fe6..5ce3618 100644 --- a/src/app/sidebar/sidebar.component.ts +++ b/src/app/sidebar/sidebar.component.ts @@ -1,19 +1,16 @@ -import { Router } from '@angular/router'; import { Component } from '@angular/core'; +import { Router } from '@angular/router'; import { ConnectionService } from '../services/connection.service'; @Component({ - selector: 'app-sidebar', - templateUrl: './sidebar.component.html', - styleUrls: ['./sidebar.component.scss'], + selector: 'app-sidebar', + templateUrl: './sidebar.component.html', + styleUrls: ['./sidebar.component.scss'], }) export class SidebarComponent { - constructor( - private router: Router, - private connectionService: ConnectionService, - ) {} + constructor(private router: Router, private connectionService: ConnectionService) {} - public async logout() { - await this.connectionService.logoutRedirect(); - } + public async logout() { + await this.connectionService.logoutRedirect(); + } } diff --git a/src/app/validators/organization-mail.validator.ts b/src/app/validators/organization-mail.validator.ts index e562426..dd48692 100644 --- a/src/app/validators/organization-mail.validator.ts +++ b/src/app/validators/organization-mail.validator.ts @@ -1,21 +1,35 @@ import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; export const supportedNames = [ - 'info', 'sensor', 'beheer', 'privacy', 'kcc', 'service', 'klant', 'gemeente', 'support', 'help', 'ondersteuning', - 'informatie', 'management', 'team', 'afdeling', 'data', + 'info', + 'sensor', + 'beheer', + 'privacy', + 'kcc', + 'service', + 'klant', + 'gemeente', + 'support', + 'help', + 'ondersteuning', + 'informatie', + 'management', + 'team', + 'afdeling', + 'data', ]; export function createOrganizationMailValidator(): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - const value = control.value; + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; - if (!value) { - return null; - } + if (!value) { + return null; + } - const domainsRegexPart = supportedNames.join('|'); - const emailMatchesDomains = new RegExp(`^.*(${domainsRegexPart}).*@.+[.].+$`).test(value); + const domainsRegexPart = supportedNames.join('|'); + const emailMatchesDomains = new RegExp(`^.*(${domainsRegexPart}).*@.+[.].+$`).test(value); - return !emailMatchesDomains ? {mismatch: true} : null; - }; + return !emailMatchesDomains ? { mismatch: true } : null; + }; } diff --git a/src/app/viewer/viewer.component.ts b/src/app/viewer/viewer.component.ts index 0c0a19f..b52f21e 100644 --- a/src/app/viewer/viewer.component.ts +++ b/src/app/viewer/viewer.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; @Component({ - templateUrl: './viewer.component.html', - styleUrls: ['./viewer.component.scss'], + templateUrl: './viewer.component.html', + styleUrls: ['./viewer.component.scss'], }) export class ViewerComponent { - constructor() {} + constructor() {} } diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index c966979..da7c84f 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,3 +1,3 @@ export const environment = { - production: true, + production: true, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 31cb785..b147faf 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -3,7 +3,7 @@ // The list of file replacements can be found in `angular.json`. export const environment = { - production: false, + production: false, }; /* diff --git a/src/main.ts b/src/main.ts index db96f11..207c6dd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,8 +5,9 @@ import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { - enableProdMode(); + enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch((err) => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/polyfills.ts b/src/polyfills.ts index e0e0cae..896b37f 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -59,7 +59,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/src/test.ts b/src/test.ts index 4bd64e7..b463f44 100644 --- a/src/test.ts +++ b/src/test.ts @@ -2,23 +2,21 @@ import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting, -} from '@angular/platform-browser-dynamic/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { - keys(): string[]; - (id: string): T; - }; + context( + path: string, + deep?: boolean, + filter?: RegExp, + ): { + keys(): string[]; + (id: string): T; + }; }; // First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), -); +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); // Then we find all the tests. const context = require.context('./', true, /\.spec\.ts$/); // And load the modules. diff --git a/tslint.json b/tslint.json deleted file mode 100644 index b8625f8..0000000 --- a/tslint.json +++ /dev/null @@ -1,149 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "align": { - "options": [ - "parameters", - "statements" - ] - }, - "array-type": false, - "arrow-return-shorthand": true, - "curly": true, - "deprecation": { - "severity": "warning" - }, - "component-class-suffix": true, - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ], - "eofline": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "import-spacing": true, - "indent": { - "options": [ - "spaces" - ] - }, - "max-classes-per-file": false, - "max-line-length": [ - true, - 140 - ], - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-empty": false, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-switch-case-fall-through": true, - "no-var-requires": false, - "object-literal-key-quotes": [ - true, - "as-needed" - ], - "object-literal-sort-keys": false, - "quotemark": [ - true, - "single" - ], - "semicolon": { - "options": [ - "always" - ] - }, - "space-before-function-paren": { - "options": { - "anonymous": "never", - "asyncArrow": "always", - "constructor": "never", - "method": "never", - "named": "never" - } - }, - "typedef-whitespace": { - "options": [ - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ] - }, - "variable-name": { - "options": [ - "ban-keywords", - "check-format", - "allow-pascal-case" - ] - }, - "whitespace": { - "options": [ - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type", - "check-typecast" - ] - }, - "no-conflicting-lifecycle": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-inputs-metadata-property": true, - "no-output-native": true, - "no-output-on-prefix": true, - "no-output-rename": true, - "no-outputs-metadata-property": true, - "template-banana-in-box": true, - "template-no-negated-async": true, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] -}