diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 2b4453c7bc..8ca9766c05 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,8 +1,6 @@ module.exports = { extends: [ "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" ], env: { browser: true, @@ -19,19 +17,23 @@ module.exports = { ], overrides: [ { - "files": ["*.ts"], - "parserOptions": { - "project": [ - "./src/tsconfig.app.json" - ], - "createDefaultProgram": true + files: ["*.ts"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "./src/tsconfig.app.json", + tsconfigRootDir: __dirname, + sourceType: "module", + createDefaultProgram: true }, - "extends": [ + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", "plugin:@angular-eslint/recommended", // This is required if you use inline templates in Components - "plugin:@angular-eslint/template/process-inline-templates" + "plugin:@angular-eslint/template/process-inline-templates", + "airbnb-typescript", ], - "rules": { + rules: { /** * Any TypeScript source code (NOT TEMPLATE) related rules you wish to use/reconfigure over and above the * recommended set provided by the @angular-eslint project would go here. @@ -44,97 +46,32 @@ module.exports = { "error", { "type": "element", "prefix": "op", "style": "kebab-case" } ], - "@typescript-eslint/dot-notation": "off", - "@typescript-eslint/naming-convention": "off", - "@typescript-eslint/no-empty-function": "error", - // note you must disable the base rule as it can report incorrect errors - semi: "off", - "@typescript-eslint/semi": ["error"], - "brace-style": [ - "error", - "1tbs", - ], - curly: "error", - "eol-last": "off", - eqeqeq: [ - "error", - "smart", - ], - "guard-for-in": "error", - "id-blacklist": "off", - "id-match": "off", - "max-len": [ - "off", - { - code: 140, - }, - ], - "no-bitwise": "off", - "no-caller": "error", + "no-console": [ "error", { allow: [ - "log", "warn", - "dir", - "timeLog", - "assert", - "clear", - "count", - "countReset", - "group", - "groupEnd", - "table", - "dirxml", "error", - "groupCollapsed", - "Console", - "profile", - "profileEnd", - "timeStamp", - "context", ], }, ], - "no-debugger": "error", - "no-empty": "error", - "no-eval": "error", - "no-new-wrappers": "error", - "no-redeclare": "error", - "no-trailing-spaces": "error", - "no-underscore-dangle": "off", - "no-unused-labels": "error", - "no-var": "off", - radix: "off", - // Disable required spaces in license comments - "spaced-comment": "off", - // Disable preference on quotes, rely on formatter instead - quotes: "off", + // Who cares about line length + "max-len": "off", - // Disable consistent return as typescript checks return type - "consistent-return": "off", - - // Disable forcing arrow function params for one - "arrow-parens": "off", - - // Disable enforce class methods use this - "class-methods-use-this": "off", + // Force single quotes to align with ruby + quotes: "off", + "@typescript-eslint/quotes": ["error", "single", { avoidEscape: true }], // Disable webpack loader definitions "import/no-webpack-loader-syntax": "off", + /* // Disable use before define, as irrelevant for TS interfaces "no-use-before-define": "off", "@typescript-eslint/no-use-before-define": "off", - - // Allow object.hasOwnProperty calls - "no-prototype-builtins": "off", - - // We need to redeclare interface with the same name - // as a class or constant for type ducking - "no-redeclare": "off", + */ // Whitespace configuration "@typescript-eslint/type-annotation-spacing": [ @@ -154,17 +91,34 @@ module.exports = { // Allow empty interfaces for naming purposes (HAL resources) "@typescript-eslint/no-empty-interface": "off", - // Force spaces in objects - "object-curly-spacing": ["error", "always"], + "import/prefer-default-export": "off", + + "no-underscore-dangle": "warn", + "no-return-assign": ["error", "except-parens"], + "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], + + ////////////////////////////////////////////////////////////////////// + // Anything below this line should be turned on again at some point // + ////////////////////////////////////////////////////////////////////// + + // It's common in Angular to wrap even pure functions in classes for injection purposes + // TODO: Should probably be turned off and pure unit tests should be used at some point + "class-methods-use-this": "warn", + + // There's too much interop with legacy code that is `any`-typed for this to be an error in any practical sense + // TODO: Actually type everything + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-unsafe-call": "warn", - // Force indent to 2space - indent: ["error", 2], + // This is probably the first rule that should be fixed. It had 309 errors last time we checked + "@typescript-eslint/no-unsafe-return": "warn", } }, { - "files": ["*.html"], - "extends": ["plugin:@angular-eslint/template/recommended"], - "rules": { + files: ["*.html"], + extends: ["plugin:@angular-eslint/template/recommended"], + rules: { /** * Any template/HTML related rules you wish to use/reconfigure over and above the * recommended set provided by the @angular-eslint project would go here. diff --git a/frontend/.esprintrc b/frontend/.esprintrc new file mode 100644 index 0000000000..388aee52f2 --- /dev/null +++ b/frontend/.esprintrc @@ -0,0 +1,10 @@ +{ + "paths": ["src/**/*.ts", "src/*.ts"], + "ignored": [ + "**/node_modules/**/*", + "src/**/*.spec.ts", + "src/test/*" + ], + "port": 5004, + "quiet": true +} diff --git a/frontend/npm-shrinkwrap.json b/frontend/npm-shrinkwrap.json index 4a4eb352f0..f2496a4728 100644 --- a/frontend/npm-shrinkwrap.json +++ b/frontend/npm-shrinkwrap.json @@ -1,18247 +1,8 @@ { "name": "openproject-frontend", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "openproject-frontend", - "version": "0.1.0", - "license": "GPLv3", - "dependencies": { - "@angular-devkit/build-angular": "~12.0.2", - "@angular/animations": "~12.0.2", - "@angular/cdk": "^12.0.2", - "@angular/cli": "~12.0.2", - "@angular/common": "~12.0.2", - "@angular/compiler": "~12.0.2", - "@angular/compiler-cli": "~12.0.2", - "@angular/core": "~12.0.2", - "@angular/forms": "~12.0.2", - "@angular/platform-browser": "~12.0.2", - "@angular/platform-browser-dynamic": "~12.0.2", - "@angular/router": "~12.0.2", - "@datorama/akita": "^6.1.3", - "@fullcalendar/angular": "5.5.0", - "@fullcalendar/core": "5.5.0", - "@fullcalendar/daygrid": "5.5.0", - "@fullcalendar/interaction": "5.5.0", - "@fullcalendar/timegrid": "5.5.0", - "@kolkov/ngx-gallery": "^1.0.11", - "@ng-select/ng-option-highlight": "0.0.5", - "@ng-select/ng-select": "^4.0.4", - "@ngx-formly/core": "^5.10.19", - "@sentry/angular": "^6.2.3", - "@sentry/tracing": "^6.2.3", - "@types/chart.js": "^2.9.20", - "@types/codemirror": "0.0.87", - "@types/dragula": "^3.7.0", - "@types/hammerjs": "^2.0.36", - "@types/jquery": "^3.3.33", - "@types/jqueryui": "^1.12.10", - "@types/lodash": "^4.14.149", - "@types/moment-timezone": "^0.5.12", - "@types/mousetrap": "^1.6.3", - "@types/pako": "^1.0.1", - "@types/resize-observer-browser": "^0.1.4", - "@types/urijs": "^1.19.6", - "@types/webpack-env": "^1.16.0", - "@uirouter/angular": "^8.0.0", - "@uirouter/core": "^6.0.7", - "@uirouter/rx": "^0.6.5", - "@w11k/ngx-componentdestroyed": "^5.0.2", - "@xeokit/xeokit-bim-viewer": "2.2.0", - "@xeokit/xeokit-sdk": "1.9.0", - "autoprefixer": "^9.6.1", - "browserslist": "^4.9.1", - "byte-base64": "^1.1.0", - "cdk-drag-scroll": "^0.0.6", - "chart.js": "2.9.4", - "chartjs-plugin-datalabels": "^0.6.0", - "codemirror": "^5.48.4", - "copy-text-to-clipboard": "^3.0.0", - "core-js": "^3.2.1", - "crossvent": "^1.5.4", - "dom-autoscroller": "^2.2.8", - "dom-plane": "^1.0.2", - "dragula": "^3.5.2", - "expose-loader": "^0.7.5", - "flatpickr": "^4.6.3", - "fuse.js": "^3.4.5", - "glob": "^7.1.4", - "hammerjs": "^2.0.8", - "jquery": "^3.5.1", - "jquery-ui": "git+https://github.com/jquery/jquery-ui.git#74f8a0ac952f6f45f773312292baef1c26d81300", - "jquery-ujs": "^1.2.2", - "jquery.caret": "^0.3.1", - "json5": "^2.1.0", - "lodash": "^4.17.19", - "mark.js": "^8.11.0", - "moment": "^2.24.0", - "moment-timezone": "^0.5.26", - "mousetrap": "~1.6.3", - "ng-dynamic-component": "^6.0.0", - "ng2-charts": "^2.3.1", - "ng2-dragula": "^2.1.1", - "observable-array": "0.0.4", - "pako": "^2.0.3", - "reactivestates": "2.0.1", - "reflect-metadata": "^0.1.13", - "rxjs": "^6.6.6", - "screenfull": "^4.2.1", - "tablesorter": "^2.31.3", - "tickety-tick-formatter": "github:bitcrowd/tickety-tick-formatter", - "typedjson": "^1.5.1", - "typescript": "~4.2.4", - "urijs": "^1.19.2", - "zone.js": "~0.11.4" - }, - "devDependencies": { - "@angular-eslint/builder": "^12.0.0", - "@angular-eslint/eslint-plugin": "^12.0.0", - "@angular-eslint/eslint-plugin-template": "^12.0.0", - "@angular-eslint/schematics": "12.0.0", - "@angular-eslint/template-parser": "^12.0.0", - "@angular/language-service": "12.0.2", - "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@types/jasmine": "~3.6.0", - "@typescript-eslint/eslint-plugin": "4.23.0", - "@typescript-eslint/parser": "4.23.0", - "codelyzer": "^6.0.0", - "eslint": "^7.26.0", - "eslint-config-airbnb-base": "^14.2.1", - "eslint-plugin-import": "^2.22.1", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", - "karma": "~6.3.2", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage": "~2.0.3", - "karma-coverage-istanbul-reporter": "~3.0.2", - "karma-jasmine": "~3.3.0", - "karma-jasmine-html-reporter": "^1.5.0", - "karma-spec-reporter": "^0.0.32", - "ts-node": "~8.3.0" - } - }, - "node_modules/@angular-devkit/architect": { - "version": "0.1200.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1200.2.tgz", - "integrity": "sha512-Vy/dE1iwEiV63cxcU+SC+Lf5SUnY64vg9J3YA3jxFlJnELbxxN+T7xDfjMEMPoLzTY02K9XNb8ZGLStZxVmZLg==", - "dependencies": { - "@angular-devkit/core": "12.0.2", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-12.0.2.tgz", - "integrity": "sha512-s7tAnqT3L+4uMnDHOwx4nncxDjHrmfPGiQFdi2hI2VTbuB7ZUU7LE9M/2O5MWnRcwNKOlE/Ls6tWZRYD2HoBsA==", - "dependencies": { - "@angular-devkit/architect": "0.1200.2", - "@angular-devkit/build-optimizer": "0.1200.2", - "@angular-devkit/build-webpack": "0.1200.2", - "@angular-devkit/core": "12.0.2", - "@babel/core": "7.14.3", - "@babel/generator": "7.14.3", - "@babel/plugin-transform-async-to-generator": "7.13.0", - "@babel/plugin-transform-runtime": "7.14.3", - "@babel/preset-env": "7.14.2", - "@babel/runtime": "7.14.0", - "@babel/template": "7.12.13", - "@discoveryjs/json-ext": "0.5.2", - "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "12.0.2", - "ansi-colors": "4.1.1", - "babel-loader": "8.2.2", - "browserslist": "^4.9.1", - "cacache": "15.0.6", - "caniuse-lite": "^1.0.30001032", - "circular-dependency-plugin": "5.2.2", - "copy-webpack-plugin": "8.1.1", - "core-js": "3.12.0", - "critters": "0.0.10", - "css-loader": "5.2.4", - "css-minimizer-webpack-plugin": "3.0.0", - "find-cache-dir": "3.3.1", - "glob": "7.1.7", - "https-proxy-agent": "5.0.0", - "inquirer": "8.0.0", - "jest-worker": "26.6.2", - "karma-source-map-support": "1.4.0", - "less": "4.1.1", - "less-loader": "8.1.1", - "license-webpack-plugin": "2.3.17", - "loader-utils": "2.0.0", - "mini-css-extract-plugin": "1.5.1", - "minimatch": "3.0.4", - "open": "8.0.2", - "ora": "5.4.0", - "parse5-html-rewriting-stream": "6.0.1", - "postcss": "8.3.0", - "postcss-import": "14.0.1", - "postcss-loader": "5.2.0", - "postcss-preset-env": "6.7.0", - "raw-loader": "4.0.2", - "regenerator-runtime": "0.13.7", - "resolve-url-loader": "4.0.0", - "rimraf": "3.0.2", - "rxjs": "6.6.7", - "sass": "1.32.12", - "sass-loader": "11.0.1", - "semver": "7.3.5", - "source-map": "0.7.3", - "source-map-loader": "2.0.1", - "source-map-support": "0.5.19", - "style-loader": "2.0.0", - "stylus": "0.54.8", - "stylus-loader": "5.0.0", - "terser": "5.7.0", - "terser-webpack-plugin": "5.1.2", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "webpack": "5.36.2", - "webpack-dev-middleware": "4.1.0", - "webpack-dev-server": "3.11.2", - "webpack-merge": "5.7.3", - "webpack-subresource-integrity": "1.5.2" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^12.0.0", - "@angular/localize": "^12.0.0", - "@angular/service-worker": "^12.0.0", - "karma": "^6.3.0", - "ng-packagr": "^12.0.0", - "protractor": "^7.0.0", - "tailwindcss": "^2.0.0", - "tslint": "^6.1.0", - "typescript": "~4.2.3" - }, - "peerDependenciesMeta": { - "@angular/localize": { - "optional": true - }, - "@angular/service-worker": { - "optional": true - }, - "karma": { - "optional": true - }, - "ng-packagr": { - "optional": true - }, - "protractor": { - "optional": true - }, - "tailwindcss": { - "optional": true - }, - "tslint": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/circular-dependency-plugin": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", - "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-middleware": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-4.1.0.tgz", - "integrity": "sha512-mpa/FY+DiBu5+r5JUIyTCYWRfkWgyA3/OOE9lwfzV9S70A4vJYLsVRKj5rMFEsezBroy2FmPyQ8oBRVW8QmK1A==", - "dependencies": { - "colorette": "^1.2.1", - "mem": "^8.0.0", - "memfs": "^3.2.0", - "mime-types": "^2.1.28", - "range-parser": "^1.2.1", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/@angular-devkit/build-optimizer": { - "version": "0.1200.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1200.2.tgz", - "integrity": "sha512-46z35d4oOHiF7Peiez7DRcsB5dwjnYP3fm6KwVNm/8Zq6nnykxvipywgJB6inkGdcI0j2m8v3+shVAaWvdS93w==", - "dependencies": { - "source-map": "0.7.3", - "tslib": "2.2.0", - "typescript": "4.2.4" - }, - "bin": { - "build-optimizer": "src/build-optimizer/cli.js" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "webpack": "^5.30.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-optimizer/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1200.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1200.2.tgz", - "integrity": "sha512-tqHq2Ld91CQfIJpdsA8b5q6wq4tS3eX5Z1rwzy2j0gJ5s0I/cpklfTXHBh6StSoS9Df4uHl5V2oLRBagky1pDA==", - "dependencies": { - "@angular-devkit/architect": "0.1200.2", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "webpack": "^5.30.0", - "webpack-dev-server": "^3.1.4" - } - }, - "node_modules/@angular-devkit/core": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-12.0.2.tgz", - "integrity": "sha512-n7BmZAW0nx4pEigdAsibvtIm4Vjk1IwY3Bbc8fqD+AQpdYCo+Ake1Eu6fL2XoW8Yco7U4JYYdn/uodWEgBdroQ==", - "dependencies": { - "ajv": "8.2.0", - "ajv-formats": "2.0.2", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/ajv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.2.0.tgz", - "integrity": "sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/core/node_modules/ajv-formats": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.0.2.tgz", - "integrity": "sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/@angular-devkit/schematics": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-12.0.2.tgz", - "integrity": "sha512-PS+SrRhAc/lyRfuBsi6Rt2yV7IA34B7T6J0K8/Av0GABZ83x+0vLiZC39eSPS1X8qcM/U09pCfDT8Q6ZQPCICA==", - "dependencies": { - "@angular-devkit/core": "12.0.2", - "ora": "5.4.0", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-eslint/builder": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-12.0.0.tgz", - "integrity": "sha512-gvvXQDXXi0gsWZ25KyMqF/1b3AaX+CJbpVgTPqxJdEx4euvmG/m3993ynmpf+Kc+F+aI2O9W4TUbDbbLWoCjIA==", - "dev": true, - "peerDependencies": { - "@angular/cli": ">= 12.0.0 < 13.0.0", - "eslint": "*", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-12.0.0.tgz", - "integrity": "sha512-osdJdMu8bYFv9WGhC04AwRcbeKq4sxCQnShV7NiF0xkgNG9KqDaStytVhPjJFn2Ja1QhfiTGlcFFk4D/9aruog==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.23.0" - }, - "peerDependencies": { - "@angular/compiler": ">= 12.0.0 < 13.0.0", - "eslint": "*", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-12.0.0.tgz", - "integrity": "sha512-RF8PwN2A3U4ihd7sKYUM8wgPj46M30reziLl8CPPhN3H5Hn46nksmKmHRbPNakH2gW0Ba7NIxy+ocqUy0fQpcQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.23.0", - "aria-query": "^4.2.2", - "axobject-query": "^2.2.0" - }, - "peerDependencies": { - "@angular/compiler": ">= 12.0.0 < 13.0.0", - "eslint": "*", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template/node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", - "dev": true - }, - "node_modules/@angular-eslint/schematics": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-12.0.0.tgz", - "integrity": "sha512-AwYEVuAQLJfyIF5vxBL98a67ecF9U25pQSIg0xCY6DeDpIaGdORr4yg2rGYy8fTlzDQo6BctKZQOTiVX3Y3uew==", - "dev": true, - "dependencies": { - "@angular-eslint/eslint-plugin": "12.0.0", - "@angular-eslint/eslint-plugin-template": "12.0.0", - "ignore": "5.1.8", - "strip-json-comments": "3.1.1", - "tmp": "0.2.1" - }, - "peerDependencies": { - "@angular/cli": ">= 12.0.0 < 13.0.0" - } - }, - "node_modules/@angular-eslint/template-parser": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-12.0.0.tgz", - "integrity": "sha512-gl5ansA2a8LY6TEjhe0k8NiQJJdEQPjjqpysO1Rpt3NWUYQkFMt+1+AnUyokHB1TU3/11dHRUjVWXj+pMtTIAA==", - "dev": true, - "dependencies": { - "eslint-scope": "^5.1.0" - }, - "peerDependencies": { - "@angular/compiler": ">= 12.0.0 < 13.0.0", - "eslint": "*", - "typescript": "*" - } - }, - "node_modules/@angular/animations": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-12.0.2.tgz", - "integrity": "sha512-5RZ5a8ouVZ3iDy29uZ7jCryisWjX2c9xjIG5F5rn+mBL3UkxzzUDFhql9rP0PB5G+aGgaElSlTatGJ85mY0XvA==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/core": "12.0.2" - } - }, - "node_modules/@angular/animations/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/cdk": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.0.2.tgz", - "integrity": "sha512-1JGayyUJmwaul5YSEgb780aWxk+MLVG5FakVaxz5NtbPZx19ZyOuZqytCh5js11LsBDipF5/kirYhJPBFlMbWQ==", - "dependencies": { - "tslib": "^2.1.0" - }, - "optionalDependencies": { - "parse5": "^5.0.0" - }, - "peerDependencies": { - "@angular/common": "^12.0.0 || ^13.0.0-0", - "@angular/core": "^12.0.0 || ^13.0.0-0", - "rxjs": "^6.5.3" - } - }, - "node_modules/@angular/cdk/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - }, - "node_modules/@angular/cdk/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/cli": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.0.2.tgz", - "integrity": "sha512-hXxnOxPl6+v/O+OnkJxPPupCeQJNmI08EdtnER5z/UCSpmlJibbTAqLF9rFaOi/7dPLS0RCNWmCRA6grgTlP9g==", - "hasInstallScript": true, - "dependencies": { - "@angular-devkit/architect": "0.1200.2", - "@angular-devkit/core": "12.0.2", - "@angular-devkit/schematics": "12.0.2", - "@schematics/angular": "12.0.2", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.3.1", - "ini": "2.0.0", - "inquirer": "8.0.0", - "jsonc-parser": "3.0.0", - "npm-package-arg": "8.1.2", - "npm-pick-manifest": "6.1.1", - "open": "8.0.2", - "ora": "5.4.0", - "pacote": "11.3.2", - "resolve": "1.20.0", - "rimraf": "3.0.2", - "semver": "7.3.5", - "symbol-observable": "4.0.0", - "uuid": "8.3.2" - }, - "bin": { - "ng": "bin/ng" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/cli/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@angular/cli/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@angular/cli/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@angular/cli/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/cli/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@angular/common": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-12.0.2.tgz", - "integrity": "sha512-KJFtqdIGVrF97iK3zcN9FoQ1fDsc/u4ez1Qtx0HdH21qAuAvK/FYybg4r1G6miXoUJcO8hu9oRddEoiNPkXAew==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/core": "12.0.2", - "rxjs": "^6.5.3" - } - }, - "node_modules/@angular/common/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/compiler": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-12.0.2.tgz", - "integrity": "sha512-PLkDb1mxWpsOJwR0t9yDI8A9dSMUEmVf+HdNWFO1aFY84nZ3sH8t6e/BpoaRcbLJCkNgtm9YD8FmRHE30LZ3CA==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - } - }, - "node_modules/@angular/compiler-cli": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.0.2.tgz", - "integrity": "sha512-ocm/c4ZcdtlvMSlN1L6Asyx4nYC4qA0j3+H3mKl/ds8bq/8Gb9cxOiu3hqmUKgovXF/Wue6orsWLzHowuxfagA==", - "dependencies": { - "@babel/core": "^7.8.6", - "@babel/types": "^7.8.6", - "canonical-path": "1.0.0", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "semver": "^7.0.0", - "source-map": "^0.6.1", - "sourcemap-codec": "^1.4.8", - "tslib": "^2.1.0", - "yargs": "^16.2.0" - }, - "bin": { - "ivy-ngcc": "ngcc/main-ivy-ngcc.js", - "ng-xi18n": "src/extract_i18n.js", - "ngc": "src/main.js", - "ngcc": "ngcc/main-ngcc.js" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/compiler": "12.0.2", - "typescript": ">=4.2.3 <4.3" - } - }, - "node_modules/@angular/compiler-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@angular/compiler-cli/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@angular/compiler-cli/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/compiler-cli/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/compiler-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@angular/compiler-cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/compiler-cli/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/compiler-cli/node_modules/yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@angular/compiler/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/core": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-12.0.2.tgz", - "integrity": "sha512-RBTl8L2ckSI3n2lo9rmwlUeZAuEd/TWUjoidoxPYWrKfbnFwKX65idCCbWLJivSVIBnPQYvAdC+3k4w40N9AWA==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3", - "zone.js": "~0.11.4" - } - }, - "node_modules/@angular/core/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/forms": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-12.0.2.tgz", - "integrity": "sha512-tHIYb3c6H2M9qSi/SJOtZqJ8XWi3LM5mhA4sDpEGxfJ8RLVTcujd2gYnJ0N3vcpmWwy3mA/EayhAC2bhgWAazA==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/common": "12.0.2", - "@angular/core": "12.0.2", - "@angular/platform-browser": "12.0.2", - "rxjs": "^6.5.3" - } - }, - "node_modules/@angular/forms/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/language-service": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-12.0.2.tgz", - "integrity": "sha512-H1aauvtS9qnq0FCdr3J2UROF7+AnOTCksD2AYuGS2/9O8tZLwwNz/UkXfxS3bHYzndYCsNiLPwAW7d0dWIjlqQ==", - "dev": true, - "engines": { - "node": "^12.14.1 || >=14.0.0" - } - }, - "node_modules/@angular/platform-browser": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.0.2.tgz", - "integrity": "sha512-m1tPOpNZdnp8jrtFP3FrjpBV+S/rd5HLd4Q9MaC62LJYf8VvRx8wKdUO8tBDJYPbGiHGL4q4BUvKswrzfFWbXg==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/animations": "12.0.2", - "@angular/common": "12.0.2", - "@angular/core": "12.0.2" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } - } - }, - "node_modules/@angular/platform-browser-dynamic": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.0.2.tgz", - "integrity": "sha512-FVJBIxn85EX3a6kSdqwXAC8i7jxeo5iwHBKUEFYukbAXrHuMmwFz7Nvqkqqo0xIJXymFz8Q04VbTThkgp2uerg==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/common": "12.0.2", - "@angular/compiler": "12.0.2", - "@angular/core": "12.0.2", - "@angular/platform-browser": "12.0.2" - } - }, - "node_modules/@angular/platform-browser-dynamic/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/platform-browser/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@angular/router": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-12.0.2.tgz", - "integrity": "sha512-ATUPPz+dpuPNNWlvg6PtLBDN97nbT8Bu7tUwjL8G0xOwxAxsJqgoImcxYWAKaovAoK4YntVQXqP+UPxpZQtLLA==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": "^12.14.1 || >=14.0.0" - }, - "peerDependencies": { - "@angular/common": "12.0.2", - "@angular/core": "12.0.2", - "@angular/platform-browser": "12.0.2", - "rxjs": "^6.5.3" - } - }, - "node_modules/@angular/router/node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz", - "integrity": "sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ==" - }, - "node_modules/@babel/core": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", - "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.3", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.3", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", - "dependencies": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz", - "integrity": "sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA==", - "dependencies": { - "@babel/compat-data": "^7.14.4", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz", - "integrity": "sha512-idr3pthFlDCpV+p/rMgGLGYIVtazeatrSOQk8YzO2pAepIjQhCN3myeihVg58ax2bbbGK9PUE1reFi7axOYIOw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.14.4", - "@babel/helper-split-export-declaration": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.3.tgz", - "integrity": "sha512-JIB2+XJrb7v3zceV2XzDhGIB902CmKGSpSl4q2C6agU9SNLG/2V1RtFRGPG1Ajh9STj3+q6zJMOC+N/pp2P9DA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz", - "integrity": "sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", - "dependencies": { - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", - "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", - "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", - "dependencies": { - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.16" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", - "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz", - "integrity": "sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.4" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", - "dependencies": { - "@babel/types": "^7.12.1" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", - "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", - "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", - "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", - "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", - "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.13.12" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz", - "integrity": "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.3.tgz", - "integrity": "sha512-HEjzp5q+lWSjAgJtSluFDrGGosmwTgKwCXdDQZvhKsRlwv3YdkUEqxNrrjesJd+B9E9zvr1PVPVBvhYZ9msjvQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.14.3", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-class-static-block": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz", - "integrity": "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz", - "integrity": "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz", - "integrity": "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz", - "integrity": "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz", - "integrity": "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz", - "integrity": "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.4.tgz", - "integrity": "sha512-AYosOWBlyyXEagrPRfLJ1enStufsr7D1+ddpj8OLi9k7B6+NdZ0t/9V7Fh+wJ4g2Jol8z2JkgczYqtWrZd4vbA==", - "dependencies": { - "@babel/compat-data": "^7.14.4", - "@babel/helper-compilation-targets": "^7.14.4", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz", - "integrity": "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz", - "integrity": "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-create-class-features-plugin": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", - "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", - "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.4.tgz", - "integrity": "sha512-5KdpkGxsZlTk+fPleDtGKsA+pon28+ptYmMO8GBSa5fHERCJWAzj50uAfCKBqq42HO+Zot6JF1x37CRprwmN4g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.4.tgz", - "integrity": "sha512-p73t31SIj6y94RDVX57rafVjttNr8MvKEgs5YFatNB/xC68zM3pyosuOEcQmYsYlyQaGY9R7rAULVRcat5FKJQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.14.4", - "@babel/helper-split-export-declaration": "^7.12.13", - "globals": "^11.1.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.4.tgz", - "integrity": "sha512-JyywKreTCGTUsL1OKu1A3ms/R1sTP0WxbpXlALeGzF53eB3bxtNkYdMj9SDgK7g6ImPy76J5oYYKoTtQImlhQA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", - "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", - "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz", - "integrity": "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", - "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.13.12", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", - "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", - "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", - "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", - "dependencies": { - "regenerator-transform": "^0.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.3.tgz", - "integrity": "sha512-t960xbi8wpTFE623ef7sd+UpEC5T6EEguQlTBJDEO05+XwnIWVfuqLw/vdLWY6IdFmtZE+65CZAfByT39zRpkg==", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", - "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz", - "integrity": "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==", - "dependencies": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.14.2", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-class-static-block": "^7.13.11", - "@babel/plugin-proposal-dynamic-import": "^7.14.2", - "@babel/plugin-proposal-export-namespace-from": "^7.14.2", - "@babel/plugin-proposal-json-strings": "^7.14.2", - "@babel/plugin-proposal-logical-assignment-operators": "^7.14.2", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.2", - "@babel/plugin-proposal-numeric-separator": "^7.14.2", - "@babel/plugin-proposal-object-rest-spread": "^7.14.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.14.2", - "@babel/plugin-proposal-optional-chaining": "^7.14.2", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-private-property-in-object": "^7.14.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.12.13", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.14.2", - "@babel/plugin-transform-classes": "^7.14.2", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.17", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.14.2", - "@babel/plugin-transform-modules-commonjs": "^7.14.0", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.14.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.14.2", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.13.15", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.2", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "core-js-compat": "^3.9.0", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", - "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz", - "integrity": "sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw==", - "dev": true, - "dependencies": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" - } - }, - "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/template/node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/@babel/types": { - "version": "7.14.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", - "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/@csstools/convert-colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", - "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@datorama/akita": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@datorama/akita/-/akita-6.1.3.tgz", - "integrity": "sha512-bruAfUxwLh/lYVwe8iErNwhTr1/Qf4B5VitR+RgeMNVzWx+9/N15+5/9hFVT5yCYMr5Iw8XBFG3cAb+tBm3NVg==", - "dependencies": { - "schematics-utilities": "^1.1.1" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz", - "integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "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==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@fullcalendar/angular": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/angular/-/angular-5.5.0.tgz", - "integrity": "sha512-e1KZTzMjB4/rcCMKmQl7DzQN4cmBOk34KZ2kdrX7cAq6B4yOUUt3A0PMrohU4JKdlLw/XsQL1O4TrPyOoWfMjw==", - "dependencies": { - "@fullcalendar/core": "~5.5.0", - "fast-deep-equal": "^3.1.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 6.9.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/@fullcalendar/common": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/common/-/common-5.5.0.tgz", - "integrity": "sha512-xHsHRI2xnC4vDPdRaOkt03SGIEsmlcSTI6RVInI2PQKi3n4otgM4ej3WL4B010uT4IzrBlUvNm8HHEo/o3g8Ug==", - "dependencies": { - "ical.js": "^1.4.0", - "tslib": "^2.0.3" - } - }, - "node_modules/@fullcalendar/common/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "node_modules/@fullcalendar/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-5.5.0.tgz", - "integrity": "sha512-NHyrH6AP1zRRCWsK0NuY/bZFQes1UaGB2pukMv86A0Yq1BI7Y7m9u/yH7D0/ZCaNkCLVxrSlIwAOtMQxPPITdg==", - "dependencies": { - "@fullcalendar/common": "~5.5.0", - "preact": "^10.0.5", - "tslib": "^2.0.3" - } - }, - "node_modules/@fullcalendar/core/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "node_modules/@fullcalendar/daygrid": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-5.5.0.tgz", - "integrity": "sha512-O/59C3ihElLsPnkjlD1xUgAf3v7WEARXVO+wZfASKtK14Ccux2MA9mWUA1+80m9q8on7SYL36ORhrea7GKy/qQ==", - "dependencies": { - "@fullcalendar/common": "~5.5.0", - "tslib": "^2.0.3" - } - }, - "node_modules/@fullcalendar/daygrid/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "node_modules/@fullcalendar/interaction": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-5.5.0.tgz", - "integrity": "sha512-LKc4VeESBkBkrI/sAtV++f4qUleYyt7STBrnuc5Mpojv7jg5x5XZ1rfOarLwRbEYSvbo6CwccDrsaLTbC1Qk2A==", - "dependencies": { - "@fullcalendar/common": "~5.5.0", - "tslib": "^2.0.3" - } - }, - "node_modules/@fullcalendar/interaction/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "node_modules/@fullcalendar/timegrid": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.5.0.tgz", - "integrity": "sha512-Y4k3/ux031pL7WQWJnvJbGcOua+zg1BGn08xeycgBICNqVMIhKm++uCnLzCvHGDV4Gp7oRjNAw/nJw4M1kMoXg==", - "dependencies": { - "@fullcalendar/common": "~5.5.0", - "@fullcalendar/daygrid": "~5.5.0", - "tslib": "^2.0.3" - } - }, - "node_modules/@fullcalendar/timegrid/node_modules/tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dependencies": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, - "node_modules/@jsdevtools/coverage-istanbul-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/@kolkov/ngx-gallery": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@kolkov/ngx-gallery/-/ngx-gallery-1.0.11.tgz", - "integrity": "sha512-PqtDc+Vffz5Hq5bosUEHk8qkUUTnlCVqRg6MpOfiRtsAF2IkLTS2Yw+czP3Y+wSI9OESvMGEhKmqN7VywkeTGQ==" - }, - "node_modules/@ng-select/ng-option-highlight": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@ng-select/ng-option-highlight/-/ng-option-highlight-0.0.5.tgz", - "integrity": "sha512-X3Ftec/i7Drcmr2dNnhEcRhcWkR+6wJpR5q0DYfwDXve405AcdtDZom7gf2xUDt1d2IXcFejTTVD0jy8qy8Nkw==" - }, - "node_modules/@ng-select/ng-select": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-4.0.4.tgz", - "integrity": "sha512-KaVlVdt7DWZBfmL6nc16i80l1dDx0cO3eGjW+grQPnhZ9KurapioQUYVf6dSmYyWyBXihvt4I3SWkCzqSWFJbQ==", - "dependencies": { - "tslib": "^1.10.0" - }, - "engines": { - "node": ">= 6.9.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/@ng-select/ng-select/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "node_modules/@ngtools/webpack": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.0.2.tgz", - "integrity": "sha512-q+AzIMxWb/8jEDhNrKM2THlJrhUCkX18sKBWY+r221uXtt1sz58MdJyE0UTALvOvjHxEF5OvbWpeAbKUOKgVFA==", - "dependencies": { - "enhanced-resolve": "5.7.0" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^12.0.0", - "typescript": "~4.2.3", - "webpack": "^5.30.0" - } - }, - "node_modules/@ngx-formly/core": { - "version": "5.10.20", - "resolved": "https://registry.npmjs.org/@ngx-formly/core/-/core-5.10.20.tgz", - "integrity": "sha512-TwZfyRBnqd9JqLXR90MVOoxH+6G+B0Uli65DWTwJeO/Sgxb/3zRE7nSDVaSiD4f1Et+ce6JftktiyRMcA2okNA==", - "dependencies": { - "tslib": "^1.7.1" - }, - "peerDependencies": { - "@angular/forms": ">=7.0.0", - "rxjs": ">=6.3.0" - } - }, - "node_modules/@ngx-formly/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", - "dependencies": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/git": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.0.9.tgz", - "integrity": "sha512-hTMbMryvOqGLwnmMBKs5usbPsJtyEsMsgXwJbmNrsEuQQh1LAIMDU77IoOrwkCg+NgQWl+ySlarJASwM3SutCA==", - "dependencies": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - } - }, - "node_modules/@npmcli/git/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/git/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/node-gyp": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz", - "integrity": "sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg==" - }, - "node_modules/@npmcli/promise-spawn": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", - "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", - "dependencies": { - "infer-owner": "^1.0.4" - } - }, - "node_modules/@npmcli/run-script": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.5.tgz", - "integrity": "sha512-NQspusBCpTjNwNRFMtz2C5MxoxyzlbuJ4YEhxAKrIonTiirKDtatsZictx9RgamQIx6+QuHMNmPl0wQdoESs9A==", - "dependencies": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "infer-owner": "^1.0.4", - "node-gyp": "^7.1.0", - "read-package-json-fast": "^2.0.1" - } - }, - "node_modules/@schematics/angular": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.0.2.tgz", - "integrity": "sha512-DMUfp7226QY2FkJeBm1xAUUKRX9umVCRhqEcku4Zaig6PylVd9LZFLjZvGKA4Vq2DkYRtClll3z5FIhAOSY3SQ==", - "dependencies": { - "@angular-devkit/core": "12.0.2", - "@angular-devkit/schematics": "12.0.2", - "jsonc-parser": "3.0.0" - }, - "engines": { - "node": "^12.14.1 || ^14.0.0", - "npm": "^6.11.0 || ^7.5.6", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@sentry/angular": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-6.2.3.tgz", - "integrity": "sha512-tP+d4bgf2ZpP7ZOzenf2wNsyX9MiIv8K2aQRXvvYk9eeKH1bwhFFOkmmz5eFsEpdDClpQXdnBx18tCL054gBjg==", - "dependencies": { - "@sentry/browser": "6.2.3", - "@sentry/types": "6.2.3", - "@sentry/utils": "6.2.3", - "rxjs": "^6.6.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/angular/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/browser": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.2.3.tgz", - "integrity": "sha512-QUqrZdAosY2MPAUfJYpyCT+dA6v7A2h8imO8R3Lbi0hRSPr+L7zjqHgFs3CTHJLmLV74cxHt6rVVUPSksYNQDQ==", - "dependencies": { - "@sentry/core": "6.2.3", - "@sentry/types": "6.2.3", - "@sentry/utils": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/browser/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/core": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.2.3.tgz", - "integrity": "sha512-GpfHoSJiXchVXgyaMWVtIPVw2t97KkD1OJ4JdL3/TeH3auX5XvsN5iHTk+x/Er8t13IpOnvidH1xWdV1dnax2w==", - "dependencies": { - "@sentry/hub": "6.2.3", - "@sentry/minimal": "6.2.3", - "@sentry/types": "6.2.3", - "@sentry/utils": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/hub": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.2.3.tgz", - "integrity": "sha512-D5Horfo2l0p52S7KPvy7qwWNMrE4IsCN8ODbfcCsfJu7hEXJmItbkbohIVSqO5neukhn5nu+x8kyCe9Q5u1Q6g==", - "dependencies": { - "@sentry/types": "6.2.3", - "@sentry/utils": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/minimal": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.2.3.tgz", - "integrity": "sha512-Gpn9x4NQAG7E94EK1+hAz9GUcYrffTuqJ/XgqvHYk0jsHZ6RfsXYrmBac0ZwUxOivMf2t0n5opK0v5rhMDfF2w==", - "dependencies": { - "@sentry/hub": "6.2.3", - "@sentry/types": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/tracing": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.2.3.tgz", - "integrity": "sha512-OnQZKp7qVera+Z4ly6hgybGgyf10p2VDXqwueXkMVeLD+PwlPG8a8NMpKkZ+QxwRbQbSFhRLQaib3NX34tusBQ==", - "dependencies": { - "@sentry/hub": "6.2.3", - "@sentry/minimal": "6.2.3", - "@sentry/types": "6.2.3", - "@sentry/utils": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/tracing/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@sentry/types": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.2.3.tgz", - "integrity": "sha512-BpA+9FherWgYlkMD/82bGFh/gAqZNlZX5UE8vWLKyyzNyOEEz3v9ScxE8dOSWE4v5iXJR1O3jjxaTcRQxPVgCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.2.3.tgz", - "integrity": "sha512-YnkJm97wSvck39eRpqWjIuuwbvzPilvAcMqhbUy9yK/UBQMDGUzAKCOKH40udw1DwMUCWjJ71mOCDgUorE4Fog==", - "dependencies": { - "@sentry/types": "6.2.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", - "integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/chart.js": { - "version": "2.9.20", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.20.tgz", - "integrity": "sha512-Xv4dd+DYqtTdUWbDIwaDEFtUIFwoQN44wiDDrWXdJtfGtOFlFIxXrsu8D+XJCS9o7mZbW29X8vPptwVrduz4JA==", - "dependencies": { - "moment": "^2.10.2" - } - }, - "node_modules/@types/codemirror": { - "version": "0.0.87", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.87.tgz", - "integrity": "sha512-Yv+cw1zckMDK35QJBMMK/z9ZWUHRUpQ2KJI+MCbR95HhDWtSQsS66j/W9OMq3JUqYL7Jb2zHA7fc575/0v1sfA==", - "dependencies": { - "@types/tern": "*" - } - }, - "node_modules/@types/component-emitter": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", - "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", - "devOptional": true - }, - "node_modules/@types/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", - "devOptional": true - }, - "node_modules/@types/cors": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", - "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", - "devOptional": true - }, - "node_modules/@types/dragula": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/dragula/-/dragula-3.7.0.tgz", - "integrity": "sha512-Scr3lQ7pDmwic+I4qrzDEIfPVGUhc/qo8S0VJJ9v5pzTyIIJzAXrnFajjsMSL8J84VERIkZUh7wH6wYEisY+TA==" - }, - "node_modules/@types/eslint": { - "version": "7.2.12", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.12.tgz", - "integrity": "sha512-HjikV/jX6e0Pg4DcB+rtOBKSrG6w5IaxWpmi3efL/eLxMz5lZTK+W1DKERrX5a+mNzL78axfsDNXu7JHFP4uLg==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", - "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", - "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" - }, - "node_modules/@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/hammerjs": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", - "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" - }, - "node_modules/@types/jasmine": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.4.tgz", - "integrity": "sha512-CTdMERA4iGNcxeqzD7pavb4WLIFq6bGnx6nIJD+1D4Knx24GE6QBPrWVhO8UlIy7gf7rbIt3ZD7iIzryRD2TgA==", - "dev": true - }, - "node_modules/@types/jquery": { - "version": "3.3.33", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.33.tgz", - "integrity": "sha512-U6IdXYGkfUI42SR79vB2Spj+h1Ly3J3UZjpd8mi943lh126TK7CB+HZOxGh2nM3IySor7wqVQdemD/xtydsBKA==", - "dependencies": { - "@types/sizzle": "*" - } - }, - "node_modules/@types/jqueryui": { - "version": "1.12.10", - "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.10.tgz", - "integrity": "sha512-T8sctslWIiLl/2EHEQQfKCB92S9bMKBaeE3+iBRbSERMK/1gzyfqjaIEksduB4eUEsKq+Ji0Y+qVbiXQwI2Mwg==", - "dependencies": { - "@types/jquery": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "node_modules/@types/lodash": { - "version": "4.14.149", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" - }, - "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" - }, - "node_modules/@types/moment-timezone": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/@types/moment-timezone/-/moment-timezone-0.5.12.tgz", - "integrity": "sha512-hnHH2+Efg2vExr/dSz+IX860nSiyk9Sk4pJF2EmS11lRpMcNXeB4KBW5xcgw2QPsb9amTXdsVNEe5IoJXiT0uw==", - "dependencies": { - "moment": ">=2.14.0" - } - }, - "node_modules/@types/mousetrap": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.3.tgz", - "integrity": "sha512-13gmo3M2qVvjQrWNseqM3+cR6S2Ss3grbR2NZltgMq94wOwqJYQdgn8qzwDshzgXqMlSUtyPZjysImmktu22ew==" - }, - "node_modules/@types/node": { - "version": "12.20.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.4.tgz", - "integrity": "sha512-xRCgeE0Q4pT5UZ189TJ3SpYuX/QGl6QIAOAIeDSbAVAd2gX1NxSZup4jNVK7cxIeP8KDSbJgcckun495isP1jQ==" - }, - "node_modules/@types/pako": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pako/-/pako-1.0.1.tgz", - "integrity": "sha512-GdZbRSJ3Cv5fiwT6I0SQ3ckeN2PWNqxd26W9Z2fCK1tGrrasGy4puvNFtnddqH9UJFMQYXxEuuB7B8UK+LLwSg==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/resize-observer-browser": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.4.tgz", - "integrity": "sha512-rPvqs+1hL/5hbES/0HTdUu4lvNmneiwKwccbWe7HGLWbnsLdqKnQHyWLg4Pj0AMO7PLHCwBM1Cs8orChdkDONg==" - }, - "node_modules/@types/sizzle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", - "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==" - }, - "node_modules/@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==" - }, - "node_modules/@types/tern": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz", - "integrity": "sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/urijs": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.6.tgz", - "integrity": "sha512-kdnK+JtEiUgnpB7r99SAZjjz9nhZ/7MWo/hxTSNfvslAa4r8jpDXDEJ2cQrjemes4eX2Y5Om3udmcc8QalPzOA==" - }, - "node_modules/@types/webpack-env": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.0.tgz", - "integrity": "sha512-Fx+NpfOO0CpeYX2g9bkvX8O5qh9wrU1sOF4g8sft4Mu7z+qfe387YlyY8w8daDyDsKY5vUxM0yxkAYnbkRbZEw==" - }, - "node_modules/@types/webpack-sources": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz", - "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==", - "dependencies": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - } - }, - "node_modules/@types/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz", - "integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.23.0", - "@typescript-eslint/scope-manager": "4.23.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "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/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "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.20.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.20.0.tgz", - "integrity": "sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz", - "integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.23.0", - "@typescript-eslint/types": "4.23.0", - "@typescript-eslint/typescript-estree": "4.23.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.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/parser": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz", - "integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "4.23.0", - "@typescript-eslint/types": "4.23.0", - "@typescript-eslint/typescript-estree": "4.23.0", - "debug": "^4.1.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/parser/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz", - "integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.23.0", - "@typescript-eslint/visitor-keys": "4.23.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.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz", - "integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw==", - "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.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz", - "integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.23.0", - "@typescript-eslint/visitor-keys": "4.23.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "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/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": 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.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz", - "integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.23.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/@uirouter/angular": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@uirouter/angular/-/angular-8.0.0.tgz", - "integrity": "sha512-ti7U7AUrdxwcL8BKJAYG1rOMjOe3dvZRGGzf3vEgjrE/Hw8AimR2OjMOYJwZH6IAcuwXCT5lolwQY8TJU40mBA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@uirouter/core": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.7.tgz", - "integrity": "sha512-KUTJxL+6q0PiBnFx4/Z+Hsyg0pSGiaW5yZQeJmUxknecjpTbnXkLU8H2EqRn9N2B+qDRa7Jg8RcgeNDPY72O1w==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@uirouter/rx": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@uirouter/rx/-/rx-0.6.5.tgz", - "integrity": "sha512-srGdX+BAVTfpZ1Kl7tvdEN1ss8yctKO4YrGyy9E/4s80bGFp4KMe9Qf2L3UHn0iugBZlX7Nbmp305AQSzWXYrw==" - }, - "node_modules/@w11k/ngx-componentdestroyed": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@w11k/ngx-componentdestroyed/-/ngx-componentdestroyed-5.0.2.tgz", - "integrity": "sha512-njpK7h6hSpF8LQp2bayb47T7rTxOwx7745TOiUP88y8YAT2JOY5OeJpYqlK2WrQQBeD7CT+DxozD6yNBN283dA==", - "dependencies": { - "rxjs": ">=6.5.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", - "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", - "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", - "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", - "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", - "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", - "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", - "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", - "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", - "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", - "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/helper-wasm-section": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-opt": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "@webassemblyjs/wast-printer": "1.11.0" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", - "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", - "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", - "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", - "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xeokit/xeokit-bim-viewer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@xeokit/xeokit-bim-viewer/-/xeokit-bim-viewer-2.2.0.tgz", - "integrity": "sha512-Xqd5V8aqnHUDHHLR9zS+gcg9L2iMFMJ6OOKQkFk90Zc7g4FaDmYjZCZODUPIsQLCOqnnGRgNqpak921aLXiocw==", - "dependencies": { - "@xeokit/xeokit-sdk": "1.9.0" - } - }, - "node_modules/@xeokit/xeokit-sdk": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@xeokit/xeokit-sdk/-/xeokit-sdk-1.9.0.tgz", - "integrity": "sha512-YrYqCkZhABpT7c5TFeT4ZuoiEpnaML3+BfrKoifhOTdf4qFeMd4Z0zGwhay9cDaZ405t7K02Jr/XVXnNU7qrOA==" - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" - }, - "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/agentkeepalive": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz", - "integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==", - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/agentkeepalive/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agentkeepalive/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/ajv-errors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz", - "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - }, - "node_modules/animation-frame-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/animation-frame-polyfill/-/animation-frame-polyfill-1.0.1.tgz", - "integrity": "sha1-X1rZk6eHlL0Xas3lttzmKGdBDJ0=" - }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/app-root-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", - "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "node_modules/are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "node_modules/arg": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", - "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "devOptional": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "dependencies": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=" - }, - "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" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "engines": { - "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" - } - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/atoa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", - "integrity": "sha1-DMDpGkgOc4+SPrwQNnZHF3mzSkk=" - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/autoprefixer": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz", - "integrity": "sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw==", - "dependencies": { - "browserslist": "^4.6.3", - "caniuse-lite": "^1.0.30000980", - "chalk": "^2.4.2", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.17", - "postcss-value-parser": "^4.0.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/autoprefixer/node_modules/postcss": { - "version": "7.0.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.17.tgz", - "integrity": "sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/autoprefixer/node_modules/postcss/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/autoprefixer/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "node_modules/axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", - "dev": true, - "dependencies": { - "ast-types-flow": "0.0.7" - } - }, - "node_modules/babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - } - }, - "node_modules/babel-loader/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", - "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", - "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.2", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.2.tgz", - "integrity": "sha512-l1Cf8PKk12eEk5QP/NQ6TH8A1pee6wWDJ96WjxrMXFLHLOBFzYM4moG80HFgduVhTqAFez4alnZKEhP/bYHg0A==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.2", - "core-js-compat": "^3.9.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", - "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "devOptional": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "devOptional": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" - }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" - }, - "node_modules/byte-base64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/byte-base64/-/byte-base64-1.1.0.tgz", - "integrity": "sha512-56cXelkJrVMdCY9V/3RfDxTh4VfMFCQ5km7B7GkIGfo4bcPL9aACyJLB0Ms3Ezu5rsHmLB2suis96z4fLM03DA==" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "15.0.6", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz", - "integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==", - "dependencies": { - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001230", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", - "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/canonical-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==" - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "node_modules/cdk-drag-scroll": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/cdk-drag-scroll/-/cdk-drag-scroll-0.0.6.tgz", - "integrity": "sha512-yPQ7sCOuo+2QmidAiXjaGsHMu7a0hK8D03DU9vsc/K6IMt6qyG93EVE8WzEO+AQqocJcltfyZ6BSg69llcRJyg==", - "dependencies": { - "tslib": "^1.9.0" - } - }, - "node_modules/cdk-drag-scroll/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "node_modules/chart.js": { - "version": "2.9.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz", - "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==", - "dependencies": { - "chartjs-color": "^2.1.0", - "moment": "^2.10.2" - } - }, - "node_modules/chartjs-color": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", - "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", - "dependencies": { - "chartjs-color-string": "^0.6.0", - "color-convert": "^1.9.3" - } - }, - "node_modules/chartjs-color-string": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", - "dependencies": { - "color-name": "^1.0.0" - } - }, - "node_modules/chartjs-plugin-datalabels": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-0.6.0.tgz", - "integrity": "sha512-3APqqeqVHlxjusTO0hETfNjq9HWCISLkjxQfAKgbnTfjZe60knOzVxDAgLGnpRvnzQvZUtwSlnJX2MHIuIUe8w==" - }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", - "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/codelyzer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.0.tgz", - "integrity": "sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA==", - "dev": true, - "dependencies": { - "@angular/compiler": "9.0.0", - "@angular/core": "9.0.0", - "app-root-path": "^3.0.0", - "aria-query": "^3.0.0", - "axobject-query": "2.0.2", - "css-selector-tokenizer": "^0.7.1", - "cssauron": "^1.4.0", - "damerau-levenshtein": "^1.0.4", - "rxjs": "^6.5.3", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.2", - "tslib": "^1.10.0", - "zone.js": "~0.10.3" - } - }, - "node_modules/codelyzer/node_modules/@angular/compiler": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz", - "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==", - "dev": true - }, - "node_modules/codelyzer/node_modules/@angular/core": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz", - "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", - "dev": true - }, - "node_modules/codelyzer/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/codelyzer/node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "node_modules/codelyzer/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, - "node_modules/codelyzer/node_modules/zone.js": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", - "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==", - "dev": true - }, - "node_modules/codemirror": { - "version": "5.48.4", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.48.4.tgz", - "integrity": "sha512-pUhZXDQ6qXSpWdwlgAwHEkd4imA0kf83hINmUEzJpmG80T/XLtDDEzZo8f6PQLuRCcUQhmzqqIo3ZPTRaWByRA==" - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/colord": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.0.1.tgz", - "integrity": "sha512-vm5YpaWamD0Ov6TSG0GGmUIwstrWcfKQV/h2CmbR7PbNu41+qdB5PW9lpzhjedrpm08uuYvcXi0Oel1RLZIJuA==" - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "devOptional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", - "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", - "dev": true - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "devOptional": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/contra": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", - "integrity": "sha1-9TveQtfltZhcrk2ZqNYQUm3o8o0=", - "dependencies": { - "atoa": "1.0.0", - "ticky": "1.0.1" - } - }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "devOptional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/copy-anything": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.3.tgz", - "integrity": "sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ==", - "dependencies": { - "is-what": "^3.12.0" - } - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==", - "engines": { - "node": ">=12" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz", - "integrity": "sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==", - "dependencies": { - "fast-glob": "^3.2.5", - "glob-parent": "^5.1.1", - "globby": "^11.0.3", - "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/core-js": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.0.tgz", - "integrity": "sha512-SaMnchL//WwU2Ot1hhkPflE8gzo7uq1FGvUJ8GKmi3TOU7rGTHIU+eir1WGf6qOtTyxdfdcp10yPdGZ59sQ3hw==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.13.1.tgz", - "integrity": "sha512-mdrcxc0WznfRd8ZicEZh1qVeJ2mu6bwQFh8YVUK48friy/FOwFV5EJj9/dlh+nMQ74YusdVfBFDuomKgUspxWQ==", - "dependencies": { - "browserslist": "^4.16.6", - "semver": "7.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/core-js-pure": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz", - "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "devOptional": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/create-point-cb": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-point-cb/-/create-point-cb-1.2.0.tgz", - "integrity": "sha1-G85H/E/AGFXuEhONZ2sMsqfLznE=", - "dependencies": { - "type-func": "^1.0.1" - } - }, - "node_modules/critters": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.10.tgz", - "integrity": "sha512-p5VKhP1803+f+0Jq5P03w1SbiHtpAKm+1EpJHkiPxQPq0Vu9QLZHviJ02GRrWi0dlcJqrmzMWInbwp4d22RsGw==", - "dependencies": { - "chalk": "^4.1.0", - "css": "^3.0.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "pretty-bytes": "^5.3.0" - } - }, - "node_modules/critters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/critters/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/critters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/critters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/critters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/critters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/crossvent": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz", - "integrity": "sha1-rSCHjkkh6b5z2daXb4suzQ9xoLE=", - "dependencies": { - "custom-event": "^1.0.0" - } - }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, - "node_modules/css-blank-pseudo": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", - "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", - "dependencies": { - "postcss": "^7.0.5" - }, - "bin": { - "css-blank-pseudo": "cli.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/css-blank-pseudo/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/css-blank-pseudo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-blank-pseudo/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/css-color-names": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", - "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", - "engines": { - "node": "*" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.0.3.tgz", - "integrity": "sha512-52P95mvW1SMzuRZegvpluT6yEv0FqQusydKQPZsNN5Q7hh8EwQvN8E2nwuJ16BBvNN6LcoIZXu/Bk58DAhrrxw==", - "dependencies": { - "timsort": "^0.3.0" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", - "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", - "dependencies": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^5.0.0-rc.4" - }, - "bin": { - "css-has-pseudo": "cli.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/css-has-pseudo/node_modules/cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-has-pseudo/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dependencies": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-has-pseudo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-has-pseudo/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/css-loader": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz", - "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==", - "dependencies": { - "camelcase": "^6.2.0", - "icss-utils": "^5.1.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.10", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.27.0 || ^5.0.0" - } - }, - "node_modules/css-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-yIrqG0pPphR1RoNx2wDxYmxRf2ubRChLDXxv7ccipEm5bRKsZRYp8n+2peeXehtTF5s3yNxlqsdz3WQOsAgUkw==", - "dependencies": { - "cssnano": "^5.0.0", - "jest-worker": "^26.3.0", - "p-limit": "^3.0.2", - "postcss": "^8.2.9", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "dependencies": { - "css": "^2.0.0" - } - }, - "node_modules/css-parse/node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-parse/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-parse/node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", - "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", - "dependencies": { - "postcss": "^7.0.5" - }, - "bin": { - "css-prefers-color-scheme": "cli.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/css-prefers-color-scheme/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/css-prefers-color-scheme/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-prefers-color-scheme/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/css-select": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", - "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^4.0.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.3", - "nth-check": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-what": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", - "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "dependencies": { - "through": "X.X.X" - } - }, - "node_modules/cssdb": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", - "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.5.tgz", - "integrity": "sha512-L2VtPXnq6rmcMC9vkBOP131sZu3ccRQI27ejKZdmQiPDpUlFkUbpXHgKN+cibeO1U4PItxVZp1zTIn5dHsXoyg==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "cssnano-preset-default": "^5.1.2", - "is-resolvable": "^1.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.2.tgz", - "integrity": "sha512-spilp8LRw0sacuxiN9A/dyyPr6G/WISKMBKcBD4NMoPV0ENx4DeuWvIIrSx9PII2nJIDCO3kywkqTPreECBVOg==", - "dependencies": { - "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^2.0.1", - "postcss-calc": "^8.0.0", - "postcss-colormin": "^5.2.0", - "postcss-convert-values": "^5.0.1", - "postcss-discard-comments": "^5.0.1", - "postcss-discard-duplicates": "^5.0.1", - "postcss-discard-empty": "^5.0.1", - "postcss-discard-overridden": "^5.0.1", - "postcss-merge-longhand": "^5.0.2", - "postcss-merge-rules": "^5.0.2", - "postcss-minify-font-values": "^5.0.1", - "postcss-minify-gradients": "^5.0.1", - "postcss-minify-params": "^5.0.1", - "postcss-minify-selectors": "^5.1.0", - "postcss-normalize-charset": "^5.0.1", - "postcss-normalize-display-values": "^5.0.1", - "postcss-normalize-positions": "^5.0.1", - "postcss-normalize-repeat-style": "^5.0.1", - "postcss-normalize-string": "^5.0.1", - "postcss-normalize-timing-functions": "^5.0.1", - "postcss-normalize-unicode": "^5.0.1", - "postcss-normalize-url": "^5.0.1", - "postcss-normalize-whitespace": "^5.0.1", - "postcss-ordered-values": "^5.0.1", - "postcss-reduce-initial": "^5.0.1", - "postcss-reduce-transforms": "^5.0.1", - "postcss-svgo": "^5.0.2", - "postcss-unique-selectors": "^5.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", - "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" - }, - "node_modules/d": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", - "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", - "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", - "devOptional": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "node_modules/default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dependencies": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dependencies": { - "object-keys": "^1.0.12" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/del/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "devOptional": true - }, - "node_modules/diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "devOptional": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" - }, - "node_modules/dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dependencies": { - "buffer-indexof": "^1.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-autoscroller": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/dom-autoscroller/-/dom-autoscroller-2.3.4.tgz", - "integrity": "sha512-HcAdt/2Dq9x4CG6LWXc2x9Iq0MJPAu8fuzHncclq7byufqYEYVtx9sZ/dyzR+gdj4qwEC9p27Lw1G2HRRYX6jQ==", - "dependencies": { - "animation-frame-polyfill": "^1.0.0", - "create-point-cb": "^1.0.0", - "dom-mousemove-dispatcher": "^1.0.1", - "dom-plane": "^1.0.1", - "dom-set": "^1.0.1", - "type-func": "^1.0.1" - } - }, - "node_modules/dom-mousemove-dispatcher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dom-mousemove-dispatcher/-/dom-mousemove-dispatcher-1.0.1.tgz", - "integrity": "sha1-okpt35Oye7NpT3IIdUalf8fpFA8=" - }, - "node_modules/dom-plane": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dom-plane/-/dom-plane-1.0.2.tgz", - "integrity": "sha1-+MheaXxYfxR+j8L6wd4HjB/kFyw=", - "dependencies": { - "create-point-cb": "^1.0.0" - } - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "devOptional": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-set": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/dom-set/-/dom-set-1.1.1.tgz", - "integrity": "sha1-XCxhDuSDm1IO1fmN28vjFMD6lUo=", - "dependencies": { - "array-from": "^2.1.1", - "is-array": "^1.0.1", - "iselement": "^1.1.4" - } - }, - "node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", - "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dragula": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.2.tgz", - "integrity": "sha1-SjXJ05gf+sGpScKcpyhQWOhzk84=", - "dependencies": { - "contra": "1.9.4", - "crossvent": "1.5.4" - } - }, - "node_modules/dragula/node_modules/crossvent": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", - "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=", - "dependencies": { - "custom-event": "1.0.0" - } - }, - "node_modules/dragula/node_modules/custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/electron-to-chromium": { - "version": "1.3.742", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz", - "integrity": "sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", - "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", - "devOptional": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~4.0.0", - "ws": "~7.4.2" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", - "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", - "devOptional": true, - "dependencies": { - "base64-arraybuffer": "0.1.4" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/enhanced-resolve": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", - "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "devOptional": true - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==" - }, - "node_modules/errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.18.0-next.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", - "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.1", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.3", - "string.prototype.trimstart": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", - "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==" - }, - "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" - } - }, - "node_modules/es5-ext": { - "version": "0.10.46", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", - "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", - "dependencies": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-iterator/node_modules/d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dependencies": { - "es5-ext": "^0.10.9" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/es6-symbol/node_modules/d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dependencies": { - "es5-ext": "^0.10.9" - } - }, - "node_modules/es6-weak-map": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", - "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", - "dependencies": { - "d": "~0.1.1", - "es5-ext": "~0.10.6", - "es6-iterator": "~0.1.3", - "es6-symbol": "~2.0.1" - } - }, - "node_modules/es6-weak-map/node_modules/es6-iterator": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", - "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", - "dependencies": { - "d": "~0.1.1", - "es5-ext": "~0.10.5", - "es6-symbol": "~2.0.1" - } - }, - "node_modules/es6-weak-map/node_modules/es6-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", - "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", - "dependencies": { - "d": "~0.1.1", - "es5-ext": "~0.10.5" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", - "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", - "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", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", - "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", - "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "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-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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/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.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "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.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/eslint/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "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": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "devOptional": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/event-emitter/node_modules/d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dependencies": { - "es5-ext": "^0.10.9" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dependencies": { - "original": "^1.0.0" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expose-loader": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-0.7.5.tgz", - "integrity": "sha512-iPowgKUZkTPX5PznYsmifVj9Bob0w2wTHVkt/eYNPSzyebkUgIedmskf/kcfEIWpiWjg3JRjnW+a17XypySMuw==", - "engines": { - "node": ">= 4.3 < 5.0.0 || >= 5.10" - } - }, - "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dependencies": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/express/node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/flatpickr": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.3.tgz", - "integrity": "sha512-007VucCkqNOMMb9ggRLNuJowwaJcyOh4sKAFcdGfahfGc7JQbf94zSzjdBq/wVyHWUEs5o3+idhFZ0wbZMRmVQ==" - }, - "node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "devOptional": true - }, - "node_modules/flatten": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", - "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==" - }, - "node_modules/follow-redirects": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.2.tgz", - "integrity": "sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "devOptional": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/fuse.js": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.5.tgz", - "integrity": "sha512-s9PGTaQIkT69HaeoTVjwGsLfb8V8ScJLx5XGFcKHg0MqLUH/UZ4EKOtqtXX9k7AFqCGxD1aJmYb8Q5VYDibVRQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "node_modules/hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, - "node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, - "node_modules/html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" - }, - "node_modules/http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dependencies": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/ical.js": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ical.js/-/ical.js-1.4.0.tgz", - "integrity": "sha512-ltHZuOFNNjcyEYbzDgjemS7LWIFh2vydJeznxQHUh3dnarbxqOYsWONYteBVAq1MEOHnwXFGN2eskZReHclnrA==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", - "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/inquirer": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", - "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.6", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dependencies": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "engines": { - "node": ">=4" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "dependencies": { - "call-bind": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz", - "integrity": "sha1-6YUMwsyGDDvAl36EzPDdRkWEJ5o=" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==" - }, - "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" - } - }, - "node_modules/is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dependencies": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "node_modules/is-color-stop/node_modules/css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "engines": { - "node": "*" - } - }, - "node_modules/is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=" - }, - "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" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isbinaryfile": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", - "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "devOptional": true, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/iselement": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iselement/-/iselement-1.1.4.tgz", - "integrity": "sha1-flW1Ko68pQp+LoDluNKEDzI1MUY=" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "node_modules/jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", - "dev": true, - "dependencies": { - "colors": "1.4.0" - } - }, - "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jquery": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", - "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" - }, - "node_modules/jquery-ui": { - "resolved": "git+ssh://git@github.com/jquery/jquery-ui.git#74f8a0ac952f6f45f773312292baef1c26d81300", - "dependencies": { - "jquery": ">=1.7.0 <4.0.0" - } - }, - "node_modules/jquery-ujs": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jquery-ujs/-/jquery-ujs-1.2.2.tgz", - "integrity": "sha1-ao7xAg5rbdo4W5CkvdwSjCHFY5c=", - "dependencies": { - "jquery": ">=1.8.0" - } - }, - "node_modules/jquery.caret": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/jquery.caret/-/jquery.caret-0.3.1.tgz", - "integrity": "sha1-nAkzGPrzJ+/zIugmyp8yQTaLx7g=" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "devOptional": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "node_modules/json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" - }, - "node_modules/json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "devOptional": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/karma": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.2.tgz", - "integrity": "sha512-fo4Wt0S99/8vylZMxNj4cBFyOBBnC1bewZ0QOlePij/2SZVWxqbyLeIddY13q6URa2EpLRW8ixvFRUMjkmo1bw==", - "devOptional": true, - "dependencies": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.4.2", - "colors": "^1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.6", - "lodash": "^4.17.19", - "log4js": "^6.2.1", - "mime": "^2.4.5", - "minimatch": "^3.0.4", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^3.1.0", - "source-map": "^0.6.1", - "tmp": "0.2.1", - "ua-parser-js": "^0.7.23", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz", - "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.1", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", - "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - } - }, - "node_modules/karma-coverage/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/karma-coverage/node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/karma-coverage/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/karma-coverage/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma-jasmine": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.3.1.tgz", - "integrity": "sha512-Nxh7eX9mOQMyK0VSsMxdod+bcqrR/ikrmEiWj5M6fwuQ7oI+YEF1FckaDsWfs6TIpULm9f0fTKMjF7XcrvWyqQ==", - "dev": true, - "dependencies": { - "jasmine-core": "^3.5.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/karma-jasmine-html-reporter": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.2.tgz", - "integrity": "sha512-ILBPsXqQ3eomq+oaQsM311/jxsypw5/d0LnZXj26XkfThwq7jZ55A2CFSKJVA5VekbbOGvMyv7d3juZj0SeTxA==", - "dev": true - }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dependencies": { - "source-map-support": "^0.5.5" - } - }, - "node_modules/karma-spec-reporter": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", - "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", - "dev": true, - "dependencies": { - "colors": "^1.1.2" - }, - "peerDependencies": { - "karma": ">=0.9" - } - }, - "node_modules/karma/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "devOptional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/karma/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/karma/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true - }, - "node_modules/karma/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "devOptional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/karma/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "devOptional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/karma/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "devOptional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "devOptional": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.7", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", - "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "devOptional": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/less": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", - "integrity": "sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw==", - "dependencies": { - "copy-anything": "^2.0.1", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "parse-node-version": "^1.0.1", - "tslib": "^1.10.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "image-size": "~0.5.0", - "needle": "^2.5.2", - "source-map": "~0.6.0" - } - }, - "node_modules/less-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-8.1.1.tgz", - "integrity": "sha512-K93jJU7fi3n6rxVvzp8Cb88Uy9tcQKfHlkoezHwKILXhlNYiRQl4yowLIkQqmBXOH/5I8yoKiYeIf781HGkW9g==", - "dependencies": { - "klona": "^2.0.4" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/less/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/license-webpack-plugin": { - "version": "2.3.17", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.17.tgz", - "integrity": "sha512-4jJ5/oIkhylMw2EjXh9sxPP8KC3FYBjTcxOCoTIaC2J/zVbJhfw992UEpSsov8VTt97XtU+xJyE4cJn4gHB2PA==", - "dependencies": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", - "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" - }, - "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", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log4js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", - "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", - "devOptional": true, - "dependencies": { - "date-format": "^3.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.1", - "rfdc": "^1.1.4", - "streamroller": "^2.2.4" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/log4js/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/log4js/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "node_modules/make-fetch-happen": { - "version": "8.0.14", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", - "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.0.5", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^5.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U=" - }, - "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mem": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz", - "integrity": "sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==", - "dependencies": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/mem?sponsor=1" - } - }, - "node_modules/mem/node_modules/mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/memfs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.2.tgz", - "integrity": "sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==", - "dependencies": { - "fs-monkey": "1.0.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memoizee": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz", - "integrity": "sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8=", - "dependencies": { - "d": "~0.1.1", - "es5-ext": "~0.10.11", - "es6-weak-map": "~0.1.4", - "event-emitter": "~0.3.4", - "lru-queue": "0.1", - "next-tick": "~0.2.2", - "timers-ext": "0.1" - } - }, - "node_modules/memoizee/node_modules/next-tick": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", - "integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0=" - }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", - "dependencies": { - "mime-db": "1.46.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.5.1.tgz", - "integrity": "sha512-wEpr0XooH6rw/Mlf+9KTJoMBLT3HujzdTrmohPjAzF47N4Q6yAeczQLpRD/WxvAtXvskcXbily7TAdCfi2M4Dg==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "webpack-sources": "^1.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.3.3.tgz", - "integrity": "sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ==", - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.26.tgz", - "integrity": "sha512-sFP4cgEKTCymBBKgoxZjYzlSovC20Y6J7y3nanDc5RoBIXKlZhoYwBoZGe3flwU6A372AcRwScH8KiwV6zjy1g==", - "dependencies": { - "moment": ">= 2.9.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mousetrap": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.3.tgz", - "integrity": "sha512-bd+nzwhhs9ifsUrC2tWaSgm24/oo2c83zaRyZQF06hYA6sANfsXHtnZ19AbbbDXCDzeH5nZBSQ4NvCjgD62tJA==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dependencies": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/needle": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", - "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", - "optional": true, - "dependencies": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/needle/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "optional": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/needle/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "optional": true - }, - "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, - "node_modules/ng-dynamic-component": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ng-dynamic-component/-/ng-dynamic-component-6.0.0.tgz", - "integrity": "sha512-soZdXYd7oWivBuJOEpyTos5Mkp0/y3Dyjl1JPahSnevdozDgY/dqBkPHzkSllCbOQaCxllFjqYy+B0rOQaOolA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/ng2-charts": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-2.3.1.tgz", - "integrity": "sha512-YSBlvLQK45wHTIzdLHcnsFr7+rgr4K9UrWfx3aK8xGhpOWI4UKC2/rpKRyJBGV5KHe4oO8mQjh0R09HMywINMQ==", - "dependencies": { - "@types/chart.js": "^2.7.48", - "lodash-es": "^4.17.11", - "tslib": "^1.9.0" - } - }, - "node_modules/ng2-charts/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - }, - "node_modules/ng2-dragula": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ng2-dragula/-/ng2-dragula-2.1.1.tgz", - "integrity": "sha512-PSo6N2Ja894KDogVLLBI0Hzpylikay7L1hWqp+qQmW+qsNsNT9J/6J2Qim9XwGzK4VQZjAwBJaJjgJ/TijRkLQ==", - "dependencies": { - "@types/dragula": "^2.1.34", - "dragula": "^3.7.2" - } - }, - "node_modules/ng2-dragula/node_modules/@types/dragula": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/@types/dragula/-/dragula-2.1.35.tgz", - "integrity": "sha512-kh0O22j8QVh7xU0P6Penwext6xVMNldVEzvtOAHU1/AzdluYstc9pe2nUL3fS6IZRyzSoSGvdR/u8zP2iIZmnA==" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/node-gyp": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", - "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "request": "^2.88.2", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "tar": "^6.0.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "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==", - "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.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-install-checks/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "node_modules/npm-package-arg": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.2.tgz", - "integrity": "sha512-6Eem455JsSMJY6Kpd3EyWE+n5hC+g9bSyHr9K9U2zqZb7+02+hObQ2c0+8iDk/mNF+8r1MhY44WypKJAkySIYA==", - "dependencies": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-package-arg/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-packlist": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz", - "integrity": "sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg==", - "dependencies": { - "glob": "^7.1.6", - "ignore-walk": "^3.0.3", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-pick-manifest": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", - "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", - "dependencies": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" - } - }, - "node_modules/npm-pick-manifest/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-registry-client": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz", - "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", - "dependencies": { - "concat-stream": "^1.5.2", - "graceful-fs": "^4.1.6", - "normalize-package-data": "~1.0.1 || ^2.0.0", - "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", - "npmlog": "2 || ^3.1.0 || ^4.0.0", - "once": "^1.3.3", - "request": "^2.74.0", - "retry": "^0.10.0", - "safe-buffer": "^5.1.1", - "semver": "2 >=2.2.1 || 3.x || 4 || 5", - "slide": "^1.1.3", - "ssri": "^5.2.4" - } - }, - "node_modules/npm-registry-client/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==" - }, - "node_modules/npm-registry-client/node_modules/npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "dependencies": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "node_modules/npm-registry-client/node_modules/retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "engines": { - "node": "*" - } - }, - "node_modules/npm-registry-client/node_modules/ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dependencies": { - "safe-buffer": "^5.1.1" - } - }, - "node_modules/npm-registry-fetch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-10.1.2.tgz", - "integrity": "sha512-KsM/TdPmntqgBFlfsbkOLkkE9ovZo7VpVcd+/eTdYszCrgy5zFl5JzWm+OxavFaEWlbkirpkou+ZYI00RmOBFA==", - "dependencies": { - "lru-cache": "^6.0.0", - "make-fetch-happen": "^8.0.9", - "minipass": "^3.1.3", - "minipass-fetch": "^1.3.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.0.0", - "npm-package-arg": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/observable-array": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/observable-array/-/observable-array-0.0.4.tgz", - "integrity": "sha1-Y4qFmEyZ1YTw8RM8o5q4gpoTsIU=", - "dependencies": { - "d": "^0.1.1", - "es5-ext": "^0.10.8", - "event-emitter": "^0.3.4", - "memoizee": "^0.3.9", - "observable-value": "0.0.5" - } - }, - "node_modules/observable-value": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/observable-value/-/observable-value-0.0.5.tgz", - "integrity": "sha1-r2Wo2g+rm3JIjWiwLiDvP3629Ps=", - "dependencies": { - "d": "^0.1.1", - "es5-ext": "^0.10.8", - "es6-symbol": "3", - "event-emitter": "^0.3.4", - "memoizee": "^0.3.9" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.0.2.tgz", - "integrity": "sha512-NV5QmWJrTaNBLHABJyrb+nd5dXI5zfea/suWawBhkHzAbVhLLiJdrqMgxMypGK9Eznp2Ltoh7SAVkQ3XAucX7Q==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open/node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz", - "integrity": "sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dependencies": { - "url-parse": "^1.4.3" - } - }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dependencies": { - "retry": "^0.12.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pacote": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.3.2.tgz", - "integrity": "sha512-lMO7V9aMhyE5gfaSFxKfW3OTdXuFBNQJfuNuet3NPzWWhOYIW90t85vHcHLDjdhgmfAdAHyh9q1HAap96ea0XA==", - "dependencies": { - "@npmcli/git": "^2.0.1", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^1.8.2", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^2.1.4", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^10.0.0", - "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pacote/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pako": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.3.tgz", - "integrity": "sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module/node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", - "dependencies": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, - "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=", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "node_modules/picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/portfinder/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", - "integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==", - "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map-js": "^0.6.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", - "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-selector-parser": "^6.0.2" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-attribute-case-insensitive/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-calc": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", - "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", - "dependencies": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", - "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-color-functional-notation/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-color-functional-notation/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-color-functional-notation/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-color-gray": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", - "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", - "dependencies": { - "@csstools/convert-colors": "^1.4.0", - "postcss": "^7.0.5", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-color-gray/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-color-gray/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-color-gray/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", - "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", - "dependencies": { - "postcss": "^7.0.14", - "postcss-values-parser": "^2.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-color-hex-alpha/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-color-hex-alpha/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-color-hex-alpha/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-color-mod-function": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", - "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", - "dependencies": { - "@csstools/convert-colors": "^1.4.0", - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-color-mod-function/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-color-mod-function/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-color-mod-function/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", - "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-color-rebeccapurple/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-color-rebeccapurple/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-color-rebeccapurple/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-colormin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.0.tgz", - "integrity": "sha512-+HC6GfWU3upe5/mqmxuqYZ9B2Wl4lcoUUNkoaX59nEWV4EtADCMiBqui111Bu8R8IvaZTmqmxrqOAqjbHIwXPw==", - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz", - "integrity": "sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-custom-media": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", - "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", - "dependencies": { - "postcss": "^7.0.14" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-custom-media/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-custom-media/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-custom-media/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-custom-properties": { - "version": "8.0.11", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", - "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", - "dependencies": { - "postcss": "^7.0.17", - "postcss-values-parser": "^2.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-custom-properties/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-custom-properties/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-custom-properties/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", - "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-selector-parser": "^5.0.0-rc.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-custom-selectors/node_modules/cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-custom-selectors/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dependencies": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-custom-selectors/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-custom-selectors/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", - "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-selector-parser": "^5.0.0-rc.3" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dependencies": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-dir-pseudo-class/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", - "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", - "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", - "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", - "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", - "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", - "dependencies": { - "postcss": "^7.0.5", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-double-position-gradients/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-double-position-gradients/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-double-position-gradients/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-env-function": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", - "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-env-function/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-env-function/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-env-function/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-focus-visible": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", - "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-focus-visible/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-focus-visible/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-focus-visible/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-focus-within": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", - "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-focus-within/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-focus-within/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-focus-within/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-font-variant": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.1.tgz", - "integrity": "sha512-I3ADQSTNtLTTd8uxZhtSOrTCQ9G4qUVKPjHiDk0bV75QSxXjVWiJVJ2VLdspGUi9fbW9BcjKJoRvxAH1pckqmA==", - "dependencies": { - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-font-variant/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-font-variant/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-font-variant/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-gap-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", - "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-gap-properties/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-gap-properties/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-gap-properties/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-image-set-function": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", - "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-image-set-function/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-image-set-function/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-image-set-function/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-import": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.1.tgz", - "integrity": "sha512-Xn2+z++vWObbEPhiiKO1a78JiyhqipyrXHBb3AHpv0ks7Cdg+GxQQJ24ODNMTanldf7197gSP3axppO9yaG0lA==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.4.tgz", - "integrity": "sha512-3RLn6DIpMsK1l5UUy9jxQvoDeUN4gP939tDcKUHD/kM8SGSKbFAnvkpFpj3Bhtz3HGk1jWY5ZNWX6mPta5M9fg==", - "dependencies": { - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-initial/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-initial/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-initial/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-lab-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", - "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", - "dependencies": { - "@csstools/convert-colors": "^1.4.0", - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-lab-function/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-lab-function/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-lab-function/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-loader": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-5.2.0.tgz", - "integrity": "sha512-uSuCkENFeUaOYsKrXm0eNNgVIxc71z8RcckLMbVw473rGojFnrUeqEz6zBgXsH2q1EIzXnO/4pEz9RhALjlITA==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "semver": "^7.3.4" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-logical": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", - "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-logical/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-logical/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-logical/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-media-minmax": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", - "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-media-minmax/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-media-minmax/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-media-minmax/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz", - "integrity": "sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw==", - "dependencies": { - "css-color-names": "^1.0.1", - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz", - "integrity": "sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg==", - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^2.0.1", - "postcss-selector-parser": "^6.0.5", - "vendors": "^1.0.3" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz", - "integrity": "sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz", - "integrity": "sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "is-color-stop": "^1.1.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz", - "integrity": "sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.0", - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0", - "uniqs": "^2.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz", - "integrity": "sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nesting": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", - "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-nesting/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-nesting/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-nesting/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", - "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz", - "integrity": "sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz", - "integrity": "sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz", - "integrity": "sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz", - "integrity": "sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz", - "integrity": "sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz", - "integrity": "sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==", - "dependencies": { - "browserslist": "^4.16.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.1.tgz", - "integrity": "sha512-hkbG0j58Z1M830/CJ73VsP7gvlG1yF+4y7Fd1w4tD2c7CaA2Psll+pQ6eQhth9y9EaqZSLzamff/D0MZBMbYSg==", - "dependencies": { - "is-absolute-url": "^3.0.3", - "normalize-url": "^4.5.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz", - "integrity": "sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.1.tgz", - "integrity": "sha512-6mkCF5BQ25HvEcDfrMHCLLFHlraBSlOXFnQMHYhSpDO/5jSR1k8LdEXOkv+7+uzW6o6tBYea1Km0wQSRkPJkwA==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", - "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", - "dependencies": { - "postcss": "^7.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-overflow-shorthand/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-overflow-shorthand/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-overflow-shorthand/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-page-break": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", - "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", - "dependencies": { - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-page-break/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-page-break/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-page-break/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-place": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", - "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-values-parser": "^2.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-place/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-place/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-place/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-preset-env": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", - "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", - "dependencies": { - "autoprefixer": "^9.6.1", - "browserslist": "^4.6.4", - "caniuse-lite": "^1.0.30000981", - "css-blank-pseudo": "^0.1.4", - "css-has-pseudo": "^0.10.0", - "css-prefers-color-scheme": "^3.1.1", - "cssdb": "^4.4.0", - "postcss": "^7.0.17", - "postcss-attribute-case-insensitive": "^4.0.1", - "postcss-color-functional-notation": "^2.0.1", - "postcss-color-gray": "^5.0.0", - "postcss-color-hex-alpha": "^5.0.3", - "postcss-color-mod-function": "^3.0.3", - "postcss-color-rebeccapurple": "^4.0.1", - "postcss-custom-media": "^7.0.8", - "postcss-custom-properties": "^8.0.11", - "postcss-custom-selectors": "^5.1.2", - "postcss-dir-pseudo-class": "^5.0.0", - "postcss-double-position-gradients": "^1.0.0", - "postcss-env-function": "^2.0.2", - "postcss-focus-visible": "^4.0.0", - "postcss-focus-within": "^3.0.0", - "postcss-font-variant": "^4.0.0", - "postcss-gap-properties": "^2.0.0", - "postcss-image-set-function": "^3.0.1", - "postcss-initial": "^3.0.0", - "postcss-lab-function": "^2.0.1", - "postcss-logical": "^3.0.0", - "postcss-media-minmax": "^4.0.0", - "postcss-nesting": "^7.0.0", - "postcss-overflow-shorthand": "^2.0.0", - "postcss-page-break": "^2.0.0", - "postcss-place": "^4.0.1", - "postcss-pseudo-class-any-link": "^6.0.0", - "postcss-replace-overflow-wrap": "^3.0.0", - "postcss-selector-matches": "^4.0.0", - "postcss-selector-not": "^4.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-preset-env/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-preset-env/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-preset-env/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", - "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", - "dependencies": { - "postcss": "^7.0.2", - "postcss-selector-parser": "^5.0.0-rc.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dependencies": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-pseudo-class-any-link/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz", - "integrity": "sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw==", - "dependencies": { - "browserslist": "^4.16.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz", - "integrity": "sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==", - "dependencies": { - "cssnano-utils": "^2.0.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", - "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", - "dependencies": { - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-replace-overflow-wrap/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-replace-overflow-wrap/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-replace-overflow-wrap/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-selector-matches": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", - "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", - "dependencies": { - "balanced-match": "^1.0.0", - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-selector-matches/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-selector-matches/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-selector-matches/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-selector-not": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz", - "integrity": "sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==", - "dependencies": { - "balanced-match": "^1.0.0", - "postcss": "^7.0.2" - } - }, - "node_modules/postcss-selector-not/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-selector-not/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-selector-not/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.2.tgz", - "integrity": "sha512-YzQuFLZu3U3aheizD+B1joQ94vzPfE6BNUcSYuceNxlVnKKsOtdo6hL9/zyC168Q8EwfLSgaDSalsUGa9f2C0A==", - "dependencies": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.3.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz", - "integrity": "sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.5", - "uniqs": "^2.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" - }, - "node_modules/postcss-values-parser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", - "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", - "dependencies": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=6.14.4" - } - }, - "node_modules/preact": { - "version": "10.5.9", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.9.tgz", - "integrity": "sha512-X4m+4VMVINl/JFQKALOCwa3p8vhMAhBvle0hJ/W44w/WWfNb2TA7RNicDV3K2dNVs57f61GviEnVLiwN+fxiIg==" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "devOptional": true, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/queue-microtask": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", - "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", - "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/raw-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/reactivestates": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/reactivestates/-/reactivestates-2.0.1.tgz", - "integrity": "sha512-WppC3LbbOcfMCIyxBNLemI0W3pYG0Au9fUQMscNCmZkJEf5pgvunERv92nhWaEHsKCDgMsLl7BmTQmjv/p/B8A==", - "dependencies": { - "rxjs": "^6.0.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/read-cache/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.2.tgz", - "integrity": "sha512-5fyFUyO9B799foVk4n6ylcoAktG/FbE3jwRKxvwaeSrIunaoMc0u81dzXxjeAFKOce7O5KncdfwpGvvs6r5PsQ==", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.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/node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dependencies": { - "regenerate": "^1.4.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" - }, - "node_modules/regjsparser": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", - "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "node_modules/resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=8.9" - }, - "peerDependencies": { - "rework": "1.0.1", - "rework-visit": "1.0.0" - }, - "peerDependenciesMeta": { - "rework": { - "optional": true - }, - "rework-visit": { - "optional": true - } - } - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-url-loader/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "engines": { - "node": ">=0.12" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "devOptional": true - }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sass": { - "version": "1.32.12", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", - "integrity": "sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/sass-loader": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-11.0.1.tgz", - "integrity": "sha512-Vp1LcP4slTsTNLEiDkTcm8zGN/XYYrZz2BZybQbliWA8eXveqA/AxsEjllQTpJbg2MzCsx/qNO48sHdZtOaxTw==", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0", - "sass": "^1.3.0", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/schematics-utilities": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/schematics-utilities/-/schematics-utilities-1.1.3.tgz", - "integrity": "sha512-5HnrH+MJkUmK7KfRpA457FY0BZatX2oxNts54P1347xlICCC7KjAh0r2Tue20Xfruw1mS3X5woTxa8od+JsqUA==", - "dependencies": { - "@angular-devkit/core": "^7.3.6", - "@angular-devkit/schematics": "^7.3.6", - "npm-registry-client": "^8.5.1", - "parse5": "^5.0.0", - "rxjs": "^6.4.0", - "typescript": "^3.3.3333" - } - }, - "node_modules/schematics-utilities/node_modules/@angular-devkit/core": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.10.tgz", - "integrity": "sha512-h8Yj2+UfBsPI7jZ8X88tImO/7RPgNWUcKF8Uq/J5eUSN6z0FMO0lluD4sM7X8aikb7RK8MwkwrqB/xfxvvkOow==", - "dependencies": { - "ajv": "6.9.1", - "chokidar": "2.0.4", - "fast-json-stable-stringify": "2.0.0", - "rxjs": "6.3.3", - "source-map": "0.7.3" - }, - "engines": { - "node": ">= 8.9.0", - "npm": ">= 5.5.1" - } - }, - "node_modules/schematics-utilities/node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/schematics-utilities/node_modules/@angular-devkit/schematics": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.10.tgz", - "integrity": "sha512-LMTGQ8kJb80LjTttu0ZqWXddzYtDwjKtMKY9X0A60Iz8/wbGl0j+wYG7KAVoRF0JeieYXs8Dl9KWdjyJyvJ/RA==", - "dependencies": { - "@angular-devkit/core": "7.3.10", - "rxjs": "6.3.3" - }, - "engines": { - "node": ">= 8.9.0", - "npm": ">= 5.5.1" - } - }, - "node_modules/schematics-utilities/node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/schematics-utilities/node_modules/ajv": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", - "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", - "dependencies": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/schematics-utilities/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/schematics-utilities/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - }, - "optionalDependencies": { - "fsevents": "^1.2.2" - } - }, - "node_modules/schematics-utilities/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "node_modules/schematics-utilities/node_modules/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "node_modules/schematics-utilities/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/schematics-utilities/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/schematics-utilities/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "node_modules/schematics-utilities/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/schematics-utilities/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/schematics-utilities/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/schematics-utilities/node_modules/typescript": { - "version": "3.9.9", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", - "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/screenfull": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-4.2.1.tgz", - "integrity": "sha512-PLSp6f5XdhvjCCCO8OjavRfzkSGL3Qmdm7P82bxyU8HDDDBhDV3UckRaYcRa/NDNTYt8YBpzjoLWHUAejmOjLg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" - }, - "node_modules/selfsigned": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", - "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", - "dependencies": { - "node-forge": "^0.10.0" - } - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "dependencies": { - "semver": "^5.3.0" - } - }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", - "engines": { - "node": "*" - } - }, - "node_modules/smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/socket.io": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", - "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", - "devOptional": true, - "dependencies": { - "@types/cookie": "^0.4.0", - "@types/cors": "^2.8.8", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "debug": "~4.3.1", - "engine.io": "~4.1.0", - "socket.io-adapter": "~2.1.0", - "socket.io-parser": "~4.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", - "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", - "devOptional": true - }, - "node_modules/socket.io-parser": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", - "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "devOptional": true, - "dependencies": { - "@types/component-emitter": "^1.2.10", - "component-emitter": "~1.3.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sockjs-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.0.tgz", - "integrity": "sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q==", - "dependencies": { - "debug": "^3.2.6", - "eventsource": "^1.0.7", - "faye-websocket": "^0.11.3", - "inherits": "^2.0.4", - "json3": "^3.3.3", - "url-parse": "^1.4.7" - } - }, - "node_modules/sockjs-client/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/sockjs-client/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/sockjs-client/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/socks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", - "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", - "dependencies": { - "ip": "^1.1.5", - "smart-buffer": "^4.1.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz", - "integrity": "sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA==", - "dependencies": { - "agent-base": "6", - "debug": "4", - "socks": "^2.3.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", - "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-2.0.1.tgz", - "integrity": "sha512-UzOTTQhoNPeTNzOxwFw220RSRzdGSyH4lpNyWjR7Qm34P4/N0W669YSUFdH07+YNeN75h765XLHmNsF/bm97RQ==", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.2", - "source-map-js": "^0.6.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-loader/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "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==", - "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==" - }, - "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==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/spdy-transport/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/spdy-transport/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/spdy-transport/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/spdy/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/spdy/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "devOptional": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/streamroller": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", - "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", - "devOptional": true, - "dependencies": { - "date-format": "^2.1.0", - "debug": "^4.1.1", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/streamroller/node_modules/date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "devOptional": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/streamroller/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/streamroller/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.1.tgz", - "integrity": "sha512-LL0OLyN6AnfV9xqGQpDBwedT2Rt63737LxvsRxbcwpa2aIeynBApG2Sm//F3TaLHIR1aJBN52DWklc06b94o5Q==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "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" - } - }, - "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" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "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", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/style-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", - "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/style-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/stylehacks": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz", - "integrity": "sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==", - "dependencies": { - "browserslist": "^4.16.0", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/stylus": { - "version": "0.54.8", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", - "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", - "dependencies": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.6", - "mkdirp": "~1.0.4", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.3.0", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-5.0.0.tgz", - "integrity": "sha512-1OaGgixTgC8IAaMCodZXg7XYsfP1qU0UzTHDyPaWACUh34j9geJL4iA583tFJDOtfNUOfDLaBpUywc5MicQ1aA==", - "dependencies": { - "fast-glob": "^3.2.5", - "klona": "^2.0.4", - "normalize-path": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylus": ">=0.52.4", - "webpack": "^5.0.0" - } - }, - "node_modules/stylus/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/stylus/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stylus/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/svgo": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz", - "integrity": "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==", - "dependencies": { - "@trysound/sax": "0.1.1", - "chalk": "^4.1.0", - "commander": "^7.1.0", - "css-select": "^3.1.2", - "css-tree": "^1.1.2", - "csso": "^4.2.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/svgo/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/svgo/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/svgo/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/svgo/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/svgo/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/svgo/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.5.0.tgz", - "integrity": "sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tablesorter": { - "version": "2.31.3", - "resolved": "https://registry.npmjs.org/tablesorter/-/tablesorter-2.31.3.tgz", - "integrity": "sha512-ueEzeKiMajDcCWnUoT1dOeNEaS1OmPh9+8J0O2Sjp3TTijMygH74EA9QNJiNkLJqULyNU0RhbKY26UMUq9iurA==", - "dependencies": { - "jquery": ">=1.2.6" - } - }, - "node_modules/tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", - "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", - "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz", - "integrity": "sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==", - "dependencies": { - "jest-worker": "^26.6.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.7.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/tickety-tick-formatter": { - "resolved": "git+ssh://git@github.com/bitcrowd/tickety-tick-formatter.git#aa1895189e08689d340bf05d9e2855695a4dbb18", - "dependencies": { - "prettier": "^2.2.1", - "speakingurl": "^14.0.1", - "strip-indent": "^3.0.0" - } - }, - "node_modules/ticky": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", - "integrity": "sha1-t8+nHnaPHJAAxJe5FRswlHxQ5G0=" - }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "devOptional": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", - "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" - }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "optional": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - } - }, - "node_modules/tslint/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": 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==", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^1.8.1" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true, - "peer": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-func": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/type-func/-/type-func-1.0.3.tgz", - "integrity": "sha1-qxhCNK6A2NUAV87+/zstl9CK6bA=" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "node_modules/typedjson": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/typedjson/-/typedjson-1.5.1.tgz", - "integrity": "sha512-0jVMTow6Z0O/ZkGK95U5a/QKnN4xkupxKDr2ljyReBAFpzIS3/sy9ltdqyuGIaaR60ezKHPf2AgybCbOJ1VhYw==" - }, - "node_modules/typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.28", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", - "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", - "devOptional": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "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", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "node_modules/uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "devOptional": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urijs": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.6.tgz", - "integrity": "sha512-eSXsXZ2jLvGWeLYlQA3Gh36BcjF+0amo92+wHPyN1mdR8Nxf75fuEuYTd9c0a+m/vhCjRK0ESlE9YNLW+E1VEw==" - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "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==", - "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", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dependencies": { - "builtins": "^1.0.3" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.2.0.tgz", - "integrity": "sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webpack": { - "version": "5.36.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.36.2.tgz", - "integrity": "sha512-XJumVnnGoH2dV+Pk1VwgY4YT6AiMKpVoudUFCNOXMIVrEKPUgEwdIfWPjIuGLESAiS8EdIHX5+TiJz/5JccmRg==", - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.47", - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/wasm-edit": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "acorn": "^8.2.1", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.4.0", - "eslint-scope": "^5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.1", - "watchpack": "^2.0.0", - "webpack-sources": "^2.1.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", - "dependencies": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-dev-middleware/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/webpack-dev-server": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", - "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", - "dependencies": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.3.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.8", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "sockjs-client": "^1.5.0", - "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 6.11.5" - } - }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/webpack-dev-server/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/webpack-dev-server/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-dev-server/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dependencies": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/webpack-log/node_modules/ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-merge": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", - "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-subresource-integrity": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz", - "integrity": "sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw==", - "dependencies": { - "webpack-sources": "^1.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack/node_modules/webpack-sources": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.0.tgz", - "integrity": "sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ==", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "devOptional": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-parser/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zone.js": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", - "integrity": "sha512-DDh2Ab+A/B+9mJyajPjHFPWfYU1H+pdun4wnnk0OcQTNjem1XQSZ2CDW+rfZEUDjv5M19SBqAkjZi0x5wuB5Qw==", - "dependencies": { - "tslib": "^2.0.0" - } - } - }, "dependencies": { "@angular-devkit/architect": { "version": "0.1200.2", @@ -18489,7 +250,9 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.0.2.tgz", "integrity": "sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw==", - "requires": {} + "requires": { + "ajv": "^8.0.0" + } }, "json-schema-traverse": { "version": "1.0.0", @@ -18512,8 +275,7 @@ "version": "12.0.0", "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-12.0.0.tgz", "integrity": "sha512-gvvXQDXXi0gsWZ25KyMqF/1b3AaX+CJbpVgTPqxJdEx4euvmG/m3993ynmpf+Kc+F+aI2O9W4TUbDbbLWoCjIA==", - "dev": true, - "requires": {} + "dev": true }, "@angular-eslint/eslint-plugin": { "version": "12.0.0", @@ -19915,7 +1677,6 @@ "version": "7.13.9", "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz", "integrity": "sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw==", - "dev": true, "requires": { "core-js-pure": "^3.0.0", "regenerator-runtime": "^0.13.4" @@ -19988,6 +1749,21 @@ "to-fast-properties": "^2.0.0" } }, + "@braintree/sanitize-url": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-5.0.2.tgz", + "integrity": "sha512-NBEJlHWrhQucLhZGHtSxM2loSaNUMajC7KOYJLyfcdW/6goVoff2HoYI3bz8YCDN0wKGbxtUL0gx2dvHpvnWlw==" + }, + "@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, "@csstools/convert-colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", @@ -20153,6 +1929,18 @@ } } }, + "@html-eslint/eslint-plugin": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.11.0.tgz", + "integrity": "sha512-64vU1+BLaU12nSJdhkKv7WCdFgh/7aw0JciaovrkIPXcTVKWQlrM9ZRP9JEVRXS+UPhqNUkhR/CRdJdm8A2ITw==", + "dev": true + }, + "@html-eslint/parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.11.0.tgz", + "integrity": "sha512-1emf+ldTEfYcz3Ztj6UHq0mn2+N3KYaelM+H5v9p7vpv8PZ5IAuIPKk2bHP246hdKuderagR6SOUu3EKwhnjxg==", + "dev": true + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -20187,6 +1975,19 @@ "resolved": "https://registry.npmjs.org/@kolkov/ngx-gallery/-/ngx-gallery-1.0.11.tgz", "integrity": "sha512-PqtDc+Vffz5Hq5bosUEHk8qkUUTnlCVqRg6MpOfiRtsAF2IkLTS2Yw+czP3Y+wSI9OESvMGEhKmqN7VywkeTGQ==" }, + "@kyleshockey/object-assign-deep": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz", + "integrity": "sha1-hJAPDu/DcnmPR1G1JigwuCCJIuw=" + }, + "@kyleshockey/xml": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@kyleshockey/xml/-/xml-1.0.2.tgz", + "integrity": "sha512-iMo32MPLcI9cPxs3YL5kmKxKgDmkSZDCFEqIT5eRk7d/Ll8r4X3SwGYSigzALd6+RHWlFEmjL1QyaQ15xDZFlw==", + "requires": { + "stream": "^0.0.2" + } + }, "@ng-select/ng-option-highlight": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/@ng-select/ng-option-highlight/-/ng-option-highlight-0.0.5.tgz", @@ -20519,19 +2320,28 @@ "version": "1.2.10", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==", - "devOptional": true + "dev": true + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } }, "@types/cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz", "integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==", - "devOptional": true + "dev": true }, "@types/cors": { "version": "2.8.10", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", - "devOptional": true + "dev": true }, "@types/dragula": { "version": "3.7.0", @@ -20561,6 +2371,17 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" }, + "@types/express-serve-static-core": { + "version": "4.17.22", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.22.tgz", + "integrity": "sha512-WdqmrUsRS4ootGha6tVwk/IVHM1iorU8tGehftQD2NWiPniw/sm7xdJOIlXLwqdInL9wBw/p7oO8vaYEF3NDmA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -20575,6 +2396,14 @@ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" }, + "@types/hast": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.2.tgz", + "integrity": "sha512-Op5W7jYgZI7AWKY5wQ0/QNMzQM7dGQPyW1rXKNiymVCy5iTfdPuGu4HhYNOM2sIv8gUfIuIdcYlXmAepwaowow==", + "requires": { + "@types/unist": "*" + } + }, "@types/jasmine": { "version": "3.6.4", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.4.tgz", @@ -20646,6 +2475,18 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, "@types/resize-observer-browser": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.4.tgz", @@ -20661,6 +2502,12 @@ "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==" }, + "@types/swagger-ui": { + "version": "3.47.1", + "resolved": "https://registry.npmjs.org/@types/swagger-ui/-/swagger-ui-3.47.1.tgz", + "integrity": "sha512-qYPVoCH7P63JmTM4/sQtRER05PQlzWf46eEQ04nvFM0wCTALo0Sbx9BVLgRQNi7SgjWpFhjG7Z0pJgzbzw+Trw==", + "dev": true + }, "@types/tern": { "version": "0.23.3", "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz", @@ -20669,6 +2516,11 @@ "@types/estree": "*" } }, + "@types/unist": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.5.tgz", + "integrity": "sha512-wnra4Vw9dopnuybR6HBywJ/URYpYrKLoepBTEtgfJup8Ahoi2zJECPP2cwiXp7btTvOT2CULv87aQRA4eZSP6g==" + }, "@types/urijs": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.6.tgz", @@ -20696,6 +2548,15 @@ } } }, + "@types/ws": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.5.tgz", + "integrity": "sha512-8mbDgtc8xpxDDem5Gwj76stBDJX35KQ3YBoayxlqUQcL5BZUthiqP/VQ4PQnLHqM4PmlbyO74t98eJpURO+gPA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz", @@ -21063,6 +2924,16 @@ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==" }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -21091,8 +2962,7 @@ "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==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -21179,8 +3049,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "alphanum-sort": { "version": "1.0.2", @@ -21262,7 +3131,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "devOptional": true, "requires": { "sprintf-js": "~1.0.2" } @@ -21341,6 +3209,23 @@ "es-abstract": "^1.18.0-next.1" } }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -21404,6 +3289,21 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "autolinker": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.14.3.tgz", + "integrity": "sha512-t81i2bCpS+s+5FIhatoww9DmpjhbdiimuU9ATEuLxtZMQ7jLv9fyFn7SWNG8IkEfD4AmYyirL1ss9k1aqVWRvg==", + "requires": { + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "autoprefixer": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz", @@ -21455,6 +3355,12 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "axe-core": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.2.3.tgz", + "integrity": "sha512-pXnVMfJKSIWU2Ml4JHP7pZEPIrgBO1Fd3WGx+fPBsS+KRGhE4vxooD8XBGWbQOIVSZsVK7pUDBBkCicNu80yzQ==", + "dev": true + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -21608,6 +3514,27 @@ "@babel/helper-define-polyfill-provider": "^0.2.2" } }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -21667,7 +3594,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "devOptional": true + "dev": true }, "base64-js": { "version": "1.5.1", @@ -21678,7 +3605,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "devOptional": true + "dev": true }, "batch": { "version": "0.6.1", @@ -21822,6 +3749,20 @@ "node-releases": "^1.1.71" } }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -21841,13 +3782,6 @@ "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "optional": true, - "peer": true - }, "builtins": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", @@ -21953,6 +3887,15 @@ "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==" }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -21983,6 +3926,21 @@ "supports-color": "^5.3.0" } }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -22065,6 +4023,11 @@ } } }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -22254,7 +4217,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "devOptional": true + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -22264,6 +4227,11 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -22327,7 +4295,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "devOptional": true, + "dev": true, "requires": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -22384,8 +4352,7 @@ "cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "devOptional": true + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "cookie-signature": { "version": "1.0.6", @@ -22410,6 +4377,14 @@ "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "copy-webpack-plugin": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz", @@ -22468,8 +4443,7 @@ "core-js-pure": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz", - "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==", - "dev": true + "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==" }, "core-util-is": { "version": "1.0.2", @@ -22480,7 +4454,7 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "devOptional": true, + "dev": true, "requires": { "object-assign": "^4", "vary": "^1" @@ -22506,6 +4480,15 @@ "type-func": "^1.0.1" } }, + "create-react-class": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz", + "integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==", + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, "critters": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.10.tgz", @@ -22563,6 +4546,21 @@ } } }, + "cross-fetch": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz", + "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==", + "requires": { + "node-fetch": "2.6.1" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + } + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -22891,6 +4889,11 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=" + }, "cssauron": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", @@ -22959,8 +4962,7 @@ "cssnano-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", - "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", - "requires": {} + "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==" }, "csso": { "version": "4.2.0", @@ -23001,7 +5003,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", - "devOptional": true + "dev": true }, "debug": { "version": "2.6.9", @@ -23034,6 +5036,11 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -23155,6 +5162,12 @@ } } }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "dev": true + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -23189,13 +5202,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "devOptional": true + "dev": true }, "diff": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "devOptional": true + "dev": true }, "dir-glob": { "version": "3.0.1", @@ -23266,7 +5279,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "devOptional": true, + "dev": true, "requires": { "custom-event": "~1.0.0", "ent": "~2.2.0", @@ -23307,6 +5320,11 @@ "domelementtype": "^2.2.0" } }, + "dompurify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.0.tgz", + "integrity": "sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==" + }, "domutils": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", @@ -23360,6 +5378,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz", "integrity": "sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==" }, + "emitter-component": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", + "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY=" + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -23379,7 +5402,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, "requires": { "iconv-lite": "^0.6.2" }, @@ -23388,7 +5410,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -23407,7 +5428,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.1.1.tgz", "integrity": "sha512-t2E9wLlssQjGw0nluF6aYyfX8LwYU8Jj0xct+pAhfWfv/YrBn6TSNtEYsgxHIfaMqfrLx07czcMg9bMN6di+3w==", - "devOptional": true, + "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -23422,7 +5443,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -23431,7 +5452,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true } } }, @@ -23439,7 +5460,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.2.tgz", "integrity": "sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==", - "devOptional": true, + "dev": true, "requires": { "base64-arraybuffer": "0.1.4" } @@ -23466,7 +5487,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "devOptional": true + "dev": true }, "entities": { "version": "2.2.0", @@ -23567,6 +5588,21 @@ } } }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "es6-symbol": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", @@ -23823,6 +5859,17 @@ } } }, + "eslint-config-airbnb": { + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz", + "integrity": "sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^14.2.1", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + } + }, "eslint-config-airbnb-base": { "version": "14.2.1", "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", @@ -23834,6 +5881,17 @@ "object.entries": "^1.1.2" } }, + "eslint-config-airbnb-typescript": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.3.1.tgz", + "integrity": "sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^4.4.1", + "eslint-config-airbnb": "^18.2.0", + "eslint-config-airbnb-base": "^14.2.0" + } + }, "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", @@ -23879,68 +5937,232 @@ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "^1.0.0" + "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 + }, + "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.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + }, + "dependencies": { + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + } + } + }, + "eslint-plugin-react": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", + "integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.4", + "object.fromentries": "^2.0.4", + "object.values": "^1.1.4", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.5" + }, + "dependencies": { + "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" } }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", "dev": true, "requires": { - "p-limit": "^1.1.0" + "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", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "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" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" } }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", "dev": true }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "object.entries": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", "dev": true, "requires": { - "find-up": "^2.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" } - } - } - }, - "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + }, + "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": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" } } } }, + "eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", + "dev": true + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -24001,8 +6223,122 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "devOptional": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esprint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/esprint/-/esprint-3.1.0.tgz", + "integrity": "sha512-UfD6iLycX/bZHxDJW3bfYTNj+RyD89nS6fVx57yS6tuup5ymUxGzopEwhwK2q8D0HKn0pxwaEwTjxBAfH7WggQ==", + "dev": true, + "requires": { + "fb-watchman": "^2.0.1", + "glob": "^7.1.7", + "jayson": "^3.6.3", + "jest-worker": "^27.0.2", + "sane": "^4.1.0", + "yargs": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "27.0.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.6.tgz", + "integrity": "sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.0.1.tgz", + "integrity": "sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } }, "esquery": { "version": "1.4.0", @@ -24088,6 +6424,12 @@ "original": "^1.0.0" } }, + "exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, "execa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", @@ -24188,6 +6530,21 @@ } } }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -24296,6 +6653,12 @@ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -24314,6 +6677,11 @@ "picomatch": "^2.2.1" } }, + "fast-json-patch": { + "version": "3.0.0-1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz", + "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -24339,6 +6707,14 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "requires": { + "format": "^0.2.0" + } + }, "faye-websocket": { "version": "0.11.3", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", @@ -24347,6 +6723,36 @@ "websocket-driver": ">=0.5.1" } }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -24436,7 +6842,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "devOptional": true + "dev": true }, "flatten": { "version": "1.0.3", @@ -24468,6 +6874,11 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=" + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -24490,7 +6901,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "devOptional": true, + "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -24707,6 +7118,12 @@ "function-bind": "^1.1.1" } }, + "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-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -24769,11 +7186,41 @@ } } }, + "hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" + }, + "hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + } + }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", @@ -25049,8 +7496,7 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "ieee754": { "version": "1.2.1", @@ -25076,6 +7522,11 @@ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", "optional": true }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -25214,6 +7665,25 @@ "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", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -25252,6 +7722,20 @@ } } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arguments": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", @@ -25270,6 +7754,12 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "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", @@ -25278,6 +7768,15 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -25340,6 +7839,11 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -25362,6 +7866,15 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, + "is-dom": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz", + "integrity": "sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ==", + "requires": { + "is-object": "^1.0.1", + "is-window": "^1.0.2" + } + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -25385,6 +7898,11 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, "is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -25406,6 +7924,17 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, + "is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==" + }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", @@ -25435,6 +7964,11 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, "is-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", @@ -25484,6 +8018,11 @@ "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==" }, + "is-window": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz", + "integrity": "sha1-LIlspT25feRdPDMTOmXYyfVjSA0=" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -25503,7 +8042,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz", "integrity": "sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==", - "devOptional": true + "dev": true }, "iselement": { "version": "1.1.4", @@ -25520,6 +8059,29 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isomorphic-form-data": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-form-data/-/isomorphic-form-data-2.0.0.tgz", + "integrity": "sha512-TYgVnXWeESVmQSg4GLVbalmQ+B4NPi/H4eWxqALKj63KsUrcu301YDjBqaOw3h+cbak7Na4Xyps3BiptHtxTfg==", + "requires": { + "form-data": "^2.3.2" + } + }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "dev": true + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -25658,6 +8220,37 @@ "colors": "1.4.0" } }, + "jayson": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.6.4.tgz", + "integrity": "sha512-GH63DsRFFlodS8krFgAhxwYvQFmSwjsFxKnPrHQtp+BJj/tpeSj3hyBGGqmTkuq043U1Gn6u8VdsVRFZX1EEiQ==", + "dev": true, + "requires": { + "@types/connect": "^3.4.33", + "@types/express-serve-static-core": "^4.17.9", + "@types/lodash": "^4.14.159", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "JSONStream": "^1.3.5", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.20", + "uuid": "^3.4.0", + "ws": "^7.4.5" + }, + "dependencies": { + "@types/lodash": { + "version": "4.14.170", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", + "integrity": "sha512-bpcvu/MKHHeYX+qeEN8GE7DIravODWdACVA1ctevD8CN24RhPZIKMn9ntfAsrvLfSX3cR5RrBKAbYm9bGs0A+Q==", + "dev": true + } + } + }, "jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -25708,6 +8301,11 @@ "resolved": "https://registry.npmjs.org/jquery.caret/-/jquery.caret-0.3.1.tgz", "integrity": "sha1-nAkzGPrzJ+/zIugmyp8yQTaLx7g=" }, + "js-file-download": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -25717,7 +8315,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "devOptional": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -25786,7 +8383,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "devOptional": true, + "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -25807,11 +8404,21 @@ "verror": "1.10.0" } }, + "jsx-ast-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", + "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" + } + }, "karma": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.2.tgz", "integrity": "sha512-fo4Wt0S99/8vylZMxNj4cBFyOBBnC1bewZ0QOlePij/2SZVWxqbyLeIddY13q6URa2EpLRW8ixvFRUMjkmo1bw==", - "devOptional": true, + "dev": true, "requires": { "body-parser": "^1.19.0", "braces": "^3.0.2", @@ -25842,7 +8449,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -25851,7 +8458,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "devOptional": true, + "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -25862,7 +8469,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -25871,19 +8478,19 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "dev": true }, "mime": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "devOptional": true + "dev": true }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, + "dev": true, "requires": { "glob": "^7.1.3" } @@ -25892,13 +8499,13 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true + "dev": true }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "devOptional": true, + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -25909,13 +8516,13 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "devOptional": true + "dev": true }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "devOptional": true, + "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -25930,7 +8537,7 @@ "version": "20.2.7", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz", "integrity": "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==", - "devOptional": true + "dev": true } } }, @@ -26051,6 +8658,21 @@ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==" }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, "less": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/less/-/less-4.1.1.tgz", @@ -26267,7 +8889,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", - "devOptional": true, + "dev": true, "requires": { "date-format": "^3.0.0", "debug": "^4.1.1", @@ -26280,7 +8902,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -26289,7 +8911,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true } } }, @@ -26298,6 +8920,23 @@ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "requires": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -26359,6 +8998,15 @@ "ssri": "^8.0.0" } }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", @@ -26820,6 +9468,15 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-forge": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", @@ -26868,6 +9525,12 @@ } } }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, "node-releases": { "version": "1.1.72", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", @@ -27139,8 +9802,7 @@ "object-inspect": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" }, "object-is": { "version": "1.1.5", @@ -27187,6 +9849,18 @@ "has": "^1.0.3" } }, + "object.fromentries": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", + "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -27503,6 +10177,19 @@ } } }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -28085,26 +10772,22 @@ "postcss-discard-comments": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", - "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "requires": {} + "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==" }, "postcss-discard-duplicates": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", - "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "requires": {} + "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==" }, "postcss-discard-empty": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", - "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "requires": {} + "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==" }, "postcss-discard-overridden": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", - "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", - "requires": {} + "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==" }, "postcss-double-position-gradients": { "version": "1.0.0", @@ -28568,8 +11251,7 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "requires": {} + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -28633,8 +11315,7 @@ "postcss-normalize-charset": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", - "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "requires": {} + "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==" }, "postcss-normalize-display-values": { "version": "5.0.1", @@ -29116,6 +11797,11 @@ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" }, + "prismjs": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz", + "integrity": "sha512-mNPsedLuk90RVJioIky8ANZEwYm5w9LcvCXrxHlwf4fNVSn8jEipMybMkWUyyF0JhnC+C4VcOVSBuHRKs1L5Ow==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -29127,6 +11813,14 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -29141,6 +11835,24 @@ "retry": "^0.12.0" } }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "requires": { + "xtend": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -29178,7 +11890,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "devOptional": true + "dev": true }, "qs": { "version": "6.7.0", @@ -29190,6 +11902,11 @@ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, + "querystring-browser": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/querystring-browser/-/querystring-browser-1.0.4.tgz", + "integrity": "sha1-8uNYgYQKgZvHsb9Zf68JeeZiLcY=" + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -29200,6 +11917,14 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz", "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==" }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -29264,6 +11989,117 @@ } } }, + "react": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/react/-/react-15.7.0.tgz", + "integrity": "sha512-5/MMRYmpmM0sMTHGLossnJCrmXQIiJilD6y3YN3TzAwGFj6zdnMtFv6xmi65PHKRV+pehIHpT7oy67Sr6s9AHA==", + "requires": { + "create-react-class": "^15.6.0", + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-copy-to-clipboard": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.3.tgz", + "integrity": "sha512-9S3j+m+UxDZOM0Qb8mhnT/rMR0NGSrj9A/073yz2DSxPMYhmYFBMYIdI2X4o8AjOjyFsSNxDRnCX6s/gRxpriw==", + "requires": { + "copy-to-clipboard": "^3", + "prop-types": "^15.5.8" + } + }, + "react-debounce-input": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.3.tgz", + "integrity": "sha512-7Bfjm9sxrtgB+IsSrdXoo4CVqKg7CbWC68dNhr8q7ZmY6C0AqtR524//SenHQWT+eeSG9DmSLWNWCUFSyaaWSQ==", + "requires": { + "lodash.debounce": "^4", + "prop-types": "^15.7.2" + } + }, + "react-dom": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.7.0.tgz", + "integrity": "sha512-mpjXqC2t1FuYsILOLCj0kg6pbg460byZkVA/80VtDmKU/pYmoTdHOtaMcTRIDiyXLz4sIur0cQ04nOC6iGndJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.0", + "prop-types": "^15.5.10" + } + }, + "react-immutable-proptypes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz", + "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==", + "requires": { + "invariant": "^2.2.2" + } + }, + "react-immutable-pure-component": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-1.2.4.tgz", + "integrity": "sha512-zPXaFWxaF4+ztVMFNMlCFkrhjpb9MPcL3JnXUpb6wKGF1+vBoSgClFbpbOsZAji7gm+RHBE24H44Lday2xxPjw==" + }, + "react-inspector": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-2.3.1.tgz", + "integrity": "sha512-tUUK7t3KWgZEIUktOYko5Ic/oYwvjEvQUFAGC1UeMeDaQ5za2yZFtItJa2RTwBJB//NxPr000WQK6sEbqC6y0Q==", + "requires": { + "babel-runtime": "^6.26.0", + "is-dom": "^1.0.9", + "prop-types": "^15.6.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-motion": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/react-motion/-/react-motion-0.5.2.tgz", + "integrity": "sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ==", + "requires": { + "performance-now": "^0.2.0", + "prop-types": "^15.5.8", + "raf": "^3.1.0" + }, + "dependencies": { + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + } + } + }, + "react-redux": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz", + "integrity": "sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg==", + "requires": { + "create-react-class": "^15.5.1", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.0.0", + "lodash": "^4.17.11", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2" + } + }, + "react-syntax-highlighter": { + "version": "15.4.3", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.3.tgz", + "integrity": "sha512-TnhGgZKXr5o8a63uYdRTzeb8ijJOgRGe0qjrE0eK/gajtdyqnSO6LqB3vW16hHB0cFierYSoy/AOJw8z1Dui8g==", + "requires": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "lowlight": "^1.17.0", + "prismjs": "^1.22.0", + "refractor": "^3.2.0" + } + }, "reactivestates": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/reactivestates/-/reactivestates-2.0.1.tgz", @@ -29401,11 +12237,47 @@ "picomatch": "^2.2.1" } }, + "redux": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", + "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", + "requires": { + "lodash": "^4.2.1", + "lodash-es": "^4.2.1", + "loose-envify": "^1.1.0", + "symbol-observable": "^1.0.3" + }, + "dependencies": { + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + } + } + }, + "redux-immutable": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-3.1.0.tgz", + "integrity": "sha1-yvvWhuBxEmERm5wolgk13EeknQo=", + "requires": { + "immutable": "^3.8.1" + } + }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, + "refractor": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.4.0.tgz", + "integrity": "sha512-dBeD02lC5eytm9Gld2Mx0cMcnR+zhSnsTfPpWqFaMgUMJfC9A6bcN3Br/NaXrnBJcuxnLFR90k1jrkaSyV8umg==", + "requires": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.24.0" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -29494,6 +12366,15 @@ } } }, + "remarkable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz", + "integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==", + "requires": { + "argparse": "^1.0.10", + "autolinker": "^3.11.0" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -29563,6 +12444,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, + "reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" + }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -29655,7 +12541,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "devOptional": true + "dev": true }, "rgb-regex": { "version": "1.0.1", @@ -29675,6 +12561,12 @@ "glob": "^7.1.3" } }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -29703,24 +12595,165 @@ } } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, "sass": { "version": "1.32.12", "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.12.tgz", @@ -30088,6 +13121,21 @@ } } }, + "serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "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==" + } + } + }, "serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", @@ -30165,11 +13213,25 @@ } } }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -30191,6 +13253,16 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "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", @@ -30366,7 +13438,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.1.2.tgz", "integrity": "sha512-JubKZnTQ4Z8G4IZWtaAZSiRP3I/inpy8c/Bsx2jrwGrTbKeVU5xd6qkKMHpChYeM3dWZSO0QACiGK+obhBNwYw==", - "devOptional": true, + "dev": true, "requires": { "@types/cookie": "^0.4.0", "@types/cors": "^2.8.8", @@ -30383,7 +13455,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -30392,7 +13464,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true } } }, @@ -30400,13 +13472,13 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.1.0.tgz", "integrity": "sha512-+vDov/aTsLjViYTwS9fPy5pEtTkrbEKsw2M+oVSoFGw6OD1IpvlV1VPhUzNbofCQ8oyMbdYJqDtGdmHQK6TdPg==", - "devOptional": true + "dev": true }, "socket.io-parser": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", - "devOptional": true, + "dev": true, "requires": { "@types/component-emitter": "^1.2.10", "component-emitter": "~1.3.0", @@ -30417,7 +13489,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -30426,7 +13498,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true } } }, @@ -30577,6 +13649,11 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -30686,8 +13763,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "devOptional": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -30742,11 +13818,19 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8=", + "requires": { + "emitter-component": "^1.1.1" + } + }, "streamroller": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", - "devOptional": true, + "dev": true, "requires": { "date-format": "^2.1.0", "debug": "^4.1.1", @@ -30757,13 +13841,13 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "devOptional": true + "dev": true }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -30772,18 +13856,10 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, "string-width": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.1.tgz", @@ -30794,6 +13870,76 @@ "strip-ansi": "^6.0.0" } }, + "string.prototype.matchall": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz", + "integrity": "sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "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", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "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" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + } + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -30814,6 +13960,14 @@ "define-properties": "^1.1.3" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -30994,6 +14148,181 @@ } } }, + "swagger-client": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.13.6.tgz", + "integrity": "sha512-hmE1qMqEw0C16pIiNlQAfUBO5s7tYLRq2/KPXr96V5BLXe5sAGeffBISuMX5l78wFgDMdAmctqBzEUM4eL3msg==", + "requires": { + "@babel/runtime-corejs3": "^7.11.2", + "btoa": "^1.2.1", + "buffer": "^6.0.3", + "cookie": "~0.4.1", + "cross-fetch": "^3.1.4", + "deep-extend": "~0.6.0", + "fast-json-patch": "^3.0.0-1", + "isomorphic-form-data": "~2.0.0", + "js-yaml": "^3.14.0", + "lodash": "^4.17.19", + "qs": "^6.9.4", + "querystring-browser": "^1.0.4", + "traverse": "~0.6.6", + "url": "~0.11.0" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "qs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", + "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", + "requires": { + "side-channel": "^1.0.4" + } + } + } + }, + "swagger-ui": { + "version": "3.51.1", + "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-3.51.1.tgz", + "integrity": "sha512-iLYCozRC5Jj0DQNJ0C46gYDTnv+TfNEnXeNvGIR0qIvyfosmqY11XzVdeibP1fbh/hFTq0GgXCbRm/ctfpqdHw==", + "requires": { + "@babel/runtime-corejs3": "^7.14.7", + "@braintree/sanitize-url": "^5.0.2", + "@kyleshockey/object-assign-deep": "^0.4.2", + "@kyleshockey/xml": "^1.0.2", + "base64-js": "^1.5.1", + "classnames": "^2.3.1", + "css.escape": "1.5.1", + "deep-extend": "0.6.0", + "dompurify": "^2.2.9", + "ieee754": "^1.2.1", + "immutable": "^3.x.x", + "js-file-download": "^0.4.12", + "js-yaml": "^3.13.1", + "lodash": "^4.17.21", + "memoizee": "^0.4.15", + "prop-types": "^15.7.2", + "randombytes": "^2.1.0", + "react": "=15.7.0", + "react-copy-to-clipboard": "5.0.3", + "react-debounce-input": "^3.2.3", + "react-dom": "=15.7.0", + "react-immutable-proptypes": "2.2.0", + "react-immutable-pure-component": "^1.1.1", + "react-inspector": "^2.3.0", + "react-motion": "^0.5.2", + "react-redux": "=4.4.10", + "react-syntax-highlighter": "^15.4.3", + "redux": "=3.7.2", + "redux-immutable": "3.1.0", + "remarkable": "^2.0.1", + "reselect": "^4.0.0", + "serialize-error": "^8.1.0", + "sha.js": "^2.4.11", + "swagger-client": "^3.13.5", + "url-parse": "^1.5.1", + "xml-but-prettier": "^1.0.1", + "zenscroll": "^4.0.2" + }, + "dependencies": { + "@babel/runtime-corejs3": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", + "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", + "requires": { + "core-js-pure": "^3.15.0", + "regenerator-runtime": "^0.13.4" + } + }, + "core-js-pure": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.2.tgz", + "integrity": "sha512-D42L7RYh1J2grW8ttxoY1+17Y4wXZeKe7uyplAI3FkNQyI5OgBIAjUfFiTPfL1rs0qLpxaabITNbjKl1Sp82tA==" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + }, + "dependencies": { + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + } + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + } + } + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -31161,7 +14490,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "devOptional": true, + "dev": true, "requires": { "rimraf": "^3.0.0" }, @@ -31170,13 +14499,19 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, + "dev": true, "requires": { "glob": "^7.1.3" } } } }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -31219,6 +14554,11 @@ "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -31233,6 +14573,11 @@ "punycode": "^2.1.1" } }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -31279,56 +14624,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" }, - "tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "optional": true, - "peer": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true, - "peer": true - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "optional": true, - "peer": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "optional": true, - "peer": true - } - } - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -31342,6 +14637,11 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -31388,8 +14688,27 @@ "ua-parser-js": { "version": "0.7.28", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", - "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", - "devOptional": true + "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" + }, + "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" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } + } }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", @@ -31456,7 +14775,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "devOptional": true + "dev": true }, "unpipe": { "version": "1.0.0", @@ -31614,7 +14933,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "devOptional": true + "dev": true + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } }, "watchpack": { "version": "2.2.0", @@ -32063,6 +15391,11 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -32071,6 +15404,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", @@ -32178,8 +15524,20 @@ "version": "7.4.6", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "devOptional": true, - "requires": {} + "dev": true + }, + "xml-but-prettier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-but-prettier/-/xml-but-prettier-1.0.1.tgz", + "integrity": "sha1-9aMyZ+1CzNTjVcYlV6XjmwH7QPM=", + "requires": { + "repeat-string": "^1.5.2" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.1", @@ -32275,6 +15633,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, + "zenscroll": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zenscroll/-/zenscroll-4.0.2.tgz", + "integrity": "sha1-6NV3TRwHOKR7z6hynzcS4t7d6yU=" + }, "zone.js": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 69c838c300..1fee2de740 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,8 @@ "@angular-eslint/schematics": "12.0.0", "@angular-eslint/template-parser": "^12.0.0", "@angular/language-service": "12.0.2", + "@html-eslint/eslint-plugin": "^0.11.0", + "@html-eslint/parser": "^0.11.0", "@jsdevtools/coverage-istanbul-loader": "3.0.5", "@types/jasmine": "~3.6.0", "@types/swagger-ui": "^3.47.0", @@ -19,7 +21,12 @@ "codelyzer": "^6.0.0", "eslint": "^7.26.0", "eslint-config-airbnb-base": "^14.2.1", + "eslint-config-airbnb-typescript": "^12.3.1", "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.24.0", + "eslint-plugin-react-hooks": "^4.2.0", + "esprint": "^3.1.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~6.3.2", @@ -132,6 +139,7 @@ "test": "ng test --watch=false", "test-watch": "ng test --watch=true", "lint": "eslint -c .eslintrc.js --ext .ts src/app/", + "lint:fix": "esprint check --fix", "generate-typings": "tsc -d -p src/tsconfig.app.json" } } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index f56c95734d..1239bf2156 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,60 +26,78 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APP_INITIALIZER, ApplicationRef, Injector, NgModule } from '@angular/core'; +import { + APP_INITIALIZER, ApplicationRef, Injector, NgModule, +} from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { States } from 'core-app/core/states/states.service'; -import { OpenprojectFieldsModule } from "core-app/shared/components/fields/openproject-fields.module"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpDragScrollDirective } from "core-app/shared/directives/op-drag-scroll/op-drag-scroll.directive"; -import { OpenProjectDirectFileUploadService } from './core/file-upload/op-direct-file-upload.service'; -import { DynamicBootstrapper } from "core-app/core/setup/globals/dynamic-bootstrapper"; +import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openproject-fields.module'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpDragScrollDirective } from 'core-app/shared/directives/op-drag-scroll/op-drag-scroll.directive'; +import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; -import { OpenprojectGridsModule } from "core-app/shared/components/grids/openproject-grids.module"; -import { OpenprojectRouterModule } from "core-app/core/routing/openproject-router.module"; -import { OpenprojectWorkPackageRoutesModule } from "core-app/features/work-packages/openproject-work-package-routes.module"; -import { BrowserModule } from "@angular/platform-browser"; -import { OpenprojectCalendarModule } from "core-app/shared/components/calendar/openproject-calendar.module"; -import { OpenprojectGlobalSearchModule } from "core-app/core/global_search/openproject-global-search.module"; -import { OpenprojectDashboardsModule } from "core-app/features/dashboards/openproject-dashboards.module"; -import { OpenprojectWorkPackageGraphsModule } from "core-app/shared/components/work-package-graphs/openproject-work-package-graphs.module"; -import { PreviewTriggerService } from "core-app/core/setup/globals/global-listeners/preview-trigger.service"; -import { OpenprojectOverviewModule } from "core-app/features/overview/openproject-overview.module"; -import { OpenprojectMyPageModule } from "core-app/features/my-page/openproject-my-page.module"; -import { OpenprojectProjectsModule } from "core-app/features/projects/openproject-projects.module"; -import { KeyboardShortcutService } from "core-app/shared/directives/a11y/keyboard-shortcut-service"; -import { OpenprojectMembersModule } from "core-app/shared/components/autocompleter/members-autocompleter/members.module"; -import { OpenprojectAugmentingModule } from "core-app/core/augmenting/openproject-augmenting.module"; -import { OpenprojectInviteUserModalModule } from "core-app/features/invite-user-modal/invite-user-modal.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { RevitAddInSettingsButtonService } from "core-app/features/bim/revit_add_in/revit-add-in-settings-button.service"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; +import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openproject-grids.module'; +import { OpenprojectRouterModule } from 'core-app/core/routing/openproject-router.module'; +import { OpenprojectWorkPackageRoutesModule } from 'core-app/features/work-packages/openproject-work-package-routes.module'; +import { BrowserModule } from '@angular/platform-browser'; +import { OpenprojectCalendarModule } from 'core-app/shared/components/calendar/openproject-calendar.module'; +import { OpenprojectGlobalSearchModule } from 'core-app/core/global_search/openproject-global-search.module'; +import { OpenprojectDashboardsModule } from 'core-app/features/dashboards/openproject-dashboards.module'; +import { OpenprojectWorkPackageGraphsModule } from 'core-app/shared/components/work-package-graphs/openproject-work-package-graphs.module'; +import { PreviewTriggerService } from 'core-app/core/setup/globals/global-listeners/preview-trigger.service'; +import { OpenprojectOverviewModule } from 'core-app/features/overview/openproject-overview.module'; +import { OpenprojectMyPageModule } from 'core-app/features/my-page/openproject-my-page.module'; +import { OpenprojectProjectsModule } from 'core-app/features/projects/openproject-projects.module'; +import { KeyboardShortcutService } from 'core-app/shared/directives/a11y/keyboard-shortcut-service'; +import { OpenprojectMembersModule } from 'core-app/shared/components/autocompleter/members-autocompleter/members.module'; +import { OpenprojectAugmentingModule } from 'core-app/core/augmenting/openproject-augmenting.module'; +import { OpenprojectInviteUserModalModule } from 'core-app/features/invite-user-modal/invite-user-modal.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { RevitAddInSettingsButtonService } from 'core-app/features/bim/revit_add_in/revit-add-in-settings-button.service'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { OpenprojectEnterpriseModule } from 'core-app/features/enterprise/openproject-enterprise.module'; +import { MainMenuToggleComponent } from 'core-app/core/main-menu/main-menu-toggle.component'; +import { MainMenuNavigationService } from 'core-app/core/main-menu/main-menu-navigation.service'; +import { ConfirmDialogService } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.service'; +import { ConfirmDialogModalComponent } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.modal'; +import { DynamicContentModalComponent } from 'core-app/shared/components/modals/modal-wrapper/dynamic-content.modal'; +import { PasswordConfirmationModalComponent } from 'core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal'; +import { WpPreviewModalComponent } from 'core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal'; +import { ConfirmFormSubmitController } from 'core-app/shared/components/modals/confirm-form-submit/confirm-form-submit.directive'; +import { ProjectMenuAutocompleteComponent } from 'core-app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component'; +import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; +import { MainMenuResizerComponent } from 'core-app/shared/components/resizer/resizer/main-menu-resizer.component'; +import { CommentService } from 'core-app/features/work-packages/components/wp-activity/comment-service'; +import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; +import { OpenprojectAdminModule } from 'core-app/features/admin/openproject-admin.module'; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; +import { globalDynamicComponents } from 'core-app/core/setup/global-dynamic-components.const'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { OpenprojectPluginsModule } from 'core-app/features/plugins/openproject-plugins.module'; +import { LinkedPluginsModule } from 'core-app/features/plugins/linked-plugins.module'; +import { OpenProjectInAppNotificationsModule } from 'core-app/features/in-app-notifications/in-app-notifications.module'; import { OpenProjectBackupService } from './core/backup/op-backup.service'; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { OpenprojectEnterpriseModule } from "core-app/features/enterprise/openproject-enterprise.module"; -import { MainMenuToggleComponent } from "core-app/core/main-menu/main-menu-toggle.component"; -import { MainMenuNavigationService } from "core-app/core/main-menu/main-menu-navigation.service"; -import { ConfirmDialogService } from "core-app/shared/components/modals/confirm-dialog/confirm-dialog.service"; -import { ConfirmDialogModal } from "core-app/shared/components/modals/confirm-dialog/confirm-dialog.modal"; -import { DynamicContentModal } from "core-app/shared/components/modals/modal-wrapper/dynamic-content.modal"; -import { PasswordConfirmationModal } from "core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal"; -import { WpPreviewModal } from "core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal"; -import { ConfirmFormSubmitController } from "core-app/shared/components/modals/confirm-form-submit/confirm-form-submit.directive"; -import { ProjectMenuAutocompleteComponent } from "core-app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component"; -import { PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; -import { MainMenuResizerComponent } from "core-app/shared/components/resizer/resizer/main-menu-resizer.component"; -import { CommentService } from "core-app/features/work-packages/components/wp-activity/comment-service"; -import { OpenprojectTabsModule } from "core-app/shared/components/tabs/openproject-tabs.module"; -import { OpenprojectAdminModule } from "core-app/features/admin/openproject-admin.module"; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; -import { globalDynamicComponents } from "core-app/core/setup/global-dynamic-components.const"; -import { HookService } from "core-app/features/plugins/hook-service"; -import { OpenprojectPluginsModule } from "core-app/features/plugins/openproject-plugins.module"; -import { LinkedPluginsModule } from "core-app/features/plugins/linked-plugins.module"; -import { OpenProjectInAppNotificationsModule } from "core-app/features/in-app-notifications/in-app-notifications.module"; +import { OpenProjectDirectFileUploadService } from './core/file-upload/op-direct-file-upload.service'; + +export function initializeServices(injector:Injector) { + return () => { + const PreviewTrigger = injector.get(PreviewTriggerService); + const mainMenuNavigationService = injector.get(MainMenuNavigationService); + const keyboardShortcuts = injector.get(KeyboardShortcutService); + // Conditionally add the Revit Add-In settings button + injector.get(RevitAddInSettingsButtonService); + + mainMenuNavigationService.register(); + + PreviewTrigger.setupListener(); + + keyboardShortcuts.register(); + }; +} @NgModule({ imports: [ @@ -159,7 +177,9 @@ import { OpenProjectInAppNotificationsModule } from "core-app/features/in-app-no ], providers: [ { provide: States, useValue: new States() }, - { provide: APP_INITIALIZER, useFactory: initializeServices, deps: [Injector], multi: true }, + { + provide: APP_INITIALIZER, useFactory: initializeServices, deps: [Injector], multi: true, + }, PaginationService, OpenProjectBackupService, OpenProjectFileUploadService, @@ -173,10 +193,10 @@ import { OpenProjectInAppNotificationsModule } from "core-app/features/in-app-no OpContextMenuTrigger, // Modals - ConfirmDialogModal, - DynamicContentModal, - PasswordConfirmationModal, - WpPreviewModal, + ConfirmDialogModalComponent, + DynamicContentModalComponent, + PasswordConfirmationModalComponent, + WpPreviewModalComponent, // Main menu MainMenuResizerComponent, @@ -188,13 +208,11 @@ import { OpenProjectInAppNotificationsModule } from "core-app/features/in-app-no // Form configuration OpDragScrollDirective, ConfirmFormSubmitController, - ] + ], }) export class OpenProjectModule { - // noinspection JSUnusedGlobalSymbols ngDoBootstrap(appRef:ApplicationRef) { - // Register global dynamic components // this is necessary to ensure they are not tree-shaken // (if they are not used anywhere in Angular, they would be removed) @@ -214,19 +232,3 @@ export class OpenProjectModule { }); } } - -export function initializeServices(injector:Injector) { - return () => { - const PreviewTrigger = injector.get(PreviewTriggerService); - const mainMenuNavigationService = injector.get(MainMenuNavigationService); - const keyboardShortcuts = injector.get(KeyboardShortcutService); - // Conditionally add the Revit Add-In settings button - injector.get(RevitAddInSettingsButtonService); - - mainMenuNavigationService.register(); - - PreviewTrigger.setupListener(); - - keyboardShortcuts.register(); - }; -} diff --git a/frontend/src/app/core/active-window/active-window.service.ts b/frontend/src/app/core/active-window/active-window.service.ts index 9c080723f7..09369106e8 100644 --- a/frontend/src/app/core/active-window/active-window.service.ts +++ b/frontend/src/app/core/active-window/active-window.service.ts @@ -1,17 +1,16 @@ -import { Inject, Injectable } from "@angular/core"; -import { DOCUMENT } from "@angular/common"; -import { BehaviorSubject, Observable, Subject } from "rxjs"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; @Injectable({ providedIn: 'root' }) export class ActiveWindowService { - private activeState$ = new BehaviorSubject(true); constructor(@Inject(DOCUMENT) document:Document) { document.addEventListener('visibilitychange', () => { if (document.visibilityState) { - debugLog("Browser window has visibility state changed to " + document.visibilityState); + debugLog(`Browser window has visibility state changed to ${document.visibilityState}`); this.activeState$.next(document.visibilityState === 'visible'); } }); @@ -30,4 +29,4 @@ export class ActiveWindowService { public get active$():Observable { return this.activeState$.asObservable(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/core/apiv3/api-v3.service.spec.ts b/frontend/src/app/core/apiv3/api-v3.service.spec.ts index 092ac7296b..01733ca2c8 100644 --- a/frontend/src/app/core/apiv3/api-v3.service.spec.ts +++ b/frontend/src/app/core/apiv3/api-v3.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TestBed, waitForAsync } from "@angular/core/testing"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { States } from "core-app/core/states/states.service"; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { States } from 'core-app/core/states/states.service'; -describe('APIv3Service', function() { +describe('APIv3Service', () => { let service:APIV3Service; beforeEach(waitForAsync(() => { @@ -40,8 +40,8 @@ describe('APIv3Service', function() { providers: [ States, PathHelperService, - APIV3Service - ] + APIV3Service, + ], }) .compileComponents() .then(() => { @@ -53,34 +53,34 @@ describe('APIv3Service', function() { return new URLSearchParams(object).toString(); } - describe('apiV3', function() { - var projectIdentifier = 'majora'; + describe('apiV3', () => { + const projectIdentifier = 'majora'; - it('should provide the project\'s path', function() { + it("should provide the project's path", () => { expect(service.projects.id(projectIdentifier).path).toEqual('/api/v3/projects/majora'); }); - it('should provide a path to work package query on subject or ID ', function() { + it('should provide a path to work package query on subject or ID ', () => { let params = { filters: '[{"subjectOrId":{"operator":"**","values":["bogus"]}}]', sortBy: '[["updatedAt","desc"]]', offset: '1', - pageSize: '10' + pageSize: '10', }; expect( - service.work_packages.filterBySubjectOrId("bogus").path - ).toEqual('/api/v3/work_packages?' + encodeParams(params)); + service.work_packages.filterBySubjectOrId('bogus').path, + ).toEqual(`/api/v3/work_packages?${encodeParams(params)}`); params = { filters: '[{"id":{"operator":"=","values":["1234"]}}]', sortBy: '[["updatedAt","desc"]]', offset: '1', - pageSize: '10' + pageSize: '10', }; expect( - service.work_packages.filterBySubjectOrId("1234", true).path - ).toEqual('/api/v3/work_packages?' + encodeParams(params)); + service.work_packages.filterBySubjectOrId('1234', true).path, + ).toEqual(`/api/v3/work_packages?${encodeParams(params)}`); }); }); }); diff --git a/frontend/src/app/core/apiv3/api-v3.service.ts b/frontend/src/app/core/apiv3/api-v3.service.ts index 4febb6c9fe..e26b157322 100644 --- a/frontend/src/app/core/apiv3/api-v3.service.ts +++ b/frontend/src/app/core/apiv3/api-v3.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,39 +26,33 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable, Injector } from "@angular/core"; -import { - APIv3GettableResource, - APIv3ResourceCollection, - APIv3ResourcePath -} from "core-app/core/apiv3/paths/apiv3-resource"; -import { Constructor } from "@angular/cdk/table"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { Apiv3GridsPaths } from "core-app/core/apiv3/endpoints/grids/apiv3-grids-paths"; -import { Apiv3TimeEntriesPaths } from "core-app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths"; -import { Apiv3CapabilitiesPaths } from "core-app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths"; -import { Apiv3MembershipsPaths } from "core-app/core/apiv3/endpoints/memberships/apiv3-memberships-paths"; -import { Apiv3UsersPaths } from "core-app/core/apiv3/endpoints/users/apiv3-users-paths"; -import { APIv3TypesPaths } from "core-app/core/apiv3/endpoints/types/apiv3-types-paths"; -import { APIv3QueriesPaths } from "core-app/core/apiv3/endpoints/queries/apiv3-queries-paths"; -import { APIV3WorkPackagesPaths } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths"; -import { APIv3ProjectPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-project-paths"; -import { APIv3ProjectsPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-projects-paths"; -import { APIv3StatusesPaths } from "core-app/core/apiv3/endpoints/statuses/apiv3-statuses-paths"; -import { APIv3RolesPaths } from "core-app/core/apiv3/endpoints/roles/apiv3-roles-paths"; -import { APIv3VersionsPaths } from "core-app/core/apiv3/endpoints/versions/apiv3-versions-paths"; -import { Apiv3RelationsPaths } from "core-app/core/apiv3/endpoints/relations/apiv3-relations-paths"; -import { Apiv3NewsPaths } from "core-app/core/apiv3/endpoints/news/apiv3-news-paths"; -import { Apiv3HelpTextsPaths } from "core-app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths"; -import { Apiv3ConfigurationPath } from "core-app/core/apiv3/endpoints/configuration/apiv3-configuration-path"; -import { Apiv3BoardsPaths } from "core-app/core/apiv3/virtual/apiv3-boards-paths"; -import { RootResource } from "core-app/features/hal/resources/root-resource"; -import * as ts from "typescript/lib/tsserverlibrary"; -import Project = ts.server.Project; -import { Apiv3PlaceholderUsersPaths } from "core-app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths"; -import { Apiv3GroupsPaths } from "core-app/core/apiv3/endpoints/groups/apiv3-groups-paths"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Apiv3NotificationsPaths } from "core-app/core/apiv3/endpoints/notifications/apiv3-notifications-paths"; +import { Injectable, Injector } from '@angular/core'; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Constructor } from '@angular/cdk/table'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { Apiv3GridsPaths } from 'core-app/core/apiv3/endpoints/grids/apiv3-grids-paths'; +import { Apiv3TimeEntriesPaths } from 'core-app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths'; +import { Apiv3CapabilitiesPaths } from 'core-app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths'; +import { Apiv3MembershipsPaths } from 'core-app/core/apiv3/endpoints/memberships/apiv3-memberships-paths'; +import { Apiv3UsersPaths } from 'core-app/core/apiv3/endpoints/users/apiv3-users-paths'; +import { APIv3TypesPaths } from 'core-app/core/apiv3/endpoints/types/apiv3-types-paths'; +import { APIv3QueriesPaths } from 'core-app/core/apiv3/endpoints/queries/apiv3-queries-paths'; +import { APIV3WorkPackagesPaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths'; +import { APIv3ProjectPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-project-paths'; +import { APIv3ProjectsPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-projects-paths'; +import { APIv3StatusesPaths } from 'core-app/core/apiv3/endpoints/statuses/apiv3-statuses-paths'; +import { APIv3RolesPaths } from 'core-app/core/apiv3/endpoints/roles/apiv3-roles-paths'; +import { APIv3VersionsPaths } from 'core-app/core/apiv3/endpoints/versions/apiv3-versions-paths'; +import { Apiv3RelationsPaths } from 'core-app/core/apiv3/endpoints/relations/apiv3-relations-paths'; +import { Apiv3NewsPaths } from 'core-app/core/apiv3/endpoints/news/apiv3-news-paths'; +import { Apiv3HelpTextsPaths } from 'core-app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths'; +import { Apiv3ConfigurationPath } from 'core-app/core/apiv3/endpoints/configuration/apiv3-configuration-path'; +import { Apiv3BoardsPaths } from 'core-app/core/apiv3/virtual/apiv3-boards-paths'; +import { RootResource } from 'core-app/features/hal/resources/root-resource'; +import { Apiv3PlaceholderUsersPaths } from 'core-app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths'; +import { Apiv3GroupsPaths } from 'core-app/core/apiv3/endpoints/groups/apiv3-groups-paths'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Apiv3NotificationsPaths } from 'core-app/core/apiv3/endpoints/notifications/apiv3-notifications-paths'; @Injectable({ providedIn: 'root' }) export class APIV3Service { @@ -144,7 +138,7 @@ export class APIV3Service { public readonly boards = this.apiV3CustomEndpoint(Apiv3BoardsPaths); constructor(readonly injector:Injector, - readonly pathHelper:PathHelperService) { + readonly pathHelper:PathHelperService) { } /** @@ -159,13 +153,12 @@ export class APIV3Service { public withOptionalProject(projectIdentifier:string|number|null|undefined):APIv3ProjectPaths|this { if (_.isNil(projectIdentifier)) { return this; - } else { - return this.projects.id(projectIdentifier); } + return this.projects.id(projectIdentifier); } public collectionFromString(fullPath:string) { - const path = fullPath.replace(this.pathHelper.api.v3.apiV3Base + '/', ''); + const path = fullPath.replace(`${this.pathHelper.api.v3.apiV3Base}/`, ''); return this.apiV3CollectionEndpoint(path); } diff --git a/frontend/src/app/core/apiv3/cache/cachable-apiv3-collection.ts b/frontend/src/app/core/apiv3/cache/cachable-apiv3-collection.ts index cb882d00a5..6c67e07bb9 100644 --- a/frontend/src/app/core/apiv3/cache/cachable-apiv3-collection.ts +++ b/frontend/src/app/core/apiv3/cache/cachable-apiv3-collection.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { HasId, StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { tap } from "rxjs/operators"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { HasId, StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { tap } from 'rxjs/operators'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export abstract class CachableAPIV3Collection< T extends HasId = HalResource, V extends APIv3GettableResource = APIv3GettableResource, - X extends StateCacheService = StateCacheService + X extends StateCacheService = StateCacheService, > extends APIv3ResourceCollection { @InjectField() states:States; @@ -51,23 +51,22 @@ export abstract class CachableAPIV3Collection< public observeAll():Observable { return this.cache.observeAll(); } + /** * Inserts a collection or single response to cache as an rxjs tap function */ protected cacheResponse():(source:Observable) => Observable { - return (source$) => { - return source$.pipe( - tap( - (response:R) => { - if (response instanceof CollectionResource) { - response.elements.forEach(this.touch.bind(this)); - } else if (response instanceof HalResource) { - this.touch(response as any); - } + return (source$) => source$.pipe( + tap( + (response:R) => { + if (response instanceof CollectionResource) { + response.elements.forEach(this.touch.bind(this)); + } else if (response instanceof HalResource) { + this.touch(response as any); } - ) - ); - }; + }, + ), + ); } /** diff --git a/frontend/src/app/core/apiv3/cache/cachable-apiv3-resource.ts b/frontend/src/app/core/apiv3/cache/cachable-apiv3-resource.ts index 60dafb9b57..cd4b04525a 100644 --- a/frontend/src/app/core/apiv3/cache/cachable-apiv3-resource.ts +++ b/frontend/src/app/core/apiv3/cache/cachable-apiv3-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { HasId, StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { concat, from, merge, Observable, of } from "rxjs"; -import { mapTo, publish, share, shareReplay, switchMap, take, tap } from "rxjs/operators"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { HasId, StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { concat, Observable, of } from 'rxjs'; +import { + mapTo, shareReplay, switchMap, take, tap, +} from 'rxjs/operators'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export abstract class CachableAPIV3Resource extends APIv3GettableResource { @InjectField() states:States; + @InjectField() schemaCache:SchemaCacheService; readonly cache = this.createCache(); @@ -59,12 +62,12 @@ export abstract class CachableAPIV3Resource .load() .pipe( take(1), - shareReplay(1) + shareReplay(1), ); this.cache.clearAndLoad( id, - observable + observable, ); // Return concat of the loading observable @@ -72,14 +75,13 @@ export abstract class CachableAPIV3Resource // but then continue with the streamed cache return concat( observable, - this.cache.state(id).values$() + this.cache.state(id).values$(), ); } return this.cache.state(id).values$(); } - /** * Observe the values of this resource, * but do not request it actively. @@ -90,7 +92,6 @@ export abstract class CachableAPIV3Resource .observe(this.id.toString()); } - /** * Returns a (potentially cached) observable. * @@ -102,7 +103,7 @@ export abstract class CachableAPIV3Resource return this .requireAndStream(false) .pipe( - take(1) + take(1), ); } @@ -141,10 +142,9 @@ export abstract class CachableAPIV3Resource take(1), mapTo(resource), ); - } else { - return of(resource); } - }) + return of(resource); + }), ) as any; // T does not extend HalResource for virtual endpoints such as board, thus we need to cast here } @@ -159,13 +159,11 @@ export abstract class CachableAPIV3Resource * Inserts a collection response to cache as an rxjs tap function */ protected cacheResponse():(source:Observable) => Observable { - return (source$:Observable) => { - return source$.pipe( - tap( - (resource:T) => this.touch(resource) - ) - ); - }; + return (source$:Observable) => source$.pipe( + tap( + (resource:T) => this.touch(resource), + ), + ); } /** diff --git a/frontend/src/app/core/apiv3/cache/state-cache.service.ts b/frontend/src/app/core/apiv3/cache/state-cache.service.ts index a381f81196..ae5c0764ca 100644 --- a/frontend/src/app/core/apiv3/cache/state-cache.service.ts +++ b/frontend/src/app/core/apiv3/cache/state-cache.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,10 @@ //++ import { MultiInputState, State } from 'reactivestates'; -import { Observable } from "rxjs"; -import { auditTime, map, share, startWith, take } from "rxjs/operators"; +import { Observable } from 'rxjs'; +import { + auditTime, map, share, startWith, take, +} from 'rxjs/operators'; export interface HasId { id:string|null; @@ -36,6 +38,7 @@ export interface HasId { export class StateCacheService { protected cacheDurationInMs:number; + protected multiState:MultiInputState; constructor(state:MultiInputState, holdValuesForSeconds = 3600) { @@ -66,12 +69,11 @@ export class StateCacheService { * Sets a promise to the state */ public clearAndLoad(id:string, loader:Observable):Observable { - const observable = - loader - .pipe( - take(1), - share() - ); + const observable = loader + .pipe( + take(1), + share(), + ); this .multiState.get(id) @@ -102,7 +104,6 @@ export class StateCacheService { return this.updateValue(resource.id!, resource as any); } - /** * Observe the value of the given id */ @@ -135,7 +136,7 @@ export class StateCacheService { }); return mapped; - }) + }), ); } @@ -144,7 +145,7 @@ export class StateCacheService { * @param ids */ public clearSome(...ids:string[]) { - ids.forEach(id => this.multiState.get(id).clear()); + ids.forEach((id) => this.multiState.get(id).clear()); } /** @@ -173,4 +174,3 @@ export class StateCacheService { this.multiState.get(id).putValue(val); } } - diff --git a/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths.ts b/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths.ts index 01875f8e45..e2bc891609 100644 --- a/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,27 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Apiv3CapabilityPaths } from "core-app/core/apiv3/endpoints/capabilities/apiv3-capability-paths"; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { MultiInputState } from "reactivestates"; +import { Apiv3CapabilityPaths } from 'core-app/core/apiv3/endpoints/capabilities/apiv3-capability-paths'; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { CapabilityCacheService } from "core-app/core/apiv3/endpoints/capabilities/capability-cache.service"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { CapabilityCacheService } from 'core-app/core/apiv3/endpoints/capabilities/capability-cache.service'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class Apiv3CapabilitiesPaths extends CachableAPIV3Collection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'capabilities', Apiv3CapabilityPaths); } @@ -59,7 +57,7 @@ export class Apiv3CapabilitiesPaths .halResourceService .get>(this.path + listParamsString(params)) .pipe( - this.cacheResponse() + this.cacheResponse(), ); } diff --git a/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capability-paths.ts b/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capability-paths.ts index c4d672c97e..7ca47673b0 100644 --- a/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capability-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/capabilities/apiv3-capability-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Apiv3CapabilitiesPaths } from "core-app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths"; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Apiv3CapabilitiesPaths } from 'core-app/core/apiv3/endpoints/capabilities/apiv3-capabilities-paths'; export class Apiv3CapabilityPaths extends CachableAPIV3Resource { protected createCache():StateCacheService { diff --git a/frontend/src/app/core/apiv3/endpoints/capabilities/capability-cache.service.ts b/frontend/src/app/core/apiv3/endpoints/capabilities/capability-cache.service.ts index db8d50e392..d94ae052ca 100644 --- a/frontend/src/app/core/apiv3/endpoints/capabilities/capability-cache.service.ts +++ b/frontend/src/app/core/apiv3/endpoints/capabilities/capability-cache.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { States } from "core-app/core/states/states.service"; -import { Injector } from "@angular/core"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { MultiInputState } from "reactivestates"; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { Injector } from '@angular/core'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { MultiInputState } from 'reactivestates'; export class CapabilityCacheService extends StateCacheService { @InjectField() readonly states:States; diff --git a/frontend/src/app/core/apiv3/endpoints/configuration/apiv3-configuration-path.ts b/frontend/src/app/core/apiv3/endpoints/configuration/apiv3-configuration-path.ts index b33625dc76..477c8aac24 100644 --- a/frontend/src/app/core/apiv3/endpoints/configuration/apiv3-configuration-path.ts +++ b/frontend/src/app/core/apiv3/endpoints/configuration/apiv3-configuration-path.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,24 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { ConfigurationResource } from "core-app/features/hal/resources/configuration-resource"; -import { Observable } from "rxjs"; -import { shareReplay } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { ConfigurationResource } from 'core-app/features/hal/resources/configuration-resource'; +import { Observable } from 'rxjs'; +import { shareReplay } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export class Apiv3ConfigurationPath extends APIv3GettableResource { private $configuration:Observable; constructor(protected apiRoot:APIV3Service, - readonly basePath:string) { + readonly basePath:string) { super(apiRoot, basePath, 'configuration'); } - - public get():Observable { if (this.$configuration) { return this.$configuration; @@ -52,7 +48,7 @@ export class Apiv3ConfigurationPath extends APIv3GettableResource(this.path) .pipe( - shareReplay() + shareReplay(), ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-form.ts b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-form.ts index 55e584b9ac..722bee191b 100644 --- a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-form.ts +++ b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-form.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { HalPayloadHelper } from "core-app/features/hal/schemas/hal-payload.helper"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { HalPayloadHelper } from 'core-app/features/hal/schemas/hal-payload.helper'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class Apiv3GridForm extends APIv3FormResource { - /** * We need to override the grid widget extraction * to pass the correct payload to the API. @@ -43,23 +42,21 @@ export class Apiv3GridForm extends APIv3FormResource { */ public static extractPayload(resource:HalResource|Object, schema:SchemaResource|null = null):Object { if (resource instanceof HalResource && schema) { - const grid = resource as HalResource; + const grid = resource; const payload = HalPayloadHelper.extractPayloadFromSchema(grid, schema); // The widget only states the type of the widget resource but does not explain // the widget itself. We therefore have to do that by hand. if (payload.widgets) { - payload.widgets = grid.widgets.map((widget:GridWidgetResource) => { - return { - id: widget.id, - startRow: widget.startRow, - endRow: widget.endRow, - startColumn: widget.startColumn, - endColumn: widget.endColumn, - identifier: widget.identifier, - options: widget.options - }; - }); + payload.widgets = grid.widgets.map((widget:GridWidgetResource) => ({ + id: widget.id, + startRow: widget.startRow, + endRow: widget.endRow, + startColumn: widget.startColumn, + endColumn: widget.endColumn, + identifier: widget.identifier, + options: widget.options, + })); } return payload; @@ -77,5 +74,4 @@ export class Apiv3GridForm extends APIv3FormResource { public extractPayload(request:HalResource|Object, schema:SchemaResource|null = null) { return Apiv3GridForm.extractPayload(request, schema); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-paths.ts b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-paths.ts index 34d2766688..29e03864b3 100644 --- a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grid-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { Observable } from "rxjs"; -import { Apiv3GridForm } from "core-app/core/apiv3/endpoints/grids/apiv3-grid-form"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { Observable } from 'rxjs'; +import { Apiv3GridForm } from 'core-app/core/apiv3/endpoints/grids/apiv3-grid-form'; export class Apiv3GridPaths extends APIv3GettableResource { // Static paths diff --git a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grids-paths.ts b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grids-paths.ts index 830b3c9dc9..1112807e20 100644 --- a/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grids-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/grids/apiv3-grids-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Apiv3GridPaths } from "core-app/core/apiv3/endpoints/grids/apiv3-grid-paths"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { Apiv3GridForm } from "core-app/core/apiv3/endpoints/grids/apiv3-grid-form"; -import { Observable } from "rxjs"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Apiv3GridPaths } from 'core-app/core/apiv3/endpoints/grids/apiv3-grid-paths'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { Apiv3GridForm } from 'core-app/core/apiv3/endpoints/grids/apiv3-grid-form'; +import { Observable } from 'rxjs'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class Apiv3GridsPaths extends APIv3ResourceCollection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'grids', Apiv3GridPaths); } @@ -70,8 +70,8 @@ export class Apiv3GridsPaths return this .halResourceService .post( - this.path, - this.form.extractPayload(resource, schema) - ); + this.path, + this.form.extractPayload(resource, schema), + ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/groups/apiv3-group-paths.ts b/frontend/src/app/core/apiv3/endpoints/groups/apiv3-group-paths.ts index 26ff7a84a4..471aa4f85e 100644 --- a/frontend/src/app/core/apiv3/endpoints/groups/apiv3-group-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/groups/apiv3-group-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { GroupResource } from "core-app/features/hal/resources/group-resource"; -import { Observable } from "rxjs"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { GroupResource } from 'core-app/features/hal/resources/group-resource'; +import { Observable } from 'rxjs'; export class Apiv3GroupPaths extends APIv3GettableResource { /** @@ -39,8 +39,8 @@ export class Apiv3GroupPaths extends APIv3GettableResource { return this .halResourceService .patch(this.path, { - name: resource.name, - }); + name: resource.name, + }); } /** diff --git a/frontend/src/app/core/apiv3/endpoints/groups/apiv3-groups-paths.ts b/frontend/src/app/core/apiv3/endpoints/groups/apiv3-groups-paths.ts index 56c8b6fac7..6b60d39ded 100644 --- a/frontend/src/app/core/apiv3/endpoints/groups/apiv3-groups-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/groups/apiv3-groups-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,23 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Apiv3GroupPaths } from "core-app/core/apiv3/endpoints/groups/apiv3-group-paths"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Apiv3GroupPaths } from 'core-app/core/apiv3/endpoints/groups/apiv3-group-paths'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { GroupResource } from "core-app/features/hal/resources/group-resource"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { GroupResource } from 'core-app/features/hal/resources/group-resource'; export class Apiv3GroupsPaths extends APIv3ResourceCollection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'groups', Apiv3GroupPaths); } @@ -65,8 +65,8 @@ export class Apiv3GroupsPaths return this .halResourceService .post( - this.path, - resource, - ); + this.path, + resource, + ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths.ts b/frontend/src/app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths.ts index 0939ba04f6..9c5d7a7300 100644 --- a/frontend/src/app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/help_texts/apiv3-help-texts-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; -import { HelpTextResource } from "core-app/features/hal/resources/help-text-resource"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { HelpTextResource } from 'core-app/features/hal/resources/help-text-resource'; export class Apiv3HelpTextsPaths extends APIv3ResourceCollection> { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'help_texts'); } diff --git a/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-form.ts b/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-form.ts index ee3d473e6d..fb03054e5b 100644 --- a/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-form.ts +++ b/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-form.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {APIv3FormResource} from "core-app/core/apiv3/forms/apiv3-form-resource"; -import {SchemaResource} from "core-app/features/hal/resources/schema-resource"; -import {HalPayloadHelper} from "core-app/features/hal/schemas/hal-payload.helper"; -import {HalResource} from "core-app/features/hal/resources/hal-resource"; -import {MembershipResource, MembershipResourceEmbedded} from "core-app/features/hal/resources/membership-resource"; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { MembershipResourceEmbedded } from 'core-app/features/hal/resources/membership-resource'; export class Apiv3MembershipsForm extends APIv3FormResource { - /** * We need to override the grid widget extraction * to pass the correct payload to the API. @@ -46,14 +42,14 @@ export class Apiv3MembershipsForm extends APIv3FormResource { _links: { project: { href: resource.project.href }, principal: { href: resource.principal.href }, - roles: resource.roles.map(role => ({ href: role.href })), + roles: resource.roles.map((role) => ({ href: role.href })), }, _meta: { notificationMessage: { - raw: resource.notificationMessage.raw - } - } - } + raw: resource.notificationMessage.raw, + }, + }, + }; } /** @@ -65,5 +61,4 @@ export class Apiv3MembershipsForm extends APIv3FormResource { public extractPayload(request:MembershipResourceEmbedded) { return Apiv3MembershipsForm.extractPayload(request); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-paths.ts b/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-paths.ts index 06456a1719..3eb33f713c 100644 --- a/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/memberships/apiv3-memberships-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,28 +26,27 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {APIv3GettableResource, APIv3ResourceCollection} from "core-app/core/apiv3/paths/apiv3-resource"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; -import {Apiv3AvailableProjectsPaths} from "core-app/core/apiv3/endpoints/projects/apiv3-available-projects-paths"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Apiv3AvailableProjectsPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-available-projects-paths'; import { Apiv3ListParameters, - Apiv3ListResourceInterface, listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import {Observable} from "rxjs"; -import {Apiv3MembershipsForm} from "core-app/core/apiv3/endpoints/memberships/apiv3-memberships-form"; -import { MembershipResource, MembershipResourceEmbedded } from "core-app/features/hal/resources/membership-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; - + Apiv3ListResourceInterface, + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { Observable } from 'rxjs'; +import { Apiv3MembershipsForm } from 'core-app/core/apiv3/endpoints/memberships/apiv3-memberships-form'; +import { MembershipResource, MembershipResourceEmbedded } from 'core-app/features/hal/resources/membership-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class Apiv3MembershipsPaths extends APIv3ResourceCollection> implements Apiv3ListResourceInterface { - // Static paths readonly form = this.subResource('form', Apiv3MembershipsForm); constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'memberships'); } @@ -61,7 +60,6 @@ export class Apiv3MembershipsPaths .get>(this.path + listParamsString(params)); } - // /api/v3/memberships/available_projects readonly available_projects = this.subResource('available_projects', Apiv3AvailableProjectsPaths); @@ -75,9 +73,8 @@ export class Apiv3MembershipsPaths return this .halResourceService .post( - this.path, - payload, - ); + this.path, + payload, + ); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/news/apiv3-news-paths.ts b/frontend/src/app/core/apiv3/endpoints/news/apiv3-news-paths.ts index c431a50eae..42c4e1fddf 100644 --- a/frontend/src/app/core/apiv3/endpoints/news/apiv3-news-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/news/apiv3-news-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,24 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { NewsResource } from "core-app/features/hal/resources/news-resource"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { NewsResource } from 'core-app/features/hal/resources/news-resource'; export class Apiv3NewsPaths extends APIv3ResourceCollection> implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'news'); } diff --git a/frontend/src/app/core/apiv3/endpoints/notifications/apiv3-notification-paths.ts b/frontend/src/app/core/apiv3/endpoints/notifications/apiv3-notification-paths.ts index 83fe5303c6..92ce808b69 100644 --- a/frontend/src/app/core/apiv3/endpoints/notifications/apiv3-notification-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/notifications/apiv3-notification-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Observable } from "rxjs"; -import { InAppNotification } from "core-app/features/in-app-notifications/store/in-app-notification.model"; -import { HttpClient } from "@angular/common/http"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Observable } from 'rxjs'; +import { InAppNotification } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; +import { HttpClient } from '@angular/common/http'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class Apiv3NotificationPaths extends APIv3GettableResource { @InjectField() http:HttpClient; @@ -39,12 +39,12 @@ export class Apiv3NotificationPaths extends APIv3GettableResource { @InjectField() http:HttpClient; constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'notifications', Apiv3NotificationPaths); } public facet(facet:string, params?:Apiv3ListParameters):Observable> { - if(facet === 'unread') { + if (facet === 'unread') { return this.unread(params); - } else { - return this.list(params); - }; + } + return this.list(params); } /** @@ -70,7 +69,7 @@ export class Apiv3NotificationsPaths public unread(additional?:Apiv3ListParameters):Observable> { const params:Apiv3ListParameters = { ...additional, - filters: [["readIAN", "=", false]] + filters: [['readIAN', '=', false]], }; return this.list(params); @@ -84,12 +83,12 @@ export class Apiv3NotificationsPaths return this .http .post( - this.path + '/read_ian' + listParamsString({ filters: [['id', "=", ids.map(id => id.toString())]] }), + `${this.path}/read_ian${listParamsString({ filters: [['id', '=', ids.map((id) => id.toString())]] })}`, {}, { withCredentials: true, - responseType: 'json' - } + responseType: 'json', + }, ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths.ts b/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths.ts index a70e4adc7a..560dd2ef76 100644 --- a/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { PlaceholderUserResource } from "core-app/features/hal/resources/placeholder-user-resource"; -import { Observable } from "rxjs"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { PlaceholderUserResource } from 'core-app/features/hal/resources/placeholder-user-resource'; +import { Observable } from 'rxjs'; export class Apiv3PlaceholderUserPaths extends APIv3GettableResource { /** @@ -39,8 +39,8 @@ export class Apiv3PlaceholderUserPaths extends APIv3GettableResource(this.path, { - name: resource.name - }); + name: resource.name, + }); } /** diff --git a/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths.ts b/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths.ts index b4bd8d6fa1..ddb80a468c 100644 --- a/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-users-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,23 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Apiv3PlaceholderUserPaths } from "core-app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths"; -import { PlaceholderUserResource } from "core-app/features/hal/resources/placeholder-user-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Apiv3PlaceholderUserPaths } from 'core-app/core/apiv3/endpoints/placeholder-users/apiv3-placeholder-user-paths'; +import { PlaceholderUserResource } from 'core-app/features/hal/resources/placeholder-user-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class Apiv3PlaceholderUsersPaths extends APIv3ResourceCollection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'placeholder_users', Apiv3PlaceholderUserPaths); } @@ -65,8 +65,8 @@ export class Apiv3PlaceholderUsersPaths return this .halResourceService .post( - this.path, - resource, - ); + this.path, + resource, + ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-available-projects-paths.ts b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-available-projects-paths.ts index d80d95ceed..1650d9d0cb 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-available-projects-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-available-projects-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { Observable } from "rxjs"; -import { map } from "rxjs/operators"; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { Apiv3ListParameters, - Apiv3ListResourceInterface, listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { buildApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; + Apiv3ListResourceInterface, + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { buildApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export class Apiv3AvailableProjectsPaths extends APIv3GettableResource> implements Apiv3ListResourceInterface { - /** * Load a list of available projects with a given list parameter filter * @param params @@ -64,12 +64,11 @@ export class Apiv3AvailableProjectsPaths return this .halResourceService .get>( - this.path, - { filters: buildApiV3Filter('id', '=', [projectId]).toJson() } - ) + this.path, + { filters: buildApiV3Filter('id', '=', [projectId]).toJson() }, + ) .pipe( - map(collection => collection.count > 0) + map((collection) => collection.count > 0), ); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-copy-paths.ts b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-copy-paths.ts index 783439954e..3d6aa4c0a5 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-copy-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-copy-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { SimpleResource } from "core-app/core/apiv3/paths/path-resources"; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { SimpleResource } from 'core-app/core/apiv3/paths/path-resources'; export class APIv3ProjectCopyPaths extends SimpleResource { constructor(protected apiRoot:APIV3Service, - public basePath:string) { + public basePath:string) { super(basePath, 'copy'); } diff --git a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-paths.ts b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-paths.ts index 7857941a34..481259110a 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-project-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3QueriesPaths } from "core-app/core/apiv3/endpoints/queries/apiv3-queries-paths"; -import { APIv3TypesPaths } from "core-app/core/apiv3/endpoints/types/apiv3-types-paths"; -import { APIV3WorkPackagesPaths } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { APIv3VersionsPaths } from "core-app/core/apiv3/endpoints/versions/apiv3-versions-paths"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { APIv3ProjectsPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-projects-paths"; -import { APIv3ProjectCopyPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-project-copy-paths"; +import { APIv3QueriesPaths } from 'core-app/core/apiv3/endpoints/queries/apiv3-queries-paths'; +import { APIv3TypesPaths } from 'core-app/core/apiv3/endpoints/types/apiv3-types-paths'; +import { APIV3WorkPackagesPaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { APIv3VersionsPaths } from 'core-app/core/apiv3/endpoints/versions/apiv3-versions-paths'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { APIv3ProjectsPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-projects-paths'; +import { APIv3ProjectCopyPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-project-copy-paths'; export class APIv3ProjectPaths extends CachableAPIV3Resource { // /api/v3/projects/:project_id/available_assignees diff --git a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-projects-paths.ts b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-projects-paths.ts index ca25f3f1d9..5a8c78de37 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/apiv3-projects-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/apiv3-projects-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ProjectPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-project-paths"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { APIv3ProjectPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-project-paths'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { Observable } from "rxjs"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { ProjectCache } from "core-app/core/apiv3/endpoints/projects/project.cache"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { Observable } from 'rxjs'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { ProjectCache } from 'core-app/core/apiv3/endpoints/projects/project.cache'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class APIv3ProjectsPaths extends CachableAPIV3Collection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'projects', APIv3ProjectPaths); } @@ -62,7 +62,7 @@ export class APIv3ProjectsPaths .halResourceService .get>(this.path + listParamsString(params)) .pipe( - this.cacheResponse() + this.cacheResponse(), ); } diff --git a/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts b/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts index 4920f62861..7679e03dce 100644 --- a/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts +++ b/frontend/src/app/core/apiv3/endpoints/projects/project.cache.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,13 +27,11 @@ //++ import { MultiInputState } from 'reactivestates'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; import { Injectable, Injector } from '@angular/core'; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; @Injectable() export class ProjectCache extends StateCacheService { diff --git a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-queries-paths.ts b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-queries-paths.ts index 835c611df3..c13e0e3603 100644 --- a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-queries-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-queries-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,24 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { APIv3QueryPaths } from "core-app/core/apiv3/endpoints/queries/apiv3-query-paths"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Apiv3QueryForm } from "core-app/core/apiv3/endpoints/queries/apiv3-query-form"; -import { Observable } from "rxjs"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { Apiv3ListParameters, listParamsString } from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { QueryFiltersService } from "core-app/features/work-packages/components/wp-query/query-filters.service"; -import { HalPayloadHelper } from "core-app/features/hal/schemas/hal-payload.helper"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { APIv3QueryPaths } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-paths'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Apiv3QueryForm } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-form'; +import { Observable } from 'rxjs'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { Apiv3ListParameters, listParamsString } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { QueryFiltersService } from 'core-app/features/work-packages/components/wp-query/query-filters.service'; +import { HalPayloadHelper } from 'core-app/features/hal/schemas/hal-payload.helper'; export class APIv3QueriesPaths extends APIv3ResourceCollection { @InjectField() private queryFilters:QueryFiltersService; constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'queries', APIv3QueryPaths); } @@ -88,7 +88,6 @@ export class APIv3QueriesPaths extends APIv3ResourceCollection(path, queryData); } - /** * Stream the response for the given query request * @@ -97,9 +96,9 @@ export class APIv3QueriesPaths extends APIv3ResourceCollection { return this.halResourceService .get( - this.default.path, - params - ); + this.default.path, + params, + ); } /** @@ -118,8 +117,8 @@ export class APIv3QueriesPaths extends APIv3ResourceCollection( - this.apiRoot.queries.path, payload - ); + this.apiRoot.queries.path, payload, + ); } /** @@ -130,9 +129,8 @@ export class APIv3QueriesPaths extends APIv3ResourceCollection { if (query.starred) { return query.unstar(); - } else { - return query.star(); } + return query.star(); } /** @@ -142,7 +140,7 @@ export class APIv3QueriesPaths extends APIv3ResourceCollection> { const listParams:Apiv3ListParameters = { - filters: [['hidden', '=', ['f']]] + filters: [['hidden', '=', ['f']]], }; if (projectIdentifier) { diff --git a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-form.ts b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-form.ts index d3e036095f..2ffff3dc72 100644 --- a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-form.ts +++ b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-form.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { Observable } from "rxjs"; -import * as URI from "urijs"; -import { map, tap } from "rxjs/operators"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { QueryFiltersService } from "core-app/features/work-packages/components/wp-query/query-filters.service"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { Observable } from 'rxjs'; +import * as URI from 'urijs'; +import { map, tap } from 'rxjs/operators'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { QueryFiltersService } from 'core-app/features/work-packages/components/wp-query/query-filters.service'; export class Apiv3QueryForm extends APIv3FormResource { @InjectField() private queryFilters:QueryFiltersService; @@ -47,23 +47,23 @@ export class Apiv3QueryForm extends APIv3FormResource { // can check whether form saving is possible. // The query needs a name to be valid. const payload:any = { - 'name': query.name || '!!!__O__o__O__!!!' + name: query.name || '!!!__O__o__O__!!!', }; if (query.project) { - payload['_links'] = { - 'project': { - 'href': query.project.href - } + payload._links = { + project: { + href: query.project.href, + }, }; } - const path = this.apiRoot.queries.withOptionalId(query.id).form.path; + const { path } = this.apiRoot.queries.withOptionalId(query.id).form; return this.halResourceService .post(path, payload) .pipe( - tap(form => this.queryFilters.setSchemas(form.$embedded.schema.$embedded.filtersSchemas)), - map(form => [form, this.buildQueryResource(form)]) + tap((form) => this.queryFilters.setSchemas(form.$embedded.schema.$embedded.filtersSchemas)), + map((form) => [form, this.buildQueryResource(form)]), ); } @@ -75,7 +75,7 @@ export class Apiv3QueryForm extends APIv3FormResource { * @param projectIdentifier * @param payload */ - public loadWithParams(params:{[key:string]:unknown}, queryId:string|undefined, projectIdentifier:string|undefined|null, payload:any = {}):Observable<[QueryFormResource, QueryResource]> { + public loadWithParams(params:{ [key:string]:unknown }, queryId:string|undefined, projectIdentifier:string|undefined|null, payload:any = {}):Observable<[QueryFormResource, QueryResource]> { // We need a valid payload so that we // can check whether form saving is possible. // The query needs a name to be valid. @@ -86,18 +86,17 @@ export class Apiv3QueryForm extends APIv3FormResource { if (projectIdentifier) { payload._links = payload._links || {}; payload._links.project = { - 'href': this.apiRoot.projects.id(projectIdentifier).toString() + href: this.apiRoot.projects.id(projectIdentifier).toString(), }; - } - const path = this.apiRoot.queries.withOptionalId(queryId).form.path; + const { path } = this.apiRoot.queries.withOptionalId(queryId).form; const href = URI(path).search(params).toString(); return this.halResourceService .post(href, payload) .pipe( - tap(form => this.queryFilters.setSchemas(form.$embedded.schema.$embedded.filtersSchemas)), - map(form => [form, this.buildQueryResource(form)]) + tap((form) => this.queryFilters.setSchemas(form.$embedded.schema.$embedded.filtersSchemas)), + map((form) => [form, this.buildQueryResource(form)]), ); } diff --git a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-order.ts b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-order.ts index 2bb1d5e571..42621fa656 100644 --- a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-order.ts +++ b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-order.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injector } from "@angular/core"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HttpClient } from "@angular/common/http"; -import { SimpleResource } from "core-app/core/apiv3/paths/path-resources"; +import { Injector } from '@angular/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HttpClient } from '@angular/common/http'; +import { SimpleResource } from 'core-app/core/apiv3/paths/path-resources'; export type QueryOrder = { [wpId:string]:number }; @@ -37,34 +37,34 @@ export class APIV3QueryOrder extends SimpleResource { @InjectField() http:HttpClient; constructor(readonly injector:Injector, - readonly basePath:string, - readonly id:string|number) { + readonly basePath:string, + readonly id:string|number) { super(basePath, id); } public get():Promise { return this.http .get( - this.path - ) + this.path, + ) .toPromise() - .then(result => result || {}); + .then((result) => result || {}); } public update(delta:QueryOrder):Promise { return this.http .patch( this.path, - { delta: delta }, - { withCredentials: true } + { delta }, + { withCredentials: true }, ) .toPromise() - .then((response:{t:string}) => response.t); + .then((response:{ t:string }) => response.t); } public delete(id:string, ...wpIds:string[]) { const delta:QueryOrder = {}; - wpIds.forEach(id => delta[id] = -1); + wpIds.forEach((id) => delta[id] = -1); return this.update(delta); } diff --git a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-paths.ts b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-paths.ts index 439ce33cfb..b1dcc1764f 100644 --- a/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/queries/apiv3-query-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { APIV3QueryOrder } from "core-app/core/apiv3/endpoints/queries/apiv3-query-order"; -import { Apiv3QueryForm } from "core-app/core/apiv3/endpoints/queries/apiv3-query-form"; -import { Observable } from "rxjs"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { QueryFiltersService } from "core-app/features/work-packages/components/wp-query/query-filters.service"; -import { HalPayloadHelper } from "core-app/features/hal/schemas/hal-payload.helper"; -import { PaginationObject } from "core-app/shared/components/table-pagination/pagination-service"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { APIV3QueryOrder } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-order'; +import { Apiv3QueryForm } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-form'; +import { Observable } from 'rxjs'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { QueryFiltersService } from 'core-app/features/work-packages/components/wp-query/query-filters.service'; +import { HalPayloadHelper } from 'core-app/features/hal/schemas/hal-payload.helper'; +import { PaginationObject } from 'core-app/shared/components/table-pagination/pagination-service'; export class APIv3QueryPaths extends APIv3GettableResource { @InjectField() private queryFilters:QueryFiltersService; @@ -88,5 +88,4 @@ export class APIv3QueryPaths extends APIv3GettableResource { public paginated(pagination:PaginationObject):Observable { return this.parameterised(pagination); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/relations/apiv3-relations-paths.ts b/frontend/src/app/core/apiv3/endpoints/relations/apiv3-relations-paths.ts index b4177e7a05..85e57039a7 100644 --- a/frontend/src/app/core/apiv3/endpoints/relations/apiv3-relations-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/relations/apiv3-relations-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { from, Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { map } from "rxjs/operators"; -import { buildApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { APIv3GettableResource, APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { from, Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { map } from 'rxjs/operators'; +import { buildApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export class Apiv3RelationsPaths extends APIv3ResourceCollection> { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'relations'); } @@ -50,7 +50,7 @@ export class Apiv3RelationsPaths extends APIv3ResourceCollection { - const validIds = _.filter(workPackageIds, id => /\d+/.test(id)); + const validIds = _.filter(workPackageIds, (id) => /\d+/.test(id)); if (validIds.length === 0) { return from([]); @@ -60,7 +60,7 @@ export class Apiv3RelationsPaths extends APIv3ResourceCollection { - protected createCache():StateCacheService { return new StateCacheService(this.states.roles); } diff --git a/frontend/src/app/core/apiv3/endpoints/roles/apiv3-roles-paths.ts b/frontend/src/app/core/apiv3/endpoints/roles/apiv3-roles-paths.ts index 5b11474df5..068e89a112 100644 --- a/frontend/src/app/core/apiv3/endpoints/roles/apiv3-roles-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/roles/apiv3-roles-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection, APIv3ResourcePath } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Injector } from "@angular/core"; -import { RoleResource } from "core-app/features/hal/resources/role-resource"; -import { APIv3RolePaths } from "core-app/core/apiv3/endpoints/roles/apiv3-role-paths"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { tap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { APIv3RolePaths } from 'core-app/core/apiv3/endpoints/roles/apiv3-role-paths'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { tap } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export class APIv3RolesPaths extends APIv3ResourceCollection { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'roles', APIv3RolePaths); } @@ -49,12 +48,11 @@ export class APIv3RolesPaths extends APIv3ResourceCollection>(this.path) .pipe( - tap(collection => { + tap((collection) => { collection.elements.forEach((resource, id) => { this.id(resource.id!).cache.updateValue(resource.id!, resource); }); - }) + }), ); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-status-paths.ts b/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-status-paths.ts index 3594e7cc63..52aa9b3a0d 100644 --- a/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-status-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-status-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { StatusResource } from "core-app/features/hal/resources/status-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class APIv3StatusPaths extends CachableAPIV3Resource { - protected createCache():StateCacheService { return new StateCacheService(this.states.statuses); } diff --git a/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-statuses-paths.ts b/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-statuses-paths.ts index 579bca0f94..c812559ac2 100644 --- a/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-statuses-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/statuses/apiv3-statuses-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection, APIv3ResourcePath } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Injector } from "@angular/core"; -import { StatusResource } from "core-app/features/hal/resources/status-resource"; -import { APIv3StatusPaths } from "core-app/core/apiv3/endpoints/statuses/apiv3-status-paths"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { tap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; +import { APIv3StatusPaths } from 'core-app/core/apiv3/endpoints/statuses/apiv3-status-paths'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { tap } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export class APIv3StatusesPaths extends APIv3ResourceCollection { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'statuses', APIv3StatusPaths); } @@ -49,12 +48,11 @@ export class APIv3StatusesPaths extends APIv3ResourceCollection>(this.path) .pipe( - tap(collection => { + tap((collection) => { collection.elements.forEach((resource, id) => { this.id(resource.id!).cache.updateValue(resource.id!, resource); }); - }) + }), ); } - } diff --git a/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths.ts b/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths.ts index 0d689980d4..2d1f47d5c8 100644 --- a/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,28 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Apiv3TimeEntryPaths } from "core-app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { Observable } from "rxjs"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { MultiInputState } from "reactivestates"; +import { Apiv3TimeEntryPaths } from 'core-app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { Observable } from 'rxjs'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; import { Apiv3ListParameters, Apiv3ListResourceInterface, - listParamsString -} from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { TimeEntryCacheService } from "core-app/core/apiv3/endpoints/time-entries/time-entry-cache.service"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; + listParamsString, +} from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { TimeEntryCacheService } from 'core-app/core/apiv3/endpoints/time-entries/time-entry-cache.service'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class Apiv3TimeEntriesPaths extends CachableAPIV3Collection implements Apiv3ListResourceInterface { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'time_entries', Apiv3TimeEntryPaths); } @@ -63,7 +61,7 @@ export class Apiv3TimeEntriesPaths .halResourceService .get>(this.path + listParamsString(params)) .pipe( - this.cacheResponse() + this.cacheResponse(), ); } @@ -76,7 +74,7 @@ export class Apiv3TimeEntriesPaths .halResourceService .post(this.path, payload) .pipe( - this.cacheResponse() + this.cacheResponse(), ); } diff --git a/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths.ts b/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths.ts index 5be1e8d0d2..119bb483e3 100644 --- a/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/time-entries/apiv3-time-entry-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { Observable } from "rxjs"; -import { tap } from "rxjs/operators"; -import { Apiv3TimeEntriesPaths } from "core-app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths"; -import { HalPayloadHelper } from "core-app/features/hal/schemas/hal-payload.helper"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { Apiv3TimeEntriesPaths } from 'core-app/core/apiv3/endpoints/time-entries/apiv3-time-entries-paths'; +import { HalPayloadHelper } from 'core-app/features/hal/schemas/hal-payload.helper'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class Apiv3TimeEntryPaths extends CachableAPIV3Resource { // Static paths @@ -54,7 +54,7 @@ export class Apiv3TimeEntryPaths extends CachableAPIV3Resource(this.path, this.extractPayload(payload, schema)) .pipe( - tap(resource => this.touch(resource)) + tap((resource) => this.touch(resource)), ); } @@ -66,7 +66,7 @@ export class Apiv3TimeEntryPaths extends CachableAPIV3Resource(this.path) .pipe( - tap(() => this.cache.clearSome(this.id.toString())) + tap(() => this.cache.clearSome(this.id.toString())), ); } @@ -84,10 +84,9 @@ export class Apiv3TimeEntryPaths extends CachableAPIV3Resource { @InjectField() readonly states:States; + @InjectField() readonly schemaCache:SchemaCacheService; constructor(readonly injector:Injector, state:MultiInputState) { diff --git a/frontend/src/app/core/apiv3/endpoints/types/apiv3-type-paths.ts b/frontend/src/app/core/apiv3/endpoints/types/apiv3-type-paths.ts index fb47b6e3fc..36f48bc730 100644 --- a/frontend/src/app/core/apiv3/endpoints/types/apiv3-type-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/types/apiv3-type-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { APIv3TypesPaths } from "core-app/core/apiv3/endpoints/types/apiv3-types-paths"; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { APIv3TypesPaths } from 'core-app/core/apiv3/endpoints/types/apiv3-types-paths'; export class APIv3TypePaths extends CachableAPIV3Resource { - protected createCache():StateCacheService { return (this.parent as APIv3TypesPaths).cache; } diff --git a/frontend/src/app/core/apiv3/endpoints/types/apiv3-types-paths.ts b/frontend/src/app/core/apiv3/endpoints/types/apiv3-types-paths.ts index e0e3df6a7f..648de19834 100644 --- a/frontend/src/app/core/apiv3/endpoints/types/apiv3-types-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/types/apiv3-types-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { APIv3TypePaths } from "core-app/core/apiv3/endpoints/types/apiv3-type-paths"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { APIv3TypePaths } from 'core-app/core/apiv3/endpoints/types/apiv3-type-paths'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class APIv3TypesPaths extends CachableAPIV3Collection { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'types', APIv3TypePaths); } diff --git a/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-paths.ts b/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-paths.ts index 760eb56abb..ddabb06b2a 100644 --- a/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Apiv3UserPreferencesPaths } from "core-app/core/apiv3/endpoints/users/apiv3-user-preferences-paths"; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Apiv3UserPreferencesPaths } from 'core-app/core/apiv3/endpoints/users/apiv3-user-preferences-paths'; export class APIv3UserPaths extends CachableAPIV3Resource { - readonly avatar = this.subResource('avatar'); readonly preferences = this.subResource('preferences', Apiv3UserPreferencesPaths); diff --git a/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-preferences-paths.ts b/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-preferences-paths.ts index 166db6ac26..de77af4907 100644 --- a/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-preferences-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/users/apiv3-user-preferences-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourcePath } from "core-app/core/apiv3/paths/apiv3-resource"; -import { Observable } from "rxjs"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HttpClient } from "@angular/common/http"; -import { UserPreferencesModel } from "core-app/features/user-preferences/state/user-preferences.model"; +import { APIv3ResourcePath } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { Observable } from 'rxjs'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HttpClient } from '@angular/common/http'; +import { UserPreferencesModel } from 'core-app/features/user-preferences/state/user-preferences.model'; export class Apiv3UserPreferencesPaths extends APIv3ResourcePath { @InjectField() http:HttpClient; - /** * Perform a request to the backend to load preferences */ @@ -43,8 +42,8 @@ export class Apiv3UserPreferencesPaths extends APIv3ResourcePath( - this.path, - ); + this.path, + ); } /** @@ -54,9 +53,9 @@ export class Apiv3UserPreferencesPaths extends APIv3ResourcePath( - this.path, - payload, - { withCredentials: true, responseType: 'json' } - ); + this.path, + payload, + { withCredentials: true, responseType: 'json' }, + ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/users/apiv3-users-paths.ts b/frontend/src/app/core/apiv3/endpoints/users/apiv3-users-paths.ts index 13600dc461..46acc7262b 100644 --- a/frontend/src/app/core/apiv3/endpoints/users/apiv3-users-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/users/apiv3-users-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { APIv3UserPaths } from "core-app/core/apiv3/endpoints/users/apiv3-user-paths"; -import { Observable } from "rxjs"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { APIv3UserPaths } from 'core-app/core/apiv3/endpoints/users/apiv3-user-paths'; +import { Observable } from 'rxjs'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; export class Apiv3UsersPaths extends APIv3ResourceCollection { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'users', APIv3UserPaths); } @@ -68,8 +68,8 @@ export class Apiv3UsersPaths extends APIv3ResourceCollection( - this.path, - resource, - ); + this.path, + resource, + ); } } diff --git a/frontend/src/app/core/apiv3/endpoints/versions/apiv3-version-paths.ts b/frontend/src/app/core/apiv3/endpoints/versions/apiv3-version-paths.ts index 09dbf5cc55..50a64b6de5 100644 --- a/frontend/src/app/core/apiv3/endpoints/versions/apiv3-version-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/versions/apiv3-version-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { Observable } from "rxjs"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { MultiInputState } from "reactivestates"; -import { tap } from "rxjs/operators"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { Observable } from 'rxjs'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { tap } from 'rxjs/operators'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class APIv3VersionPaths extends CachableAPIV3Resource { - /** * Update a version resource with the given payload * @@ -45,11 +43,11 @@ export class APIv3VersionPaths extends CachableAPIV3Resource { return this .halResourceService .patch( - this.path, - payload - ) + this.path, + payload, + ) .pipe( - tap(version => this.touch(version)) + tap((version) => this.touch(version)), ); } diff --git a/frontend/src/app/core/apiv3/endpoints/versions/apiv3-versions-paths.ts b/frontend/src/app/core/apiv3/endpoints/versions/apiv3-versions-paths.ts index d7937c0e1f..f48ae400ff 100644 --- a/frontend/src/app/core/apiv3/endpoints/versions/apiv3-versions-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/versions/apiv3-versions-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource, APIv3ResourceCollection } from "core-app/core/apiv3/paths/apiv3-resource"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { Observable } from "rxjs"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { Apiv3AvailableProjectsPaths } from "core-app/core/apiv3/endpoints/projects/apiv3-available-projects-paths"; -import { APIv3VersionPaths } from "core-app/core/apiv3/endpoints/versions/apiv3-version-paths"; +import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { Observable } from 'rxjs'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { Apiv3AvailableProjectsPaths } from 'core-app/core/apiv3/endpoints/projects/apiv3-available-projects-paths'; +import { APIv3VersionPaths } from 'core-app/core/apiv3/endpoints/versions/apiv3-version-paths'; export class APIv3VersionsPaths extends APIv3ResourceCollection { constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'versions', APIv3VersionPaths); } diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource.ts index 46fd8264e2..869370ed1b 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { Observable } from "rxjs"; -import { APIV3WorkPackagesPaths } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths"; -import { take, tap } from "rxjs/operators"; -import { WorkPackageCache } from "core-app/core/apiv3/endpoints/work_packages/work-package.cache"; -import { States } from "core-app/core/states/states.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { Observable } from 'rxjs'; +import { APIV3WorkPackagesPaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths'; +import { take, tap } from 'rxjs/operators'; +import { WorkPackageCache } from 'core-app/core/apiv3/endpoints/work_packages/work-package.cache'; +import { States } from 'core-app/core/states/states.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export class ApiV3WorkPackageCachedSubresource extends APIv3GettableResource { @InjectField() private states:States; @@ -45,9 +45,9 @@ export class ApiV3WorkPackageCachedSubresource extends APIv3GettableResource(this.path) .pipe( - tap(collection => collection.schemas && this.updateSchemas(collection.schemas)), - tap(collection => this.cache.updateWorkPackageList(collection.elements)), - take(1) + tap((collection) => collection.schemas && this.updateSchemas(collection.schemas)), + tap((collection) => this.cache.updateWorkPackageList(collection.elements)), + take(1), ); } @@ -56,7 +56,7 @@ export class ApiV3WorkPackageCachedSubresource extends APIv3GettableResource) { - schemas.elements.forEach(schema => { + schemas.elements.forEach((schema) => { this.states.schemas.get(schema.href as string).putValue(schema); }); } diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts index 4ff74d29f0..0c9295b0f0 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Apiv3RelationsPaths } from "core-app/core/apiv3/endpoints/relations/apiv3-relations-paths"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { APIV3WorkPackagesPaths } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Apiv3RelationsPaths } from 'core-app/core/apiv3/endpoints/relations/apiv3-relations-paths'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { APIV3WorkPackagesPaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class APIV3WorkPackagePaths extends CachableAPIV3Resource { - // /api/v3/(?:projectPath)/work_packages/(:workPackageId)/relations public readonly relations = this.subResource('relations', Apiv3RelationsPaths); diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts index d58e2749b6..e4683b68ce 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/api-v3-work-packages-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,29 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { APIV3WorkPackagePaths } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { Observable } from "rxjs"; -import { APIv3WorkPackageForm } from "core-app/core/apiv3/endpoints/work_packages/apiv3-work-package-form"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { WorkPackageCache } from "core-app/core/apiv3/endpoints/work_packages/work-package.cache"; -import { APIv3GettableResource } from "core-app/core/apiv3/paths/apiv3-resource"; -import { ApiV3WorkPackageCachedSubresource } from "core-app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource"; -import { ApiV3FilterBuilder, buildApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; - +import { Observable } from 'rxjs'; +import { APIV3WorkPackagePaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { APIv3WorkPackageForm } from 'core-app/core/apiv3/endpoints/work_packages/apiv3-work-package-form'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { WorkPackageCache } from 'core-app/core/apiv3/endpoints/work_packages/work-package.cache'; +import { APIv3GettableResource } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { ApiV3WorkPackageCachedSubresource } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-package-cached-subresource'; +import { + ApiV3FilterBuilder, + ApiV3FilterValueType, + buildApiV3Filter, +} from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export class APIV3WorkPackagesPaths extends CachableAPIV3Collection { // Base path public readonly path:string; constructor(readonly apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'work_packages', APIV3WorkPackagePaths); } @@ -75,7 +78,6 @@ export class APIV3WorkPackagesPaths extends CachableAPIV3Collection(this.path, payload) .pipe( - this.cacheResponse() + this.cacheResponse(), ); } @@ -121,7 +123,7 @@ export class APIV3WorkPackagesPaths extends CachableAPIV3Collectiond', [timestamp, '']); const params = { offset: '1', - pageSize: '10' + pageSize: '10', }; return this.filtered(filters, params); @@ -156,12 +158,12 @@ export class APIV3WorkPackagesPaths extends CachableAPIV3Collection( - this.path, - ids.length, - { - filters: buildApiV3Filter('id', '=', ids).toJson(), - } - ); + this.path, + ids.length, + { + filters: buildApiV3Filter('id', '=', ids).toJson(), + }, + ); } protected createCache():WorkPackageCache { diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/apiv3-work-package-form.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/apiv3-work-package-form.ts index f879523e20..60a80ac2dc 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/apiv3-work-package-form.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/apiv3-work-package-form.ts @@ -1,7 +1,7 @@ -import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { Observable } from "rxjs"; -import { HalSource } from "core-app/features/hal/resources/hal-resource"; +import { APIv3FormResource } from 'core-app/core/apiv3/forms/apiv3-form-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { Observable } from 'rxjs'; +import { HalSource } from 'core-app/features/hal/resources/hal-resource'; export class APIv3WorkPackageForm extends APIv3FormResource { /** @@ -12,10 +12,11 @@ export class APIv3WorkPackageForm extends APIv3FormResource { * @returns A work package form resource prefilled with the provided payload. */ public forTypePayload(payload:HalSource):Observable { - const typePayload = payload._links['type'] ? { _links: { type: payload['_links']['type'] } } : { _links: {} } ; + const typePayload = payload._links.type ? { _links: { type: payload._links.type } } : { _links: {} }; return this.post(payload); } + /** * Returns a promise to post `/api/v3/work_packages/form` where the * payload sent to the backend has been provided. @@ -27,4 +28,3 @@ export class APIv3WorkPackageForm extends APIv3FormResource { return this.post(payload); } } - diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/work-package-cache.spec.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/work-package-cache.spec.ts index f2e3514678..f67b366857 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/work-package-cache.spec.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/work-package-cache.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -31,20 +31,20 @@ import { TestBed } from '@angular/core/testing'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { States } from 'core-app/core/states/states.service'; import { take, takeWhile } from 'rxjs/operators'; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackageCache } from "core-app/core/apiv3/endpoints/work_packages/work-package.cache"; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { OpenProjectDirectFileUploadService } from "core-app/core/file-upload/op-direct-file-upload.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackageCache } from 'core-app/core/apiv3/endpoints/work_packages/work-package.cache'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { OpenProjectDirectFileUploadService } from 'core-app/core/file-upload/op-direct-file-upload.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; describe('WorkPackageCache', () => { let injector:Injector; @@ -73,7 +73,7 @@ describe('WorkPackageCache', () => { { provide: WorkPackageNotificationService, useValue: {} }, { provide: OpenProjectFileUploadService, useValue: {} }, { provide: OpenProjectDirectFileUploadService, useValue: {} }, - ] + ], }); injector = TestBed.inject(Injector); @@ -84,27 +84,26 @@ describe('WorkPackageCache', () => { // sinon.stub(schemaCacheService, 'ensureLoaded').returns(Promise.resolve(true)); spyOn(schemaCacheService, 'ensureLoaded').and.returnValue(Promise.resolve(true as any)); - const workPackage1 = new WorkPackageResource( injector, { id: '1', _links: { - self: '' - } + self: '', + }, }, true, (wp:WorkPackageResource) => undefined, - 'WorkPackage' + 'WorkPackage', ); dummyWorkPackages = [workPackage1 as any]; }); - it('returns a work package after the list has been initialized', function (done:any) { + it('returns a work package after the list has been initialized', (done:any) => { workPackageCache.state('1').values$() .pipe( - take(1) + take(1), ) .subscribe((wp:WorkPackageResource) => { expect(wp.id!).toEqual('1'); @@ -119,7 +118,7 @@ describe('WorkPackageCache', () => { workPackageCache.state('1').values$() .pipe( - takeWhile((wp) => count < 2) + takeWhile((wp) => count < 2), ) .subscribe((wp:WorkPackageResource) => { expect(wp.id!).toEqual('1'); @@ -134,5 +133,4 @@ describe('WorkPackageCache', () => { workPackageCache.updateWorkPackageList([dummyWorkPackages[0]], false); workPackageCache.updateWorkPackageList([dummyWorkPackages[0]], false); }); - }); diff --git a/frontend/src/app/core/apiv3/endpoints/work_packages/work-package.cache.ts b/frontend/src/app/core/apiv3/endpoints/work_packages/work-package.cache.ts index 840dd726a1..e2e6e38667 100644 --- a/frontend/src/app/core/apiv3/endpoints/work_packages/work-package.cache.ts +++ b/frontend/src/app/core/apiv3/endpoints/work_packages/work-package.cache.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,12 +27,12 @@ //++ import { MultiInputState } from 'reactivestates'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { Injectable, Injector } from '@angular/core'; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; @Injectable() export class WorkPackageCache extends StateCacheService { @@ -53,13 +53,12 @@ export class WorkPackageCache extends StateCacheService { updateWorkPackage(wp:WorkPackageResource, immediate = false):Promise { if (immediate || wp.isNew) { return super.updateValue(wp.id!, wp); - } else { - return this.updateValue(wp.id!, wp); } + return this.updateValue(wp.id!, wp); } updateWorkPackageList(list:WorkPackageResource[], skipOnIdentical = true) { - for (var i of list) { + for (const i of list) { const wp = i; const workPackageId = wp.id!; const state = this.multiState.get(workPackageId); diff --git a/frontend/src/app/core/apiv3/forms/apiv3-form-resource.ts b/frontend/src/app/core/apiv3/forms/apiv3-form-resource.ts index 8791943391..4f71485e72 100644 --- a/frontend/src/app/core/apiv3/forms/apiv3-form-resource.ts +++ b/frontend/src/app/core/apiv3/forms/apiv3-form-resource.ts @@ -1,8 +1,8 @@ -import { APIv3ResourcePath } from "core-app/core/apiv3/paths/apiv3-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { Observable } from "rxjs"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { HalPayloadHelper } from "core-app/features/hal/schemas/hal-payload.helper"; +import { APIv3ResourcePath } from 'core-app/core/apiv3/paths/apiv3-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { Observable } from 'rxjs'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { HalPayloadHelper } from 'core-app/features/hal/schemas/hal-payload.helper'; export class APIv3FormResource extends APIv3ResourcePath { /** @@ -13,9 +13,9 @@ export class APIv3FormResource extends AP return this .halResourceService .post( - this.path, - this.extractPayload(request, schema) - ); + this.path, + this.extractPayload(request, schema), + ); } /** @@ -27,4 +27,4 @@ export class APIv3FormResource extends AP public extractPayload(request:T|Object, schema:SchemaResource|null = null) { return HalPayloadHelper.extractPayload(request, schema); } -} \ No newline at end of file +} diff --git a/frontend/src/app/core/apiv3/openproject-api-v3.module.ts b/frontend/src/app/core/apiv3/openproject-api-v3.module.ts index d20328dec3..bc9f791b98 100644 --- a/frontend/src/app/core/apiv3/openproject-api-v3.module.ts +++ b/frontend/src/app/core/apiv3/openproject-api-v3.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,14 +28,14 @@ import { OPSharedModule } from 'core-app/shared/shared.module'; import { NgModule } from '@angular/core'; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; @NgModule({ imports: [ // Commons OPSharedModule, OpenprojectHalModule, - ] + ], }) export class OpenprojectApiV3Module { } diff --git a/frontend/src/app/core/apiv3/paths/apiv3-list-resource.interface.ts b/frontend/src/app/core/apiv3/paths/apiv3-list-resource.interface.ts index eadd29c1c6..7412e49ad3 100644 --- a/frontend/src/app/core/apiv3/paths/apiv3-list-resource.interface.ts +++ b/frontend/src/app/core/apiv3/paths/apiv3-list-resource.interface.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { Observable } from "rxjs"; -import { ApiV3FilterBuilder, FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { Observable } from 'rxjs'; +import { ApiV3FilterBuilder, FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export type ApiV3ListFilter = [string, FilterOperator, boolean|string[]]; diff --git a/frontend/src/app/core/apiv3/paths/apiv3-resource.ts b/frontend/src/app/core/apiv3/paths/apiv3-resource.ts index 29303c493c..a7ce6d4d2f 100644 --- a/frontend/src/app/core/apiv3/paths/apiv3-resource.ts +++ b/frontend/src/app/core/apiv3/paths/apiv3-resource.ts @@ -1,25 +1,25 @@ -import { Constructor } from "@angular/cdk/table"; -import { SimpleResource, SimpleResourceCollection } from "core-app/core/apiv3/paths/path-resources"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { Observable } from "rxjs"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { Constructor } from '@angular/cdk/table'; +import { SimpleResource, SimpleResourceCollection } from 'core-app/core/apiv3/paths/path-resources'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { Observable } from 'rxjs'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class APIv3ResourcePath extends SimpleResource { readonly injector = this.apiRoot.injector; + @InjectField() halResourceService:HalResourceService; constructor(protected apiRoot:APIV3Service, - readonly basePath:string, - readonly id:string|number, - protected parent?:APIv3ResourcePath|APIv3ResourceCollection) { + readonly basePath:string, + readonly id:string|number, + protected parent?:APIv3ResourcePath|APIv3ResourceCollection) { super(basePath, id); } - /** * Build a singular resource from the current segment * @@ -30,7 +30,6 @@ export class APIv3ResourcePath extends SimpleResource { } } - export class APIv3GettableResource extends APIv3ResourcePath { /** * Perform a request to the HalResourceService with the current path @@ -44,12 +43,13 @@ export class APIv3GettableResource extends APIv3ResourcePath export class APIv3ResourceCollection> extends SimpleResourceCollection { readonly injector = this.apiRoot.injector; + @InjectField() halResourceService:HalResourceService; constructor(protected apiRoot:APIV3Service, - protected basePath:string, - segment:string, - protected resource?:Constructor) { + protected basePath:string, + segment:string, + protected resource?:Constructor) { super(basePath, segment, resource); } @@ -69,13 +69,11 @@ export class APIv3ResourceCollection> exte return new (this.resource || APIv3GettableResource)(this.apiRoot, this.path, id, this) as T; } - public withOptionalId(id?:string|number|null):this|T { if (_.isNil(id)) { return this; - } else { - return this.id(id); } + return this.id(id); } /** @@ -100,7 +98,7 @@ export class APIv3ResourceCollection> exte * @param params additional URL params to append */ public filtered>>(filters:ApiV3FilterBuilder, params:{ [key:string]:string } = {}, resourceClass?:Constructor):R { - return this.subResource('?' + filters.toParams(params), resourceClass) as R; + return this.subResource(`?${filters.toParams(params)}`, resourceClass); } /** @@ -111,4 +109,4 @@ export class APIv3ResourceCollection> exte protected subResource>(segment:string, cls:Constructor = APIv3GettableResource as any):R { return new cls(this.apiRoot, this.path, segment, this); } -} \ No newline at end of file +} diff --git a/frontend/src/app/core/apiv3/paths/path-resources.ts b/frontend/src/app/core/apiv3/paths/path-resources.ts index f248bc1644..7a6b65b74f 100644 --- a/frontend/src/app/core/apiv3/paths/path-resources.ts +++ b/frontend/src/app/core/apiv3/paths/path-resources.ts @@ -1,4 +1,4 @@ -import { Constructor } from "@angular/cdk/table"; +import { Constructor } from '@angular/cdk/table'; /** * Simple resource collection to construct paths for RESTful resources. @@ -28,9 +28,8 @@ export class SimpleResourceCollection { public withOptionalId(id?:string|number):this|T { if (_.isNil(id)) { return this; - } else { - return this.id(id); } + return this.id(id); } public toString():string { diff --git a/frontend/src/app/core/apiv3/types/hal-collection.type.ts b/frontend/src/app/core/apiv3/types/hal-collection.type.ts index 99587b5535..ac42d9488f 100644 --- a/frontend/src/app/core/apiv3/types/hal-collection.type.ts +++ b/frontend/src/app/core/apiv3/types/hal-collection.type.ts @@ -7,4 +7,4 @@ export interface IHALCollection { _embedded:{ elements:T[]; } -} \ No newline at end of file +} diff --git a/frontend/src/app/core/apiv3/virtual/apiv3-board-path.ts b/frontend/src/app/core/apiv3/virtual/apiv3-board-path.ts index 257e04596a..a1fb73882d 100644 --- a/frontend/src/app/core/apiv3/virtual/apiv3-board-path.ts +++ b/frontend/src/app/core/apiv3/virtual/apiv3-board-path.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Board } from "core-app/features/boards/board/board"; -import { Observable } from "rxjs"; -import { map, switchMap, tap } from "rxjs/operators"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { MultiInputState } from "reactivestates"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Apiv3BoardsPaths } from "core-app/core/apiv3/virtual/apiv3-boards-paths"; +import { Board } from 'core-app/features/boards/board/board'; +import { Observable } from 'rxjs'; +import { map, switchMap, tap } from 'rxjs/operators'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Apiv3BoardsPaths } from 'core-app/core/apiv3/virtual/apiv3-boards-paths'; export class APIv3BoardPath extends CachableAPIV3Resource { - /** * Perform a request to the HalResourceService with the current path */ @@ -47,13 +45,13 @@ export class APIv3BoardPath extends CachableAPIV3Resource { .id(this.id) .get() .pipe( - map(grid => { + map((grid) => { const newBoard = new Board(grid); newBoard.sortWidgets(); return newBoard; - }) + }), ); } @@ -68,14 +66,13 @@ export class APIv3BoardPath extends CachableAPIV3Resource { .apiRoot .grids .id(board.grid) - .patch(board.grid, schema) - ), - map(grid => { + .patch(board.grid, schema)), + map((grid) => { board.grid = grid; board.sortWidgets(); return board; }), - this.cacheResponse() + this.cacheResponse(), ); } @@ -86,7 +83,7 @@ export class APIv3BoardPath extends CachableAPIV3Resource { .id(this.id) .delete() .pipe( - tap(() => this.cache.clearSome(this.id.toString())) + tap(() => this.cache.clearSome(this.id.toString())), ); } @@ -98,7 +95,7 @@ export class APIv3BoardPath extends CachableAPIV3Resource { .form .post({}) .pipe( - map(form => form.schema) + map((form) => form.schema), ); } diff --git a/frontend/src/app/core/apiv3/virtual/apiv3-boards-paths.ts b/frontend/src/app/core/apiv3/virtual/apiv3-boards-paths.ts index 448525cdf8..c76df578f7 100644 --- a/frontend/src/app/core/apiv3/virtual/apiv3-boards-paths.ts +++ b/frontend/src/app/core/apiv3/virtual/apiv3-boards-paths.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,30 +26,27 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Constructor } from "@angular/cdk/table"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; -import { Apiv3ListParameters, listParamsString } from "core-app/core/apiv3/paths/apiv3-list-resource.interface"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { Board, BoardType } from "core-app/features/boards/board/board"; -import { map, switchMap, tap } from "rxjs/operators"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { CachableAPIV3Collection } from "core-app/core/apiv3/cache/cachable-apiv3-collection"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { MultiInputState } from "reactivestates"; -import { APIv3BoardPath } from "core-app/core/apiv3/virtual/apiv3-board-path"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { Apiv3ListParameters, listParamsString } from 'core-app/core/apiv3/paths/apiv3-list-resource.interface'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { Board, BoardType } from 'core-app/features/boards/board/board'; +import { map, switchMap, tap } from 'rxjs/operators'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { CachableAPIV3Collection } from 'core-app/core/apiv3/cache/cachable-apiv3-collection'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { APIv3BoardPath } from 'core-app/core/apiv3/virtual/apiv3-board-path'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; export class Apiv3BoardsPaths extends CachableAPIV3Collection { - @InjectField() private authorisationService:AuthorisationService; + @InjectField() private PathHelper:PathHelperService; constructor(protected apiRoot:APIV3Service, - protected basePath:string) { + protected basePath:string) { super(apiRoot, basePath, 'grids', APIv3BoardPath); } @@ -62,16 +59,14 @@ export class Apiv3BoardsPaths extends CachableAPIV3Collection>(this.path + listParamsString(params)) .pipe( - tap(collection => this.authorisationService.initModelAuth('boards', collection.$links)), - map(collection => - collection.elements.map(grid => { - const board = new Board(grid); - board.sortWidgets(); - this.touch(board); + tap((collection) => this.authorisationService.initModelAuth('boards', collection.$links)), + map((collection) => collection.elements.map((grid) => { + const board = new Board(grid); + board.sortWidgets(); + this.touch(board); - return board; - }) - ) + return board; + })), ); } @@ -96,7 +91,7 @@ export class Apiv3BoardsPaths extends CachableAPIV3Collection new Board(grid)) + map((grid) => new Board(grid)), ); } @@ -115,9 +110,9 @@ export class Apiv3BoardsPaths extends CachableAPIV3Collection { - const payload:any = _.set({ name: name }, '_links.scope.href', scope); + const payload:any = _.set({ name }, '_links.scope.href', scope); payload.options = { - type: type, + type, }; if (actionAttribute) { @@ -130,12 +125,10 @@ export class Apiv3BoardsPaths extends CachableAPIV3Collection { - return this - .apiRoot - .grids - .post(form.payload.$source); - }) + switchMap((form) => this + .apiRoot + .grids + .post(form.payload.$source)), ); } } diff --git a/frontend/src/app/core/augmenting/dynamic-scripts/backlogs/model.js b/frontend/src/app/core/augmenting/dynamic-scripts/backlogs/model.js index d52e016928..c81441c9f2 100644 --- a/frontend/src/app/core/augmenting/dynamic-scripts/backlogs/model.js +++ b/frontend/src/app/core/augmenting/dynamic-scripts/backlogs/model.js @@ -300,7 +300,7 @@ RB.Model = (function ($) { }, getType: function () { - throw "Child objects must override getType()"; + throw new Error("Child objects must override getType()"); }, handleClick: function (e) { @@ -346,7 +346,7 @@ RB.Model = (function ($) { }, markIfClosed: function () { - throw "Child objects must override markIfClosed()"; + throw new Error("Child objects must override markIfClosed()"); }, markSaving: function () { @@ -393,7 +393,7 @@ RB.Model = (function ($) { }, saveDirectives: function () { - throw "Child object must implement saveDirectives()"; + throw new Error("Child object must implement saveDirectives()"); }, saveEdits: function () { diff --git a/frontend/src/app/core/augmenting/dynamic-scripts/global_roles.ts b/frontend/src/app/core/augmenting/dynamic-scripts/global_roles.ts index cdacd4c92f..773e08dc98 100644 --- a/frontend/src/app/core/augmenting/dynamic-scripts/global_roles.ts +++ b/frontend/src/app/core/augmenting/dynamic-scripts/global_roles.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,90 +27,90 @@ //++ // Loaded dynamically when path matches -(function ($, undefined) { - var global_roles = { - init: function () { - if (global_roles.script_applicable()) { - global_roles.toggle_forms_on_click(); - global_roles.activation_and_visibility_based_on_checked($('#global_role')); +(function ($) { + const globalRoles = { + init() { + if (globalRoles.script_applicable()) { + globalRoles.toggle_forms_on_click(); + globalRoles.activation_and_visibility_based_on_checked($('#global_role')); } }, - toggle_forms_on_click: function () { - $('#global_role').on("click", global_roles.toggle_forms); + toggle_forms_on_click() { + $('#global_role').on('click', this.toggle_forms); }, - toggle_forms: function (event:any) { - global_roles.activation_and_visibility_based_on_checked(this); + toggle_forms() { + globalRoles.activation_and_visibility_based_on_checked(this); }, - activation_and_visibility_based_on_checked: function (element:any) { - if ($(element).prop("checked")) { - global_roles.show_global_forms(); - global_roles.hide_member_forms(); - global_roles.enable_global_forms(); - global_roles.disable_member_forms(); + activation_and_visibility_based_on_checked(element:any) { + if ($(element).prop('checked')) { + globalRoles.show_global_forms(); + globalRoles.hide_member_forms(); + globalRoles.enable_global_forms(); + globalRoles.disable_member_forms(); } else { - global_roles.show_member_forms(); - global_roles.hide_global_forms(); - global_roles.disable_global_forms(); - global_roles.enable_member_forms(); + globalRoles.show_member_forms(); + globalRoles.hide_global_forms(); + globalRoles.disable_global_forms(); + globalRoles.enable_member_forms(); } }, - show_global_forms: function () { + show_global_forms() { $('#global_permissions').show(); }, - show_member_forms: function () { + show_member_forms() { $('#member_attributes').show(); $('#member_permissions').show(); }, - hide_global_forms: function () { + hide_global_forms() { $('#global_permissions').hide(); }, - hide_member_forms: function () { + hide_member_forms() { $('#member_attributes').hide(); $('#member_permissions').hide(); }, - enable_global_forms: function () { - $('#global_attributes input, #global_attributes input, #global_permissions input').each(function (ix, el) { - global_roles.enable_element(el); + enable_global_forms() { + $('#global_attributes input, #global_attributes input, #global_permissions input').each((ix, el) => { + globalRoles.enable_element(el); }); }, - enable_member_forms: function () { - $('#member_attributes input, #member_attributes input, #member_permissions input').each(function (ix, el) { - global_roles.enable_element(el); + enable_member_forms() { + $('#member_attributes input, #member_attributes input, #member_permissions input').each((ix, el) => { + globalRoles.enable_element(el); }); }, - enable_element: function (element:any) { + enable_element(element:any) { $(element).prop('disabled', false); }, - disable_global_forms: function () { - $('#global_attributes input, #global_attributes input, #global_permissions input').each(function (ix, el) { - global_roles.disable_element(el); + disable_global_forms() { + $('#global_attributes input, #global_attributes input, #global_permissions input').each((ix, el) => { + globalRoles.disable_element(el); }); }, - disable_member_forms: function () { - $('#member_attributes input, #member_attributes input, #member_permissions input').each(function (ix, el) { - global_roles.disable_element(el); + disable_member_forms() { + $('#member_attributes input, #member_attributes input, #member_permissions input').each((ix, el) => { + globalRoles.disable_element(el); }); }, - disable_element: function (element:any) { + disable_element(element:any) { $(element).prop('disabled', true); }, - script_applicable: function () { + script_applicable() { return $('body.controller-roles.action-new, body.controller-roles.action-create').length === 1; - } + }, }; - $(document).ready(global_roles.init); + $(document).ready(globalRoles.init); }(jQuery)); diff --git a/frontend/src/app/core/augmenting/dynamic-scripts/two_factor_authentication.ts b/frontend/src/app/core/augmenting/dynamic-scripts/two_factor_authentication.ts index 19c7206c68..6198b89432 100644 --- a/frontend/src/app/core/augmenting/dynamic-scripts/two_factor_authentication.ts +++ b/frontend/src/app/core/augmenting/dynamic-scripts/two_factor_authentication.ts @@ -1,63 +1,64 @@ import 'core-vendor/qrcode-min'; -declare var QRCode:any; +declare let QRCode:any; -jQuery(function ($) { - $('#submit_otp').submit(function() { - $('.ajax_form').find("input, radio").attr('disabled', 'disabled'); +jQuery(($) => { + $('#submit_otp').submit(() => { + $('.ajax_form').find('input, radio').attr('disabled', 'disabled'); }); - $('#toggle_resend_form').click(function(){ + $('#toggle_resend_form').click(() => { $('#resend_otp_container').toggle(); return false; }); - $('.qr-code-element').each(function() { - var el = $(this); + $('.qr-code-element').each(function () { + const el = $(this); new QRCode( el[0], { text: el.data('value'), width: 220, - height: 220 - } + height: 220, + }, ); }); - $('.ajax_form').submit(function() { - $('#submit_otp').find("input").attr('disabled', 'disabled'); - var form = $(this), - submit_button = form.find("input[type=submit]"); - $.ajax({ url: form.attr('action'), + $('.ajax_form').submit(function () { + $('#submit_otp').find('input').attr('disabled', 'disabled'); + const form = $(this); + const submit_button = form.find('input[type=submit]'); + $.ajax({ + url: form.attr('action'), type: 'post', data: form.serialize(), - beforeSend: function() { + beforeSend() { submit_button.attr('disabled', 'disabled'); submit_button.toggleClass('submitting'); $('.flash.notice').toggle(); }, - complete: function(response) { + complete(response) { submit_button.removeAttr('disabled'); - $('#submit_otp').find("input").removeAttr('disabled'); + $('#submit_otp').find('input').removeAttr('disabled'); $('.flash.notice a').html(response.responseText); $('form#resend_otp, #toggle_resend_form, .flash.notice').toggle(); submit_button.toggleClass('submitting'); - } + }, }); return false; }); - $('#print_2fa_backup_codes').click(function() { + $('#print_2fa_backup_codes').click(() => { window.print(); }); if ($('#download_2fa_backup_codes').length) { - var text = ''; - $('.two-factor-authentication--backup-codes li').each(function() { - text += this.textContent + "\n"; + let text = ''; + $('.two-factor-authentication--backup-codes li').each(function () { + text += `${this.textContent}\n`; }); - var element = $('#download_2fa_backup_codes'); - element.attr('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + const element = $('#download_2fa_backup_codes'); + element.attr('href', `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`); element.attr('download', 'backup-codes.txt'); } }); diff --git a/frontend/src/app/core/augmenting/openproject-augmenting.module.ts b/frontend/src/app/core/augmenting/openproject-augmenting.module.ts index b17da2fc83..ae2fa9a56d 100644 --- a/frontend/src/app/core/augmenting/openproject-augmenting.module.ts +++ b/frontend/src/app/core/augmenting/openproject-augmenting.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,13 +27,13 @@ //++ import { NgModule } from '@angular/core'; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { OpModalWrapperAugmentService } from "core-app/shared/components/modal/modal-wrapper-augment.service"; -import { PathScriptAugmentService } from "core-app/core/augmenting/services/path-script.augment.service"; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { OpModalWrapperAugmentService } from 'core-app/shared/components/modal/modal-wrapper-augment.service'; +import { PathScriptAugmentService } from 'core-app/core/augmenting/services/path-script.augment.service'; @NgModule({ - imports: [ OpenprojectModalModule ], - providers: [ PathScriptAugmentService ], + imports: [OpenprojectModalModule], + providers: [PathScriptAugmentService], }) export class OpenprojectAugmentingModule { constructor(modalWrapper:OpModalWrapperAugmentService, @@ -43,4 +43,3 @@ export class OpenprojectAugmentingModule { pathScript.loadRequiredScripts(); } } - diff --git a/frontend/src/app/core/augmenting/services/path-script.augment.service.ts b/frontend/src/app/core/augmenting/services/path-script.augment.service.ts index 29911c99cc..fde6be6bd6 100644 --- a/frontend/src/app/core/augmenting/services/path-script.augment.service.ts +++ b/frontend/src/app/core/augmenting/services/path-script.augment.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Inject, Injectable } from "@angular/core"; -import { DOCUMENT } from "@angular/common"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; @Injectable({ providedIn: 'root' }) export class PathScriptAugmentService { - constructor(@Inject(DOCUMENT) protected documentElement:Document) { } @@ -49,8 +47,8 @@ export class PathScriptAugmentService { const matches = this.documentElement.querySelectorAll('meta[name="required_script"]'); for (let i = 0; i < matches.length; ++i) { const name = matches[i].content; - debugLog("Loading required script " + name); - import('../dynamic-scripts/' + name); + debugLog(`Loading required script ${name}`); + import(`../dynamic-scripts/${name}`); } } } diff --git a/frontend/src/app/core/backup/op-backup.service.ts b/frontend/src/app/core/backup/op-backup.service.ts index f624aa02a0..5253b9dde3 100644 --- a/frontend/src/app/core/backup/op-backup.service.ts +++ b/frontend/src/app/core/backup/op-backup.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,29 +26,29 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {Injectable} from "@angular/core"; -import {HttpClient} from "@angular/common/http"; -import {HalResource} from "core-app/features/hal/resources/hal-resource"; -import {Observable} from "rxjs"; -import {HalResourceService} from "core-app/features/hal/services/hal-resource.service"; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Observable } from 'rxjs'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; @Injectable({ providedIn: 'root' }) export class OpenProjectBackupService { constructor(protected http:HttpClient, - protected halResource:HalResourceService) { + protected halResource:HalResourceService) { } - public triggerBackup(backupToken:string, includeAttachments:boolean=true):Observable { + public triggerBackup(backupToken:string, includeAttachments = true):Observable { return this .http .request( - "post", - "/api/v3/backups", - { - body: { backupToken: backupToken, attachments: includeAttachments }, - withCredentials: true, - responseType: "json" as any - } - ); + 'post', + '/api/v3/backups', + { + body: { backupToken, attachments: includeAttachments }, + withCredentials: true, + responseType: 'json' as any, + }, + ); } } diff --git a/frontend/src/app/core/browser/browser-detector.service.ts b/frontend/src/app/core/browser/browser-detector.service.ts index 06956dac01..487253d0ab 100644 --- a/frontend/src/app/core/browser/browser-detector.service.ts +++ b/frontend/src/app/core/browser/browser-detector.service.ts @@ -1,10 +1,9 @@ -import { Inject, Injectable } from "@angular/core"; -import { DOCUMENT } from "@angular/common"; +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; @Injectable({ providedIn: 'root' }) export class BrowserDetector { - - constructor (@Inject(DOCUMENT) private documentElement:Document) { + constructor(@Inject(DOCUMENT) private documentElement:Document) { } /** @@ -25,5 +24,4 @@ export class BrowserDetector { private hasBodyClass(name:string):boolean { return this.documentElement.body.classList.contains(name); } - } diff --git a/frontend/src/app/core/browser/device.service.ts b/frontend/src/app/core/browser/device.service.ts index 0d23855619..f89c9167aa 100644 --- a/frontend/src/app/core/browser/device.service.ts +++ b/frontend/src/app/core/browser/device.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DeviceService { - public mobileWidthTreshold = 680; public get isMobile():boolean { diff --git a/frontend/src/app/core/config/configuration.service.ts b/frontend/src/app/core/config/configuration.service.ts index e9a7353630..c97d5d93eb 100644 --- a/frontend/src/app/core/config/configuration.service.ts +++ b/frontend/src/app/core/config/configuration.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,9 +27,9 @@ //++ import { Injectable } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ConfigurationResource } from "core-app/features/hal/resources/configuration-resource"; -import * as moment from "moment"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { ConfigurationResource } from 'core-app/features/hal/resources/configuration-resource'; +import * as moment from 'moment'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable({ providedIn: 'root' }) export class ConfigurationService { @@ -37,10 +37,11 @@ export class ConfigurationService { // TODO: this currently saves the request between page reloads, // but could easily be stored in localStorage private configuration:ConfigurationResource; + public initialized:Promise; public constructor(readonly I18n:I18nService, - readonly apiV3Service:APIV3Service) { + readonly apiV3Service:APIV3Service) { this.initialized = this.loadConfiguration().then(() => true).catch(() => false); } @@ -103,9 +104,8 @@ export class ConfigurationService { public startOfWeek() { if (this.startOfWeekPresent()) { return this.systemPreference('startOfWeek'); - } else { - return moment.localeData(I18n.locale).firstDayOfWeek(); } + return moment.localeData(I18n.locale).firstDayOfWeek(); } private loadConfiguration() { diff --git a/frontend/src/app/core/current-project/current-project.service.spec.ts b/frontend/src/app/core/current-project/current-project.service.spec.ts index b0c12c3c23..83de995c01 100644 --- a/frontend/src/app/core/current-project/current-project.service.spec.ts +++ b/frontend/src/app/core/current-project/current-project.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from './current-project.service'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -describe('currentProject service', function() { +describe('currentProject service', () => { let element:JQuery; let currentProject:CurrentProjectService; const apiV3Stub:any = { projects: { - id: (id:string) => { - return { toString: () => '/api/v3/projects/' + id }; - } - } + id: (id:string) => ({ toString: () => `/api/v3/projects/${id}` }), + }, }; beforeEach(() => { diff --git a/frontend/src/app/core/current-project/current-project.service.ts b/frontend/src/app/core/current-project/current-project.service.ts index ebc872d1ad..4c8f694f00 100644 --- a/frontend/src/app/core/current-project/current-project.service.ts +++ b/frontend/src/app/core/current-project/current-project.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { Injectable } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable({ providedIn: 'root' }) export class CurrentProjectService { private current:{ id:string, identifier:string, name:string }; constructor(private PathHelper:PathHelperService, - private apiV3Service:APIV3Service) { + private apiV3Service:APIV3Service) { this.detect(); } @@ -88,7 +88,7 @@ export class CurrentProjectService { this.current = { id: element.dataset.projectId!, name: element.dataset.projectName!, - identifier: element.dataset.projectIdentifier! + identifier: element.dataset.projectIdentifier!, }; } } diff --git a/frontend/src/app/core/current-user/current-user.module.ts b/frontend/src/app/core/current-user/current-user.module.ts index 86a404e441..cccb57e4d8 100644 --- a/frontend/src/app/core/current-user/current-user.module.ts +++ b/frontend/src/app/core/current-user/current-user.module.ts @@ -1,10 +1,8 @@ -import { Injector, NgModule } from "@angular/core"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; +import { Injector, NgModule } from '@angular/core'; -import { CurrentUserService } from "./current-user.service"; -import { CurrentUserStore } from "./current-user.store"; -import { CurrentUserQuery } from "./current-user.query"; +import { CurrentUserService } from './current-user.service'; +import { CurrentUserStore } from './current-user.store'; +import { CurrentUserQuery } from './current-user.query'; export function bootstrapModule(injector:Injector) { const currentUserService = injector.get(CurrentUserService); diff --git a/frontend/src/app/core/current-user/current-user.query.ts b/frontend/src/app/core/current-user/current-user.query.ts index 698d17edf3..036b53608d 100644 --- a/frontend/src/app/core/current-user/current-user.query.ts +++ b/frontend/src/app/core/current-user/current-user.query.ts @@ -1,21 +1,16 @@ import { Injectable } from '@angular/core'; import { filterNilValue, Query } from '@datorama/akita'; -import { Observable, combineLatest } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { - CurrentUserStore, - CurrentUserState, - CurrentUser, -} from './current-user.store'; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; +import { CurrentUserState, CurrentUserStore } from './current-user.store'; @Injectable() export class CurrentUserQuery extends Query { - constructor(protected store: CurrentUserStore) { + constructor(protected store:CurrentUserStore) { super(store); } - isLoggedIn$ = this.select(state => !!state.id); + isLoggedIn$ = this.select((state) => !!state.id); + user$ = this.select(({ id, name, mail }) => ({ id, name, mail })); + capabilities$ = this.select('capabilities').pipe(filterNilValue()); } diff --git a/frontend/src/app/core/current-user/current-user.service.spec.ts b/frontend/src/app/core/current-user/current-user.service.spec.ts index f3577c24c6..45ce919249 100644 --- a/frontend/src/app/core/current-user/current-user.service.spec.ts +++ b/frontend/src/app/core/current-user/current-user.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ -import { TestBed, getTestBed } from '@angular/core/testing'; +import { getTestBed, TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { States } from "core-app/core/states/states.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { States } from 'core-app/core/states/states.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { CurrentUserService } from './current-user.service'; -import { CurrentUserStore } from "./current-user.store"; -import { CurrentUserQuery } from "./current-user.query"; -import { CurrentUser } from './current-user.store'; +import { CurrentUser, CurrentUserStore } from './current-user.store'; +import { CurrentUserQuery } from './current-user.query'; const globalCapability = { _type: 'Capability', @@ -122,7 +121,7 @@ const projectCapabilityp53Update = { }, }; -describe('CurrentUserService', function () { +describe('CurrentUserService', () => { let injector:TestBed; let currentUserService:CurrentUserService; let httpMock:HttpTestingController; @@ -171,8 +170,7 @@ describe('CurrentUserService', function () { }, }); }); - } - + }; afterEach(() => { httpMock.verify(); diff --git a/frontend/src/app/core/current-user/current-user.service.ts b/frontend/src/app/core/current-user/current-user.service.ts index 9ad260690b..3d16b4374a 100644 --- a/frontend/src/app/core/current-user/current-user.service.ts +++ b/frontend/src/app/core/current-user/current-user.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,30 +26,34 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { of, forkJoin } from 'rxjs'; -import { take, map, mergeMap, distinctUntilChanged, tap } from 'rxjs/operators'; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { CurrentUserStore, CurrentUser } from "./current-user.store"; -import { CurrentUserQuery } from "./current-user.query"; -import { FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { Injectable } from '@angular/core'; +import { forkJoin, of } from 'rxjs'; +import { + distinctUntilChanged, map, mergeMap, take, +} from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { CurrentUser, CurrentUserStore } from './current-user.store'; +import { CurrentUserQuery } from './current-user.query'; @Injectable({ providedIn: 'root' }) export class CurrentUserService { private PAGE_FETCH_SIZE = 1000; constructor( - private apiV3Service: APIV3Service, - private currentUserStore: CurrentUserStore, - private currentUserQuery: CurrentUserQuery, + private apiV3Service:APIV3Service, + private currentUserStore:CurrentUserStore, + private currentUserQuery:CurrentUserQuery, ) { this.setupLegacyDataListeners(); } public capabilities$ = this.currentUserQuery.capabilities$; + public isLoggedIn$ = this.currentUserQuery.isLoggedIn$; + public user$ = this.currentUserQuery.user$; /** @@ -57,8 +61,8 @@ export class CurrentUserService { * * This refetches the global and current project capabilities */ - public setUser(user: CurrentUser) { - this.currentUserStore.update(state => ({ + public setUser(user:CurrentUser) { + this.currentUserStore.update((state) => ({ ...state, ...user, })); @@ -72,7 +76,7 @@ export class CurrentUserService { public fetchCapabilities(contexts:string[] = []) { this.user$.pipe(take(1)).subscribe((user) => { if (!user.id) { - this.currentUserStore.update(state => ({ + this.currentUserStore.update((state) => ({ ...state, capabilities: [], })); @@ -80,56 +84,56 @@ export class CurrentUserService { return; } - const filters: [string, FilterOperator, string[]][] = [ ['principal', '=', [user.id]] ]; + const filters:[string, FilterOperator, string[]][] = [['principal', '=', [user.id]]]; if (contexts.length) { - filters.push([ 'context', '=', contexts.map(context => context === 'global' ? 'g' : `p${context}`) ]); + filters.push(['context', '=', contexts.map((context) => (context === 'global' ? 'g' : `p${context}`))]); } this.apiV3Service.capabilities.list({ pageSize: this.PAGE_FETCH_SIZE, filters, }) - .pipe( - mergeMap((data: CollectionResource) => { + .pipe( + mergeMap((data:CollectionResource) => { // The data we've loaded might not contain all capabilities. Some responses might have thousands of // capabilites, and our page size is restricted. If this is the case, we branch out and sent out parallel // requests for each of the other pages. - if (data.total > this.PAGE_FETCH_SIZE) { - const remaining = data.total - this.PAGE_FETCH_SIZE; - const pagesRemaining = Math.ceil(remaining / this.PAGE_FETCH_SIZE); - const calls = (new Array(pagesRemaining)) - .fill(null) - .map((_, i) => this.apiV3Service.capabilities.list({ - pageSize: this.PAGE_FETCH_SIZE, - offset: i + 2, // Page offsets are 1-indexed, and we already fetched the first page - filters, - })); - - // Branch out and fetch all remaining pages in parallel. - // Afterwards, merge the resulting list - return forkJoin(...calls).pipe( - map( - (results: CollectionResource[]) => results.reduce( - (acc, next) => acc.concat(next.elements), - data.elements, + if (data.total > this.PAGE_FETCH_SIZE) { + const remaining = data.total - this.PAGE_FETCH_SIZE; + const pagesRemaining = Math.ceil(remaining / this.PAGE_FETCH_SIZE); + const calls = (new Array(pagesRemaining)) + .fill(null) + .map((_, i) => this.apiV3Service.capabilities.list({ + pageSize: this.PAGE_FETCH_SIZE, + offset: i + 2, // Page offsets are 1-indexed, and we already fetched the first page + filters, + })); + + // Branch out and fetch all remaining pages in parallel. + // Afterwards, merge the resulting list + return forkJoin(...calls).pipe( + map( + (results:CollectionResource[]) => results.reduce( + (acc, next) => acc.concat(next.elements), + data.elements, + ), ), - ), - ); - } - - // The current page is the only page, return the results. - return of(data.elements); - }), - ) - .subscribe((capabilities) => { - this.currentUserStore.update(state => ({ - ...state, - capabilities: [ - ...capabilities, - ...(state.capabilities || []).filter(cap => !!capabilities.find(newCap => newCap.id === cap.id)), - ], - })); - }); + ); + } + + // The current page is the only page, return the results. + return of(data.elements); + }), + ) + .subscribe((capabilities) => { + this.currentUserStore.update((state) => ({ + ...state, + capabilities: [ + ...capabilities, + ...(state.capabilities || []).filter((cap) => !!capabilities.find((newCap) => newCap.id === cap.id)), + ], + })); + }); }); return this.currentUserQuery.capabilities$; @@ -138,37 +142,35 @@ export class CurrentUserService { /** * Returns the users' capabilities filtered by context */ - public capabilitiesForContext$(contextId: string) { + public capabilitiesForContext$(contextId:string) { return this.capabilities$.pipe( - map((capabilities) => capabilities.filter(cap => cap.context.href.endsWith(`/${contextId}`))), + map((capabilities) => capabilities.filter((cap) => cap.context.href.endsWith(`/${contextId}`))), distinctUntilChanged(), ); } /** - * Returns an Observable indicating whether the user has the required capabilities in the provided context. + * Returns an Observable indicating whether the user has the required capabilities in the provided context. */ - public hasCapabilities$(action: string|string[], contextId: string = 'global') { + public hasCapabilities$(action:string|string[], contextId = 'global') { const actions = _.castArray(action); return this.capabilitiesForContext$(contextId).pipe( map((capabilities) => actions.reduce( - (acc, action) => { - return acc && !!capabilities.find(cap => cap.action.href.endsWith(`/api/v3/actions/${action}`)); - }, + (acc, contextAction) => acc && !!capabilities.find((cap) => cap.action.href.endsWith(`/api/v3/actions/${contextAction}`)), capabilities.length > 0, )), - distinctUntilChanged() + distinctUntilChanged(), ); } /** - * Returns an Observable indicating whether the user has any of the required capabilities in the provided context. + * Returns an Observable indicating whether the user has any of the required capabilities in the provided context. */ - public hasAnyCapabilityOf$(actions: string|string[], contextId: string = 'global') { + public hasAnyCapabilityOf$(actions:string|string[], contextId = 'global') { const actionsToFilter = _.castArray(actions); return this.capabilitiesForContext$(contextId).pipe( map((capabilities) => capabilities.reduce( - (acc, cap) => acc || !!actionsToFilter.find(action => cap.action.href.endsWith(`/api/v3/actions/${action}`)), + (acc, cap) => acc || !!actionsToFilter.find((action) => cap.action.href.endsWith(`/api/v3/actions/${action}`)), false, )), distinctUntilChanged(), @@ -177,19 +179,19 @@ export class CurrentUserService { // Everything below this is deprecated legacy interfacing and should not be used - private setupLegacyDataListeners() { - this.currentUserQuery.user$.subscribe(user => this._user = user); - this.currentUserQuery.isLoggedIn$.subscribe(isLoggedIn => this._isLoggedIn = isLoggedIn); + this.currentUserQuery.user$.subscribe((user) => (this._user = user)); + this.currentUserQuery.isLoggedIn$.subscribe((isLoggedIn) => (this._isLoggedIn = isLoggedIn)); } private _isLoggedIn = false; + /** @deprecated Use the store mechanism `currentUserQuery.isLoggedIn$` */ public get isLoggedIn() { return this._isLoggedIn; } - private _user: CurrentUser = { + private _user:CurrentUser = { id: null, name: null, mail: null, diff --git a/frontend/src/app/core/current-user/current-user.store.ts b/frontend/src/app/core/current-user/current-user.store.ts index c01253b11f..40a054bb86 100644 --- a/frontend/src/app/core/current-user/current-user.store.ts +++ b/frontend/src/app/core/current-user/current-user.store.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; import { Store, StoreConfig } from '@datorama/akita'; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; export interface CurrentUser { - id: string|null; - name: string|null; - mail: string|null; + id:string|null; + name:string|null; + mail:string|null; } export interface CurrentUserState extends CurrentUser { - capabilities: CapabilityResource[]|null; + capabilities:CapabilityResource[]|null; } -export function createInitialState(): CurrentUserState { +export function createInitialState():CurrentUserState { return { id: null, name: null, diff --git a/frontend/src/app/core/datetime/timezone.service.spec.ts b/frontend/src/app/core/datetime/timezone.service.spec.ts index 2567486b75..61b7180a7a 100644 --- a/frontend/src/app/core/datetime/timezone.service.spec.ts +++ b/frontend/src/app/core/datetime/timezone.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ import { TestBed } from '@angular/core/testing'; import { HttpClientModule } from '@angular/common/http'; @@ -35,8 +35,7 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; -describe('TimezoneService', function () { - +describe('TimezoneService', () => { const TIME = '2013-02-08T09:30:26'; const DATE = '2013-02-08'; let timezoneService:TimezoneService; @@ -44,57 +43,56 @@ describe('TimezoneService', function () { const compile = (timezone?:string) => { const ConfigurationServiceStub = { isTimezoneSet: () => !!timezone, - timezone: () => timezone + timezone: () => timezone, }; TestBed.configureTestingModule({ imports: [ - HttpClientModule + HttpClientModule, ], providers: [ { provide: I18nService, useValue: {} }, { provide: ConfigurationService, useValue: ConfigurationServiceStub }, PathHelperService, TimezoneService, - ] + ], }); timezoneService = TestBed.inject(TimezoneService); }; - describe('without time zone set', function () { + describe('without time zone set', () => { beforeEach(() => { compile(); }); - describe('#parseDatetime', function () { - it('is UTC', function () { - var time = timezoneService.parseDatetime(TIME); + describe('#parseDatetime', () => { + it('is UTC', () => { + const time = timezoneService.parseDatetime(TIME); expect(time.utcOffset()).toEqual(0); expect(time.format('HH:mm')).toEqual('09:30'); }); - it('has no time information', function () { - var time = timezoneService.parseDate(DATE); + it('has no time information', () => { + const time = timezoneService.parseDate(DATE); expect(time.format('HH:mm')).toEqual('00:00'); }); }); }); - describe('with time zone set', function () { + describe('with time zone set', () => { beforeEach(() => { compile('America/Vancouver'); }); - describe('Non-UTC timezone', function () { - - it('is in the given timezone' , function () { + describe('Non-UTC timezone', () => { + it('is in the given timezone', () => { const date = timezoneService.parseDatetime(TIME); expect(date.format('HH:mm')).toEqual('01:30'); }); - it('has local time zone', function () { - expect(timezoneService.ConfigurationService.timezone()).toEqual('America/Vancouver'); + it('has local time zone', () => { + expect(timezoneService.configurationService.timezone()).toEqual('America/Vancouver'); }); }); }); diff --git a/frontend/src/app/core/datetime/timezone.service.ts b/frontend/src/app/core/datetime/timezone.service.ts index ae82f53168..c452bfb6cf 100644 --- a/frontend/src/app/core/datetime/timezone.service.ts +++ b/frontend/src/app/core/datetime/timezone.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,8 +34,8 @@ import { Moment } from 'moment'; @Injectable({ providedIn: 'root' }) export class TimezoneService { - constructor(readonly ConfigurationService:ConfigurationService, - readonly I18n:I18nService) { + constructor(readonly configurationService:ConfigurationService, + readonly I18n:I18nService) { this.setupLocale(); } @@ -48,11 +48,11 @@ export class TimezoneService { * a local date time moment object. */ public parseDatetime(datetime:string, format?:string):Moment { - var d = moment.utc(datetime, format); + const d = moment.utc(datetime, format); - if (this.ConfigurationService.isTimezoneSet()) { + if (this.configurationService.isTimezoneSet()) { d.local(); - d.tz(this.ConfigurationService.timezone()); + d.tz(this.configurationService.timezone()); } return d; @@ -73,13 +73,13 @@ export class TimezoneService { * @returns {Moment} */ public parseLocalDateTime(date:string, format?:string) { - var result; - format = format || this.getTimeFormat(); + let result; + const timeFormat = format || this.getTimeFormat(); - if (this.ConfigurationService.isTimezoneSet()) { - result = moment.tz(date, format!, this.ConfigurationService.timezone()); + if (this.configurationService.isTimezoneSet()) { + result = moment.tz(date, timeFormat, this.configurationService.timezone()); } else { - result = moment(date, format); + result = moment(date, timeFormat); } result.utc(); @@ -103,7 +103,7 @@ export class TimezoneService { } public formattedDate(date:string) { - var d = this.parseDate(date); + const d = this.parseDate(date); return d.format(this.getDateFormat()); } @@ -132,8 +132,8 @@ export class TimezoneService { } public formattedDatetime(datetimeString:string) { - var c = this.formattedDatetimeComponents(datetimeString); - return c[0] + ' ' + c[1]; + const c = this.formattedDatetimeComponents(datetimeString); + return `${c[0]} ${c[1]}`; } public formattedRelativeDateTime(datetimeString:string) { @@ -142,10 +142,10 @@ export class TimezoneService { } public formattedDatetimeComponents(datetimeString:string) { - var d = this.parseDatetime(datetimeString); + const d = this.parseDatetime(datetimeString); return [ d.format(this.getDateFormat()), - d.format(this.getTimeFormat()) + d.format(this.getTimeFormat()), ]; } @@ -174,15 +174,15 @@ export class TimezoneService { } public isValid(date:string, dateFormat:string) { - var format = dateFormat || this.getDateFormat(); + const format = dateFormat || this.getDateFormat(); return moment(date, [format], true).isValid(); } public getDateFormat() { - return this.ConfigurationService.dateFormatPresent() ? this.ConfigurationService.dateFormat() : 'L'; + return this.configurationService.dateFormatPresent() ? this.configurationService.dateFormat() : 'L'; } public getTimeFormat() { - return this.ConfigurationService.timeFormatPresent() ? this.ConfigurationService.timeFormat() : 'LT'; + return this.configurationService.timeFormatPresent() ? this.configurationService.timeFormat() : 'LT'; } } diff --git a/frontend/src/app/core/enterprise/banners.service.ts b/frontend/src/app/core/enterprise/banners.service.ts index 00093c9905..e493f1d863 100644 --- a/frontend/src/app/core/enterprise/banners.service.ts +++ b/frontend/src/app/core/enterprise/banners.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {Inject, Injectable} from '@angular/core'; -import {DOCUMENT} from "@angular/common"; -import {enterpriseEditionUrl} from "core-app/core/setup/globals/constants.const"; +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { enterpriseEditionUrl } from 'core-app/core/setup/globals/constants.const'; @Injectable({ providedIn: 'root' }) export class BannersService { - private readonly _banners:boolean = true; constructor(@Inject(DOCUMENT) protected documentElement:Document) { @@ -43,7 +42,7 @@ export class BannersService { return this._banners; } - public getEnterPriseEditionUrl({ referrer, hash }:{referrer?:string, hash?:string} = {}) { + public getEnterPriseEditionUrl({ referrer, hash }:{ referrer?:string, hash?:string } = {}) { const url = new URL(enterpriseEditionUrl); if (referrer) { url.searchParams.set('op_referrer', referrer); diff --git a/frontend/src/app/core/errors/sentry/sentry-dependency.ts b/frontend/src/app/core/errors/sentry/sentry-dependency.ts index 03234067eb..2f32e24e33 100644 --- a/frontend/src/app/core/errors/sentry/sentry-dependency.ts +++ b/frontend/src/app/core/errors/sentry/sentry-dependency.ts @@ -1,4 +1,4 @@ -import * as Sentry from "@sentry/angular"; -import { Integrations } from "@sentry/tracing"; +import * as Sentry from '@sentry/angular'; +import { Integrations } from '@sentry/tracing'; -export { Sentry, Integrations }; \ No newline at end of file +export { Sentry, Integrations }; diff --git a/frontend/src/app/core/errors/sentry/sentry-reporter.ts b/frontend/src/app/core/errors/sentry/sentry-reporter.ts index 9167ff3baa..67be8989c5 100644 --- a/frontend/src/app/core/errors/sentry/sentry-reporter.ts +++ b/frontend/src/app/core/errors/sentry/sentry-reporter.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Hub, Severity, Scope, Event as SentryEvent } from "@sentry/types"; -import { environment } from "../../../../environments/environment"; +import { + Event as SentryEvent, Hub, Scope, Severity, +} from '@sentry/types'; +import { environment } from '../../../../environments/environment'; export type ScopeCallback = (scope:Scope) => void; export type MessageSeverity = 'fatal'|'error'|'warning'|'log'|'info'|'debug'; @@ -57,7 +59,6 @@ interface QueuedMessage { } export class SentryReporter implements ErrorReporter { - private contextCallbacks:ScopeCallback[] = []; private messageStack:QueuedMessage[] = []; @@ -67,7 +68,7 @@ export class SentryReporter implements ErrorReporter { private client:Hub; constructor() { - const sentryElement = document.querySelector('meta[name=openproject_sentry]') as HTMLElement|null; + const sentryElement = document.querySelector('meta[name=openproject_sentry]') as HTMLElement; if (sentryElement !== null) { this.loadSentry(sentryElement); } else { @@ -84,9 +85,9 @@ export class SentryReporter implements ErrorReporter { import('./sentry-dependency').then((imported) => { const sentry = imported.Sentry; sentry.init({ - dsn: dsn, + dsn, debug: !environment.production, - release: 'op-frontend@' + version, + release: `op-frontend@${version}`, environment: environment.production ? 'production' : 'development', // Integrations @@ -94,13 +95,13 @@ export class SentryReporter implements ErrorReporter { tracesSampler: (samplingContext) => { switch (samplingContext.transactionContext.op) { - case 'op': - case 'navigation': + case 'op': + case 'navigation': // Trace 1% of page loads and navigation events - return Math.min(0.01 * traceFactor, 1.0); - default: + return Math.min(0.01 * traceFactor, 1.0); + default: // Trace 0.1% of requests - return Math.min(0.001 * traceFactor, 1.0); + return Math.min(0.001 * traceFactor, 1.0); } }, @@ -159,7 +160,7 @@ export class SentryReporter implements ErrorReporter { if (this.client) { /** Add to global context as well */ - callbacks.forEach(cb => this.client.configureScope(cb)); + callbacks.forEach((cb) => this.client.configureScope(cb)); } } @@ -172,7 +173,7 @@ export class SentryReporter implements ErrorReporter { if (this.sentryConfigured) { this.messageStack.push({ type, args }); } else { - console.log("[ErrorReporter] Would queue sentry message %O %O, but is not configured.", type, args); + console.log('[ErrorReporter] Would queue sentry message %O %O, but is not configured.', type, args); } } @@ -187,7 +188,7 @@ export class SentryReporter implements ErrorReporter { scope.setExtra('url_query', window.location.search); /** Execute callbacks */ - this.contextCallbacks.forEach(cb => cb(scope)); + this.contextCallbacks.forEach((cb) => cb(scope)); } /** @@ -199,10 +200,10 @@ export class SentryReporter implements ErrorReporter { private filterEvent(event:SentryEvent):SentryEvent|null { const unsupportedBrowser = document.body.classList.contains('-unsupported-browser'); if (unsupportedBrowser) { - console.warn("Browser is not supported, skipping sentry reporting completely."); + console.warn('Browser is not supported, skipping sentry reporting completely.'); return null; } return event; } -} \ No newline at end of file +} diff --git a/frontend/src/app/core/expression/expression.service.ts b/frontend/src/app/core/expression/expression.service.ts index d5bf11da62..40b2945635 100644 --- a/frontend/src/app/core/expression/expression.service.ts +++ b/frontend/src/app/core/expression/expression.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,7 +27,6 @@ //++ export class ExpressionService { - // This is what returned by rails-angular-xss when it discovers double open curly braces // See https://github.com/opf/rails-angular-xss for more information. public static get UNESCAPED_EXPRESSION() { diff --git a/frontend/src/app/core/file-upload/op-direct-file-upload.service.ts b/frontend/src/app/core/file-upload/op-direct-file-upload.service.ts index 5b64398290..fda56505bf 100644 --- a/frontend/src/app/core/file-upload/op-direct-file-upload.service.ts +++ b/frontend/src/app/core/file-upload/op-direct-file-upload.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { HttpEvent, HttpResponse } from "@angular/common/http"; -import { from, Observable, of } from "rxjs"; -import { share, switchMap } from "rxjs/operators"; -import { OpenProjectFileUploadService, UploadBlob, UploadFile, UploadInProgress } from './op-file-upload.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { HttpEvent, HttpResponse } from '@angular/common/http'; +import { from, Observable, of } from 'rxjs'; +import { share, switchMap } from 'rxjs/operators'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { + OpenProjectFileUploadService, UploadBlob, UploadFile, UploadInProgress, +} from './op-file-upload.service'; interface PrepareUploadResult { url:string; @@ -51,47 +53,47 @@ export class OpenProjectDirectFileUploadService extends OpenProjectFileUploadSer const observable = from(this.getDirectUploadFormFrom(url, file)) .pipe( switchMap(this.uploadToExternal(file, method, responseType)), - share() + share(), ); return [file, observable] as UploadInProgress; } private uploadToExternal(file:UploadFile|UploadBlob, method:string, responseType:string):(result:PrepareUploadResult) => Observable> { - return result => { + return (result) => { result.form.append('file', file, file.customName || file.name); return this .http .request( - method, - result.url, - { - body: result.form, - // Observe the response, not the body - observe: 'events', - // This is important as the CORS policy for the bucket is * and you can't use credentals then, - // besides we don't need them here anyway. - withCredentials: false, - responseType: responseType as any, - // Subscribe to progress events. subscribe() will fire multiple times! - reportProgress: true - } - ) + method, + result.url, + { + body: result.form, + // Observe the response, not the body + observe: 'events', + // This is important as the CORS policy for the bucket is * and you can't use credentals then, + // besides we don't need them here anyway. + withCredentials: false, + responseType: responseType as any, + // Subscribe to progress events. subscribe() will fire multiple times! + reportProgress: true, + }, + ) .pipe(switchMap(this.finishUpload(result))); }; } private finishUpload(result:PrepareUploadResult):(result:HttpEvent) => Observable> { - return event => { + return (event) => { if (event instanceof HttpResponse) { return this .http .get( result.response._links.completeUpload.href, { - observe: 'response' - } + observe: 'response', + }, ); } @@ -106,7 +108,7 @@ export class OpenProjectDirectFileUploadService extends OpenProjectFileUploadSer description: file.description, fileName: file.customName || file.name, fileSize: file.size, - contentType: file.type + contentType: file.type, }; /* @@ -124,14 +126,14 @@ export class OpenProjectDirectFileUploadService extends OpenProjectFileUploadSer const result = this .http .request( - "post", - url, - { - body: formData, - withCredentials: true, - responseType: "json" as any - } - ) + 'post', + url, + { + body: formData, + withCredentials: true, + responseType: 'json' as any, + }, + ) .toPromise() .then((res) => { const form = new FormData(); @@ -140,7 +142,7 @@ export class OpenProjectDirectFileUploadService extends OpenProjectFileUploadSer form.append(key, value); }); - return { url: res._links.addAttachment.href, form: form, response: res }; + return { url: res._links.addAttachment.href, form, response: res }; }); return result; diff --git a/frontend/src/app/core/file-upload/op-file-upload.service.spec.ts b/frontend/src/app/core/file-upload/op-file-upload.service.spec.ts index a5d2fc4f9a..b877a732b7 100644 --- a/frontend/src/app/core/file-upload/op-file-upload.service.spec.ts +++ b/frontend/src/app/core/file-upload/op-file-upload.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ +import { OpenProjectDirectFileUploadService } from 'core-app/core/file-upload/op-direct-file-upload.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { getTestBed, TestBed } from '@angular/core/testing'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { States } from 'core-app/core/states/states.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpenProjectFileUploadService, UploadFile, UploadResult } from './op-file-upload.service'; -import { OpenProjectDirectFileUploadService } from "core-app/core/file-upload/op-direct-file-upload.service"; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { getTestBed, TestBed } from "@angular/core/testing"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { States } from "core-app/core/states/states.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; describe('opFileUpload service', () => { let injector:TestBed; @@ -47,8 +47,8 @@ describe('opFileUpload service', () => { I18nService, OpenProjectFileUploadService, OpenProjectDirectFileUploadService, - HalResourceService - ] + HalResourceService, + ], }); injector = getTestBed(); @@ -61,16 +61,16 @@ describe('opFileUpload service', () => { }); describe('when uploading multiple files', () => { - var result:UploadResult; - const file:UploadFile = new File([ JSON.stringify({ + let result:UploadResult; + const file:UploadFile = new File([JSON.stringify({ name: 'name', - description: 'description' + description: 'description', })], 'name'); beforeEach(() => { result = service.upload('/my/api/path', [file, file]); - httpMock.match(`/my/api/path`).forEach((req) => { - expect(req.request.method).toBe("POST"); + httpMock.match('/my/api/path').forEach((req) => { + expect(req.request.method).toBe('POST'); req.flush({}); }); }); diff --git a/frontend/src/app/core/file-upload/op-file-upload.service.ts b/frontend/src/app/core/file-upload/op-file-upload.service.ts index 7cec71fc1a..111e41c9d9 100644 --- a/frontend/src/app/core/file-upload/op-file-upload.service.ts +++ b/frontend/src/app/core/file-upload/op-file-upload.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { HttpClient, HttpEvent, HttpEventType, HttpResponse } from "@angular/common/http"; -import { Observable } from "rxjs"; -import { filter, map, share } from "rxjs/operators"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { + HttpClient, HttpEvent, HttpEventType, HttpResponse, +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { filter, map, share } from 'rxjs/operators'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export interface UploadFile extends File { description?:string; customName?:string; } - export interface UploadBlob extends Blob { description?:string; customName?:string; @@ -61,7 +62,7 @@ export interface MappedUploadResult { @Injectable() export class OpenProjectFileUploadService { constructor(protected http:HttpClient, - protected halResource:HalResourceService) { + protected halResource:HalResourceService) { } /** @@ -75,11 +76,9 @@ export class OpenProjectFileUploadService { public uploadAndMapResponse(url:string, files:UploadFile[], method = 'post') { const { uploads, finished } = this.upload(url, files); const mapped = finished - .then((result:HalResource[]) => result.map((el:HalResource) => { - return { response: el, uploadUrl: el.staticDownloadLocation.href }; - })) as Promise<{ response:HalResource, uploadUrl:string }[]>; + .then((result:HalResource[]) => result.map((el:HalResource) => ({ response: el, uploadUrl: el.staticDownloadLocation.href }))) as Promise<{ response:HalResource, uploadUrl:string }[]>; - return { uploads: uploads, finished: mapped } as MappedUploadResult; + return { uploads, finished: mapped } as MappedUploadResult; } /** @@ -104,7 +103,7 @@ export class OpenProjectFileUploadService { const formData = new FormData(); const metadata = { description: file.description, - fileName: file.customName || file.name + fileName: file.customName || file.name, }; // add the metadata object @@ -119,20 +118,20 @@ export class OpenProjectFileUploadService { const observable = this .http .request( - method, - url, - { - body: formData, - // Observe the response, not the body - observe: 'events', - withCredentials: true, - responseType: responseType as any, - // Subscribe to progress events. subscribe() will fire multiple times! - reportProgress: true - } - ) + method, + url, + { + body: formData, + // Observe the response, not the body + observe: 'events', + withCredentials: true, + responseType: responseType as any, + // Subscribe to progress events. subscribe() will fire multiple times! + reportProgress: true, + }, + ) .pipe( - share() + share(), ); return [file, observable] as UploadInProgress; @@ -144,14 +143,12 @@ export class OpenProjectFileUploadService { * @param {UploadInProgress[]} uploads */ private whenFinished(uploads:UploadInProgress[]):Promise { - const promises = uploads.map(([_, observable]) => { - return observable - .pipe( - filter((evt) => evt.type === HttpEventType.Response), - map((evt:HttpResponse) => this.halResource.createHalResource(evt.body)) - ) - .toPromise(); - }); + const promises = uploads.map(([_, observable]) => observable + .pipe( + filter((evt) => evt.type === HttpEventType.Response), + map((evt:HttpResponse) => this.halResource.createHalResource(evt.body)), + ) + .toPromise()); return Promise.all(promises); } diff --git a/frontend/src/app/core/forms/forms.service.spec.ts b/frontend/src/app/core/forms/forms.service.spec.ts index 0457ba9a65..ad65d4edad 100644 --- a/frontend/src/app/core/forms/forms.service.spec.ts +++ b/frontend/src/app/core/forms/forms.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClient } from '@angular/common/http'; +import { FormBuilder } from '@angular/forms'; import { FormsService } from './forms.service'; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { HttpClient } from "@angular/common/http"; -import { FormBuilder } from "@angular/forms"; describe('FormsService', () => { let service:FormsService; @@ -10,21 +10,21 @@ describe('FormsService', () => { let httpTestingController:HttpTestingController; const testFormUrl = 'http://op.com/form'; const formModel = { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project", - "name": "Parent project" + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', + name: 'Parent project', }, - "users": [ + users: [ { - "href": "/api/v3/users/26", - "title": "User 1", - "name": "User 1" - } - ] - } + href: '/api/v3/users/26', + title: 'User 1', + name: 'User 1', + }, + ], + }, }; const formBuilder = new FormBuilder(); @@ -76,57 +76,58 @@ describe('FormsService', () => { // @ts-ignore const formattedModel = service.formatModelToSubmit(formModel); const expectedResult = { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', }, - "users": [ + users: [ { - "href": "/api/v3/users/26", - } - ] - } + href: '/api/v3/users/26', + }, + ], + }, }; expect(formattedModel).toEqual(expectedResult); }); it('should set the backend errors in the FormGroup', () => { - const form= formBuilder.group({ + const form = formBuilder.group({ ...formModel, _links: formBuilder.group(formModel._links), }); const backEndErrorResponse = { error: { - "_type": "Error", - "errorIdentifier": "urn:openproject-org:api:v3:errors:MultipleErrors", - "message": "Multiple field constraints have been violated.", - "_embedded": { - "errors": [ + _type: 'Error', + errorIdentifier: 'urn:openproject-org:api:v3:errors:MultipleErrors', + message: 'Multiple field constraints have been violated.', + _embedded: { + errors: [ { - "_type": "Error", - "errorIdentifier": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation", - "message": "Name can't be blank.", - "_embedded": { - "details": { - "attribute": "name" - } - } + _type: 'Error', + errorIdentifier: 'urn:openproject-org:api:v3:errors:PropertyConstraintViolation', + message: "Name can't be blank.", + _embedded: { + details: { + attribute: 'name', + }, + }, }, { - "_type": "Error", - "errorIdentifier": "urn:openproject-org:api:v3:errors:PropertyConstraintViolation", - "message": "Identifier can't be blank.", - "_embedded": { - "details": { - "attribute": "parent" - } - } + _type: 'Error', + errorIdentifier: 'urn:openproject-org:api:v3:errors:PropertyConstraintViolation', + message: "Identifier can't be blank.", + _embedded: { + details: { + attribute: 'parent', + }, + }, }, - ] - } - }, status: 422 + ], + }, + }, + status: 422, }; // @ts-ignore diff --git a/frontend/src/app/core/forms/forms.service.ts b/frontend/src/app/core/forms/forms.service.ts index 2d39053fee..ebfb601f85 100644 --- a/frontend/src/app/core/forms/forms.service.ts +++ b/frontend/src/app/core/forms/forms.service.ts @@ -1,40 +1,39 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpErrorResponse } from "@angular/common/http"; -import { FormGroup } from "@angular/forms"; -import { catchError, map } from "rxjs/operators"; -import { Observable } from "rxjs"; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { FormGroup } from '@angular/forms'; +import { catchError, map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class FormsService { - constructor( private _httpClient:HttpClient, ) { } - submit$(form:FormGroup, resourceEndpoint:string, resourceId?:string, formHttpMethod?: 'post' | 'patch', formSchema?:IOPFormSchema):Observable { + submit$(form:FormGroup, resourceEndpoint:string, resourceId?:string, formHttpMethod?:'post' | 'patch', formSchema?:IOPFormSchema):Observable { const modelToSubmit = this.formatModelToSubmit(form.getRawValue(), formSchema); const httpMethod = resourceId ? 'patch' : (formHttpMethod || 'post'); const url = resourceId ? `${resourceEndpoint}/${resourceId}` : resourceEndpoint; return this._httpClient - [httpMethod]( + [httpMethod]( url, modelToSubmit, { withCredentials: true, - responseType: 'json' - } + responseType: 'json', + }, ) .pipe( catchError((error:HttpErrorResponse) => { - if (error.status == 422 ) { + if (error.status == 422) { this.handleBackendFormValidationErrors(error, form); } throw error; - }) + }), ); } @@ -45,34 +44,14 @@ export class FormsService { .post( `${resourceEndpoint}/form`, modelToSubmit, - { - withCredentials: true, - responseType: 'json' - } - ) - .pipe( - map((response: HalSource) => this.getFormattedErrors(Object.values(response?._embedded?.validationErrors))), - map((formattedErrors: IFormattedValidationError[]) => this.setFormValidationErrors(formattedErrors, form)), - ); - } - - getFormBackendValidationError$(formValue: {[key:string]: any}, resourceEndpoint:string, limitValidationToKeys?:string | string[], formSchema?:IOPFormSchema) { - const modelToSubmit = this.formatModelToSubmit(formValue, formSchema); - - return this._httpClient - .post( - resourceEndpoint, - modelToSubmit, { withCredentials: true, responseType: 'json', - headers: { - 'content-type': 'application/json; charset=utf-8' - } - } + }, ) .pipe( - map((response: HalSource) => this.getAllFormValidationErrors(response._embedded.validationErrors, limitValidationToKeys)) + map((response:HalSource) => this.getFormattedErrors(Object.values(response?._embedded?.validationErrors))), + map((formattedErrors:IFormattedValidationError[]) => this.setFormValidationErrors(formattedErrors, form)), ); } @@ -84,14 +63,14 @@ export class FormsService { * in the shape of '{href:hrefValue}' in order to fit the backend expectations. * */ private formatModelToSubmit(formModel:IOPFormModel, formSchema:IOPFormSchema = {}):IOPFormModel { - let {_links:linksModel, ...mainModel} = formModel; + let { _links: linksModel, ...mainModel } = formModel; const resourcesModel = linksModel || Object.keys(formSchema) - .filter(formSchemaKey => !!formSchema[formSchemaKey]?.type && formSchema[formSchemaKey]?.location === '_links') + .filter((formSchemaKey) => !!formSchema[formSchemaKey]?.type && formSchema[formSchemaKey]?.location === '_links') .reduce((result, formSchemaKey) => { - const {[formSchemaKey]:keyToRemove, ...mainModelWithoutResource} = mainModel; + const { [formSchemaKey]: keyToRemove, ...mainModelWithoutResource } = mainModel; mainModel = mainModelWithoutResource; - return {...result, [formSchemaKey]: formModel[formSchemaKey]}; + return { ...result, [formSchemaKey]: formModel[formSchemaKey] }; }, {}); const formattedResourcesModel = Object @@ -100,9 +79,9 @@ export class FormsService { const resourceModel = resourcesModel[resourceKey]; // Form.payload resources have a HalLinkSource interface while // API resource options have a IAllowedValue interface - const formattedResourceModel = Array.isArray(resourceModel) ? - resourceModel.map(resourceElement => ({ href: resourceElement?.href || resourceElement?._links?.self?.href || null })) : - { href: resourceModel?.href || resourceModel?._links?.self?.href || null }; + const formattedResourceModel = Array.isArray(resourceModel) + ? resourceModel.map((resourceElement) => ({ href: resourceElement?.href || resourceElement?._links?.self?.href || null })) + : { href: resourceModel?.href || resourceModel?._links?.self?.href || null }; return { ...result, @@ -113,7 +92,7 @@ export class FormsService { return { ...mainModel, _links: formattedResourcesModel, - } + }; } /** HAL resources formatting @@ -125,8 +104,8 @@ export class FormsService { formatModelToEdit(formModel:IOPFormModel = {}):IOPFormModel { const { _links: resourcesModel, _meta: metaModel, ...otherElements } = formModel; const otherElementsModel = Object.keys(otherElements) - .filter(key => this.isValue(otherElements[key])) - .reduce((model, key) => ({...model, [key]:otherElements[key]}), {}); + .filter((key) => this.isValue(otherElements[key])) + .reduce((model, key) => ({ ...model, [key]: otherElements[key] }), {}); const model = { ...otherElementsModel, @@ -138,8 +117,8 @@ export class FormsService { } private handleBackendFormValidationErrors(error:HttpErrorResponse, form:FormGroup):void { - const errors:IOPFormError[] = error?.error?._embedded?.errors ? - error?.error?._embedded?.errors : [error.error]; + const errors:IOPFormError[] = error?.error?._embedded?.errors + ? error?.error?._embedded?.errors : [error.error]; const formErrors = this.getFormattedErrors(errors); this.setFormValidationErrors(formErrors, form); @@ -149,35 +128,32 @@ export class FormsService { errors.forEach((err:any) => { const formControl = form.get(err.key) || form.get('_links')?.get(err.key); - formControl?.setErrors({[err.key]: {message: err.message}}); + formControl?.setErrors({ [err.key]: { message: err.message } }); }); } - private getAllFormValidationErrors(validationErrors:IOPValidationErrors, formControlKeys?:string | string[]): {[key:string]: {message:string}} { + private getAllFormValidationErrors(validationErrors:IOPValidationErrors, formControlKeys?:string | string[]):{ [key:string]:{ message:string } } { const errors = Object.values(validationErrors); const keysToValidate = Array.isArray(formControlKeys) ? formControlKeys : [formControlKeys]; const formErrors = this.getFormattedErrors(errors) - .filter(error => { + .filter((error) => { if (!formControlKeys) { return true; - } else { - return keysToValidate.includes(error.key); } + return keysToValidate.includes(error.key); }) - .reduce((result, { key, message }) => { - return { - ...result, - [key]: {message} - } - }, {}) + .reduce((result, { key, message }) => ({ + ...result, + [key]: { message }, + }), {}); - return formErrors + return formErrors; } private getFormattedErrors(errors:IOPFormError[]):IFormattedValidationError[] { - const formattedErrors = errors.map(err => ({ + const formattedErrors = errors.map((err) => ({ key: err._embedded.details.attribute, - message: err.message + message: err.message, })); return formattedErrors; @@ -188,13 +164,13 @@ export class FormsService { const resource = resourcesModel[resourceKey]; // ng-select needs a 'name' in order to show the label // We need to add it in case of the form payload (HalLinkSource) - const resourceModel = Array.isArray(resource) ? - resource.map(resourceElement => ({...resourceElement, name: resourceElement?.name || resourceElement?.title})) : - {...resource, name: resource?.name || resource?.title}; + const resourceModel = Array.isArray(resource) + ? resource.map((resourceElement) => ({ ...resourceElement, name: resourceElement?.name || resourceElement?.title })) + : { ...resource, name: resource?.name || resource?.title }; result = { ...result, - ...this.isValue(resourceModel) && {[resourceKey]: resourceModel}, + ...this.isValue(resourceModel) && { [resourceKey]: resourceModel }, }; return result; diff --git a/frontend/src/app/core/forms/typings.d.ts b/frontend/src/app/core/forms/typings.d.ts index bfcf805112..e7b40166a3 100644 --- a/frontend/src/app/core/forms/typings.d.ts +++ b/frontend/src/app/core/forms/typings.d.ts @@ -1,5 +1,5 @@ interface IOPFormSettingsResource { - _type?:"Form"; + _type?:'Form'; _embedded:IOPFormSettings; _links?:{ self:IOPApiCall; @@ -17,7 +17,7 @@ interface IOPFormSettings { } interface IOPFormSchema { - _type?:"Schema"; + _type?:'Schema'; _dependencies?:unknown[]; _attributeGroups?:IOPAttributeGroup[]; lockVersion?:number; @@ -85,11 +85,10 @@ interface IOPApiOption { } interface IOPAttributeGroup { - _type: - | "WorkPackageFormAttributeGroup" - | "WorkPackageFormChildrenQueryGroup" - | "WorkPackageFormRelationQueryGroup" - | unknown; + _type:| 'WorkPackageFormAttributeGroup' + | 'WorkPackageFormChildrenQueryGroup' + | 'WorkPackageFormRelationQueryGroup' + | unknown; name:string; attributes:string[]; } @@ -105,8 +104,8 @@ interface IOPAllowedValue { } type OPFieldType = 'String' | 'Integer' | 'Float' | 'Boolean' | 'Date' | 'DateTime' | 'Formattable' | - 'Priority' | 'Status' | 'Type' | 'User' | 'Version' | 'TimeEntriesActivity' | 'Category' | - 'CustomOption' | 'Project' | 'ProjectStatus' | 'Password'; +'Priority' | 'Status' | 'Type' | 'User' | 'Version' | 'TimeEntriesActivity' | 'Category' | +'CustomOption' | 'Project' | 'ProjectStatus' | 'Password'; interface IOPFormError { errorIdentifier:string; @@ -137,7 +136,3 @@ interface IOPFormErrors { interface IOPValidationErrors { [key:string]:IOPFormError; } - - - - diff --git a/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts b/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts index 98a9f68c14..f078fb920d 100644 --- a/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts +++ b/frontend/src/app/core/global_search/global-search-work-packages-entry.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -40,9 +40,7 @@ export const globalSearchWorkPackagesSelectorEntry = 'global-search-work-package - ` + `, }) export class GlobalSearchWorkPackagesEntryComponent { } - - diff --git a/frontend/src/app/core/global_search/global-search-work-packages.component.ts b/frontend/src/app/core/global_search/global-search-work-packages.component.ts index 225c2bb182..81cb11e8a8 100644 --- a/frontend/src/app/core/global_search/global-search-work-packages.component.ts +++ b/frontend/src/app/core/global_search/global-search-work-packages.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { + AfterViewInit, ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, Renderer2, +} from '@angular/core'; import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { WorkPackageTableConfigurationObject } from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { WorkPackageTableConfigurationObject } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { debounceTime, distinctUntilChanged, skip } from "rxjs/operators"; -import { combineLatest } from "rxjs"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { debounceTime, distinctUntilChanged, skip } from 'rxjs/operators'; +import { combineLatest } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; export const globalSearchWorkPackagesSelector = 'global-search-work-packages'; @@ -49,11 +51,12 @@ export const globalSearchWorkPackagesSelector = 'global-search-work-packages'; [queryProps]="queryProps" [configuration]="tableConfiguration"> - ` + `, }) export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin implements OnInit, OnDestroy, AfterViewInit { public queryProps:{ [key:string]:any }; + public resultsHidden = false; public tableConfiguration:WorkPackageTableConfigurationObject = { @@ -63,35 +66,35 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple inlineCreateEnabled: false, withFilters: true, showFilterButton: true, - filterButtonText: this.I18n.t('js.button_advanced_filter') + filterButtonText: this.I18n.t('js.button_advanced_filter'), }; constructor(readonly FocusHelper:FocusHelperService, - readonly elementRef:ElementRef, - readonly renderer:Renderer2, - readonly I18n:I18nService, - readonly halResourceService:HalResourceService, - readonly globalSearchService:GlobalSearchService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly querySpace:IsolatedQuerySpace, - readonly wpFilters:WorkPackageFiltersService, - readonly cdRef:ChangeDetectorRef, - private UrlParamsHelper:UrlParamsHelperService) { + readonly elementRef:ElementRef, + readonly renderer:Renderer2, + readonly I18n:I18nService, + readonly halResourceService:HalResourceService, + readonly globalSearchService:GlobalSearchService, + readonly wpTableFilters:WorkPackageViewFiltersService, + readonly querySpace:IsolatedQuerySpace, + readonly wpFilters:WorkPackageFiltersService, + readonly cdRef:ChangeDetectorRef, + private UrlParamsHelper:UrlParamsHelperService) { super(); } ngAfterViewInit() { combineLatest([ this.globalSearchService.searchTerm$, - this.globalSearchService.projectScope$ + this.globalSearchService.projectScope$, ]) .pipe( skip(1), distinctUntilChanged(), debounceTime(10), - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(([newSearchTerm, newProjectScope]) => { + .subscribe(([]) => { this.wpFilters.visible = false; this.setQueryProps(); }); @@ -99,9 +102,9 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple this.globalSearchService .resultsHidden$ .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe((resultsHidden:boolean) => this.resultsHidden = resultsHidden); + .subscribe((resultsHidden:boolean) => (this.resultsHidden = resultsHidden)); } ngOnInit():void { @@ -116,8 +119,8 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple filters.push({ search: { operator: '**', - values: [this.globalSearchService.searchTerm] - } + values: [this.globalSearchService.searchTerm], + }, }); } @@ -125,8 +128,8 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple filters.push({ subprojectId: { operator: '!*', - values: [] - } + values: [], + }, }); columns = ['id', 'subject', 'type', 'status', 'updatedAt']; } @@ -135,8 +138,8 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple filters.push({ subprojectId: { operator: '*', - values: [] - } + values: [], + }, }); } @@ -144,8 +147,7 @@ export class GlobalSearchWorkPackagesComponent extends UntilDestroyedMixin imple 'columns[]': columns, filters: JSON.stringify(filters), sortBy: JSON.stringify([['updatedAt', 'desc']]), - showHierarchies: false + showHierarchies: false, }; } } - diff --git a/frontend/src/app/core/global_search/input/global-search-input.component.ts b/frontend/src/app/core/global_search/input/global-search-input.component.ts index aaa6e7f3ae..61b636dffa 100644 --- a/frontend/src/app/core/global_search/input/global-search-input.component.ts +++ b/frontend/src/app/core/global_search/input/global-search-input.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,32 +27,33 @@ //++ import { + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, + NgZone, OnDestroy, ViewChild, ViewEncapsulation, - NgZone, AfterViewInit } from '@angular/core'; -import { Observable, of } from "rxjs"; -import { map, tap } from "rxjs/operators"; -import { APIV3Service } from "../../apiv3/api-v3.service"; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { LinkHandling } from "core-app/shared/helpers/link-handling/link-handling"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { ContainHelpers } from "core-app/shared/directives/focus/contain-helpers"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { OpAutocompleterComponent } from "core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Observable, of } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { ContainHelpers } from 'core-app/shared/directives/focus/contain-helpers'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { OpAutocompleterComponent } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { APIV3Service } from '../../apiv3/api-v3.service'; export const globalSearchSelector = 'global-search-input'; @@ -81,29 +82,31 @@ interface SearchOptionItem { './global-search.component.sass', ], // Necessary because of ng-select - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, }) export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { @ViewChild('btn', { static: true }) btn:ElementRef; + @ViewChild(OpAutocompleterComponent, { static: true }) public ngSelectComponent:OpAutocompleterComponent; public expanded = false; + public markable = false; + public isLoading = false; - getAutocompleterData = (query:string):Observable => { - return this.autocompleteWorkPackages(query); - }; + getAutocompleterData = (query:string):Observable => this.autocompleteWorkPackages(query); public autocompleterOptions = { - filters:[], - resource:'work_packages', - searchKey:'subjectOrId', - getOptionsFn: this.getAutocompleterData + filters: [], + resource: 'work_packages', + searchKey: 'subjectOrId', + getOptionsFn: this.getAutocompleterData, }; /** Remember the current value */ public currentValue = ''; + public isFocusedDirectly = (this.globalSearchService.searchTerm.length > 0); /** Remember the item that best matches the query. @@ -113,27 +116,26 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { private unregisterGlobalListener:Function|undefined; - private isInitialized :boolean = false; public text:{ [key:string]:string } = { all_projects: this.I18n.t('js.global_search.all_projects'), current_project: this.I18n.t('js.global_search.current_project'), current_project_and_all_descendants: this.I18n.t('js.global_search.current_project_and_all_descendants'), search: this.I18n.t('js.global_search.search'), - search_dots: this.I18n.t('js.global_search.search') + ' ...', - close_search: this.I18n.t('js.global_search.close_search') + search_dots: `${this.I18n.t('js.global_search.search')} ...`, + close_search: this.I18n.t('js.global_search.close_search'), }; constructor(readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly apiV3Service:APIV3Service, - readonly PathHelperService:PathHelperService, - readonly halResourceService:HalResourceService, - readonly globalSearchService:GlobalSearchService, - readonly currentProjectService:CurrentProjectService, - readonly deviceService:DeviceService, - readonly cdRef:ChangeDetectorRef, - readonly halNotification:HalResourceNotificationService, - readonly ngZone:NgZone) { + readonly I18n:I18nService, + readonly apiV3Service:APIV3Service, + readonly pathHelperService:PathHelperService, + readonly halResourceService:HalResourceService, + readonly globalSearchService:GlobalSearchService, + readonly currentProjectService:CurrentProjectService, + readonly deviceService:DeviceService, + readonly cdRef:ChangeDetectorRef, + readonly halNotification:HalResourceNotificationService, + readonly ngZone:NgZone) { } ngAfterViewInit():void { @@ -175,7 +177,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { public redirectToWp(id:string, event:MouseEvent) { event.stopImmediatePropagation(); - if (LinkHandling.isClickedWithModifier(event)) { + if (isClickedWithModifier(event)) { return true; } @@ -185,7 +187,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { } public wpPath(id:string) { - return this.PathHelperService.workPackagePath(id); + return this.pathHelperService.workPackagePath(id); } public search($event:any) { @@ -195,7 +197,6 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { // close menu when input field is empty public openCloseMenu(searchedTerm:string) { - this.ngSelectComponent.ngSelectInstance.isOpen = (searchedTerm.trim().length > 0); } @@ -209,7 +210,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { if (!this.deviceService.isMobile) { this.expanded = (this.ngSelectComponent.ngSelectInstance.searchTerm !== null && this.ngSelectComponent.ngSelectInstance.searchTerm.length > 0); this.ngSelectComponent.ngSelectInstance.isOpen = false; - this.selectedItem =null; + this.selectedItem = null; this.toggleTopMenuClass(); } } @@ -270,31 +271,27 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { // Hide highlighting of ng-option this.markable = false; - const hashFreeQuery = this.queryWithoutHash(query); - - this.isLoading = true + this.isLoading = true; return this .fetchSearchResults(hashFreeQuery, hashFreeQuery !== query) .get() .pipe( - map((collection) => { - return this.searchResultsToOptions(collection.elements, hashFreeQuery); - }), + map((collection) => this.searchResultsToOptions(collection.elements, hashFreeQuery)), tap(() => { this.isLoading = false; - this.setMarkedOption();}) + this.setMarkedOption(); + }), ); } // Remove ID marker # when searching for # private queryWithoutHash(query:string) { - if (query.match(/^#(\d+)/)) { + if (/^#(\d+)/.exec(query)) { return query.substr(1); - } else { - return query; } + return query; } private fetchSearchResults(query:string, idOnly:boolean) { @@ -306,7 +303,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { private searchResultsToOptions(results:WorkPackageResource[], query:string) { const searchItems = results.map((wp) => { - const item = { + const item = { id: wp.id!, subject: wp.subject, status: wp.status.name, @@ -314,7 +311,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { href: wp.href, project: wp.project.name, author: wp.author, - type: wp.type + type: wp.type, } as SearchResultItem; // If we have a direct hit, we choose it to be the selected element. if (query === wp.id!.toString()) { @@ -347,9 +344,7 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { } searchOptions.push('all_projects'); - return searchOptions.map((suggestion:string) => { - return { projectScope: suggestion, text: this.text[suggestion] }; - }); + return searchOptions.map((suggestion:string) => ({ projectScope: suggestion, text: this.text[suggestion] })); } /* @@ -407,6 +402,8 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { this.submitNonEmptySearch(); break; } + default: // Do nothing + break; } } @@ -415,9 +412,9 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { if (this.currentValue.length > 0) { this.ngSelectComponent.ngSelectInstance.close(); // Work package results can update without page reload. - if (!forcePageLoad && - this.globalSearchService.isAfterSearch() && - this.globalSearchService.currentTab === 'work_packages') { + if (!forcePageLoad + && this.globalSearchService.isAfterSearch() + && this.globalSearchService.currentTab === 'work_packages') { window.history .replaceState({}, `${I18n.t('global_search.search')}: ${this.ngSelectComponent.ngSelectInstance.searchTerm}`, @@ -450,5 +447,3 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { jQuery('.op-app-header').toggleClass('op-app-header_search-open', this.expanded); } } - - diff --git a/frontend/src/app/core/global_search/openproject-global-search.module.ts b/frontend/src/app/core/global_search/openproject-global-search.module.ts index a9a639f254..89eddfe8ca 100644 --- a/frontend/src/app/core/global_search/openproject-global-search.module.ts +++ b/frontend/src/app/core/global_search/openproject-global-search.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,15 +27,15 @@ //++ import { NgModule } from '@angular/core'; -import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/openproject-work-packages.module"; -import { GlobalSearchInputComponent } from "core-app/core/global_search/input/global-search-input.component"; -import { GlobalSearchWorkPackagesComponent } from "core-app/core/global_search/global-search-work-packages.component"; -import { GlobalSearchTabsComponent } from "core-app/core/global_search/tabs/global-search-tabs.component"; -import { GlobalSearchTitleComponent } from "core-app/core/global_search/title/global-search-title.component"; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { GlobalSearchWorkPackagesEntryComponent } from "core-app/core/global_search/global-search-work-packages-entry.component"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; -import { OPSharedModule } from "core-app/shared/shared.module"; +import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; +import { GlobalSearchInputComponent } from 'core-app/core/global_search/input/global-search-input.component'; +import { GlobalSearchWorkPackagesComponent } from 'core-app/core/global_search/global-search-work-packages.component'; +import { GlobalSearchTabsComponent } from 'core-app/core/global_search/tabs/global-search-tabs.component'; +import { GlobalSearchTitleComponent } from 'core-app/core/global_search/title/global-search-title.component'; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { GlobalSearchWorkPackagesEntryComponent } from 'core-app/core/global_search/global-search-work-packages-entry.component'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; +import { OPSharedModule } from 'core-app/shared/shared.module'; @NgModule({ imports: [ @@ -52,7 +52,6 @@ import { OPSharedModule } from "core-app/shared/shared.module"; GlobalSearchWorkPackagesComponent, GlobalSearchTabsComponent, GlobalSearchTitleComponent, - ] + ], }) export class OpenprojectGlobalSearchModule { } - diff --git a/frontend/src/app/core/global_search/services/global-search.service.spec.ts b/frontend/src/app/core/global_search/services/global-search.service.spec.ts index 465aefa290..531481f2d4 100644 --- a/frontend/src/app/core/global_search/services/global-search.service.spec.ts +++ b/frontend/src/app/core/global_search/services/global-search.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { TestBed, waitForAsync } from "@angular/core/testing"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { States } from "core-app/core/states/states.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { States } from 'core-app/core/states/states.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; -describe('Global search service', function() { +describe('Global search service', () => { let service:GlobalSearchService; let CurrentProject:CurrentProjectService; let CurrentProjectSpy; @@ -51,13 +51,13 @@ describe('Global search service', function() { APIV3Service, CurrentProjectService, GlobalSearchService, - ] + ], }) - .compileComponents() - .then(() => { - CurrentProject = TestBed.inject(CurrentProjectService); - service = TestBed.inject(GlobalSearchService); - }); + .compileComponents() + .then(() => { + CurrentProject = TestBed.inject(CurrentProjectService); + service = TestBed.inject(GlobalSearchService); + }); })); describe('outside a project', () => { diff --git a/frontend/src/app/core/global_search/services/global-search.service.ts b/frontend/src/app/core/global_search/services/global-search.service.ts index 3b9126137c..bac0faf4e2 100644 --- a/frontend/src/app/core/global_search/services/global-search.service.ts +++ b/frontend/src/app/core/global_search/services/global-search.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,37 +26,42 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable , Injector } from '@angular/core'; +import { Injectable, Injector } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Injectable() export class GlobalSearchService { private _searchTerm = new BehaviorSubject(''); + public searchTerm$ = this._searchTerm.asObservable(); // Default selected tab is Work Packages private _currentTab = new BehaviorSubject('work_packages'); + public currentTab$ = this._currentTab.asObservable(); // Default project scope is "this project and all subprojets" private _projectScope = new BehaviorSubject(''); + public projectScope$ = this._projectScope.asObservable(); private _tabs = new BehaviorSubject([]); + public tabs$ = this._tabs.asObservable(); // Sometimes we need to be able to hide the search results altogether, i.e. while expecting a full page reload. private _resultsHidden = new BehaviorSubject(false); + public resultsHidden$ = this._resultsHidden.asObservable(); constructor(protected I18n:I18nService, - protected injector:Injector, - protected PathHelper:PathHelperService, - protected currentProjectService:CurrentProjectService) { + protected injector:Injector, + protected PathHelper:PathHelperService, + protected currentProjectService:CurrentProjectService) { this.initialize(); } @@ -81,10 +86,10 @@ export class GlobalSearchService { } } - private loadGonData():{available_search_types:string[], - search_term:string, - project_scope:string, - current_tab:string}|null { + private loadGonData():{ available_search_types:string[], + search_term:string, + project_scope:string, + current_tab:string }|null { try { return (window as any).gon.global_search; } catch (e) { @@ -101,7 +106,7 @@ export class GlobalSearchService { if (this.currentProjectService.path && this.projectScope !== 'all') { searchPath = this.currentProjectService.path; } - searchPath = searchPath + `/search?${this.searchQueryParams()}`; + searchPath += `/search?${this.searchQueryParams()}`; return searchPath; } diff --git a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts index 6108a7f0d1..8544c36740 100644 --- a/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts +++ b/frontend/src/app/core/global_search/tabs/global-search-tabs.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,26 +27,27 @@ //++ import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; -import { Subscription } from "rxjs"; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { ScrollableTabsComponent } from "core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component"; -import { TabDefinition } from "core-app/shared/components/tabs/tab.interface"; +import { Subscription } from 'rxjs'; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { ScrollableTabsComponent } from 'core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component'; +import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; export const globalSearchTabsSelector = 'global-search-tabs'; @Component({ selector: globalSearchTabsSelector, - templateUrl: '../../../shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html' + templateUrl: '../../../shared/components/tabs/scrollable-tabs/scrollable-tabs.component.html', }) export class GlobalSearchTabsComponent extends ScrollableTabsComponent implements OnDestroy { private currentTabSub:Subscription; + private tabsSub:Subscription; public classes:string[] = ['global-search--tabs', 'scrollable-tabs']; constructor(readonly globalSearchService:GlobalSearchService, - cdRef:ChangeDetectorRef) { + cdRef:ChangeDetectorRef) { super(cdRef); } @@ -61,7 +62,7 @@ export class GlobalSearchTabsComponent extends ScrollableTabsComponent implement .tabs$ .subscribe((tabs) => { this.tabs = tabs; - this.tabs.map((tab) => tab.path = '#'); + this.tabs.map((tab) => (tab.path = '#')); }); } diff --git a/frontend/src/app/core/global_search/title/global-search-title.component.ts b/frontend/src/app/core/global_search/title/global-search-title.component.ts index 571efda23b..62c2e5ad2e 100644 --- a/frontend/src/app/core/global_search/title/global-search-title.component.ts +++ b/frontend/src/app/core/global_search/title/global-search-title.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,25 +25,30 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Injector, Input, OnDestroy } from '@angular/core'; +import { + ChangeDetectorRef, Component, ElementRef, Injector, Input, OnDestroy, +} from '@angular/core'; import { distinctUntilChanged } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { GlobalSearchService } from "core-app/core/global_search/services/global-search.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { GlobalSearchService } from 'core-app/core/global_search/services/global-search.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; export const globalSearchTitleSelector = 'global-search-title'; @Component({ selector: 'global-search-title', - templateUrl: './global-search-title.component.html' + templateUrl: './global-search-title.component.html', }) export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements OnDestroy { @Input() public searchTerm:string; + @Input() public project:string; + @Input() public projectScope:string; + @Input() public searchTitle:string; @InjectField() private currentProjectService:CurrentProjectService; @@ -52,14 +57,14 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O all_projects: this.I18n.t('js.global_search.title.all_projects'), project_and_subprojects: this.I18n.t('js.global_search.title.project_and_subprojects'), search_for: this.I18n.t('js.global_search.title.search_for'), - in: this.I18n.t('js.label_in') + in: this.I18n.t('js.label_in'), }; constructor(readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - readonly globalSearchService:GlobalSearchService, - readonly I18n:I18nService, - readonly injector:Injector) { + readonly cdRef:ChangeDetectorRef, + readonly globalSearchService:GlobalSearchService, + readonly I18n:I18nService, + readonly injector:Injector) { super(); } @@ -67,11 +72,11 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O // Listen on changes of search input value and project scope combineLatest([ this.globalSearchService.searchTerm$, - this.globalSearchService.projectScope$ + this.globalSearchService.projectScope$, ]) .pipe( distinctUntilChanged(), - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(([newSearchTerm, newProjectScope]) => { this.searchTerm = newSearchTerm; @@ -86,17 +91,17 @@ export class GlobalSearchTitleComponent extends UntilDestroyedMixin implements O const currentProjectName = this.currentProjectService.name ? this.currentProjectService.name : ''; switch (scope) { - case 'all': - return this.text.all_projects; - break; - case 'current_project': - return currentProjectName; - break; - case '': - return currentProjectName + ' ' + this.text.project_and_subprojects; - break; - default: - return ''; + case 'all': + return this.text.all_projects; + break; + case 'current_project': + return currentProjectName; + break; + case '': + return `${currentProjectName} ${this.text.project_and_subprojects}`; + break; + default: + return ''; } } } diff --git a/frontend/src/app/core/gon/gon.service.ts b/frontend/src/app/core/gon/gon.service.ts index 0a42685d47..95bf26f1d0 100644 --- a/frontend/src/app/core/gon/gon.service.ts +++ b/frontend/src/app/core/gon/gon.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { IFCGonDefinition } from "../../features/bim/ifc_models/pages/viewer/ifc-models-data.service"; +import { Injectable } from '@angular/core'; +import { IFCGonDefinition } from '../../features/bim/ifc_models/pages/viewer/ifc-models-data.service'; declare global { interface Window { @@ -36,8 +36,8 @@ declare global { } export interface GonType { - [key:string]:unknown; - ifc_models:IFCGonDefinition; + [key:string]:unknown; + ifc_models:IFCGonDefinition; } @Injectable({ providedIn: 'root' }) diff --git a/frontend/src/app/core/html-sanitize/html-sanitize.service.ts b/frontend/src/app/core/html-sanitize/html-sanitize.service.ts index 8aaf68cf42..c2d91f7682 100644 --- a/frontend/src/app/core/html-sanitize/html-sanitize.service.ts +++ b/frontend/src/app/core/html-sanitize/html-sanitize.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable, SecurityContext } from "@angular/core"; -import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; +import { Injectable, SecurityContext } from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @Injectable({ providedIn: 'root' }) export class HTMLSanitizeService { diff --git a/frontend/src/app/core/html/op-title.service.ts b/frontend/src/app/core/html/op-title.service.ts index 9f5ba0cb73..e0887431f7 100644 --- a/frontend/src/app/core/html/op-title.service.ts +++ b/frontend/src/app/core/html/op-title.service.ts @@ -1,5 +1,5 @@ -import { Title } from "@angular/platform-browser"; -import { Injectable } from "@angular/core"; +import { Title } from '@angular/platform-browser'; +import { Injectable } from '@angular/core'; const titlePartsSeparator = ' | '; @@ -23,5 +23,4 @@ export class OpTitleService { this.titleService.setTitle(parts.join(titlePartsSeparator)); } - } diff --git a/frontend/src/app/core/i18n/i18n.service.ts b/frontend/src/app/core/i18n/i18n.service.ts index 36139cb3d7..2a28687e00 100644 --- a/frontend/src/app/core/i18n/i18n.service.ts +++ b/frontend/src/app/core/i18n/i18n.service.ts @@ -1,5 +1,4 @@ -import { Injectable } from "@angular/core"; -import { Observable, combineLatest } from "rxjs"; +import { Injectable } from '@angular/core'; /** * General components @@ -43,7 +42,6 @@ interface ToHumanSizeOptions extends ToNumberOptions { format?:string; } - @Injectable({ providedIn: 'root' }) export class I18nService { private _i18n:GlobalI18n = (window as any).I18n; @@ -61,9 +59,12 @@ export class I18nService { } public toNumber = this._i18n.toNumber.bind(this._i18n); + public toPercentage = this._i18n.toPercentage.bind(this._i18n); + public toCurrency = this._i18n.toCurrency.bind(this._i18n); + public strftime = this._i18n.strftime.bind(this._i18n); - public toHumanSize = this._i18n.toHumanSize.bind(this._i18n); + public toHumanSize = this._i18n.toHumanSize.bind(this._i18n); } diff --git a/frontend/src/app/core/loading-indicator/loading-indicator.service.ts b/frontend/src/app/core/loading-indicator/loading-indicator.service.ts index 038607de57..ac57ea6f3d 100644 --- a/frontend/src/app/core/loading-indicator/loading-indicator.service.ts +++ b/frontend/src/app/core/loading-indicator/loading-indicator.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { Observable } from "rxjs"; -import { tap } from "rxjs/operators"; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; export const indicatorLocationSelector = '.loading-indicator--location'; export const indicatorBackgroundSelector = '.loading-indicator--background'; @@ -41,8 +41,8 @@ export function withLoadingIndicator(indicator:LoadingIndicator, delayStopTim tap( () => indicator.delayedStop(delayStopTime), () => indicator.stop(), - () => indicator.stop() - ) + () => indicator.stop(), + ), ); }; } @@ -55,17 +55,15 @@ export function withDelayedLoadingIndicator(indicator:() => LoadingIndicator) tap( () => undefined, () => indicator().stop(), - () => indicator().stop() - ) + () => indicator().stop(), + ), ); }; } - export class LoadingIndicator { - private indicatorTemplate = - `
+ `
@@ -107,7 +105,6 @@ export class LoadingIndicator { @Injectable({ providedIn: 'root' }) export class LoadingIndicatorService { - // Provide shortcut to the primarily used indicators public get table() { return this.indicator('table'); @@ -130,7 +127,7 @@ export class LoadingIndicatorService { // Return an indicator by name or element public indicator(indicator:string|JQuery):LoadingIndicator { if (typeof indicator === 'string') { - indicator = this.getIndicatorAt(indicator) as JQuery; + indicator = this.getIndicatorAt(indicator); } return new LoadingIndicator(indicator); diff --git a/frontend/src/app/core/main-menu/main-menu-navigation.service.ts b/frontend/src/app/core/main-menu/main-menu-navigation.service.ts index 4e4602c824..e80cdf1f42 100644 --- a/frontend/src/app/core/main-menu/main-menu-navigation.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-navigation.service.ts @@ -1,36 +1,34 @@ -import { BehaviorSubject } from "rxjs"; -import { filter, take } from "rxjs/operators"; -import { Injectable } from "@angular/core"; +import { BehaviorSubject } from 'rxjs'; +import { filter, take } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class MainMenuNavigationService { - public navigationEvents$ = new BehaviorSubject(''); public onActivate(...names:string[]) { return this .navigationEvents$ .pipe( - filter(evt => names.indexOf(evt) !== -1), - take(1) + filter((evt) => names.indexOf(evt) !== -1), + take(1), ); } private recreateToggler() { const that = this; // rejigger the main-menu sub-menu functionality. - jQuery("#main-menu .toggler").remove(); // remove the togglers so they're inserted properly later. + jQuery('#main-menu .toggler').remove(); // remove the togglers so they're inserted properly later. - var toggler = jQuery('') - .on('click', function() { + const toggler = jQuery('') + .on('click', function () { const target = jQuery(this); if (target.hasClass('toggler')) { - // TODO: Instead of hiding the sidebar move sidebar's contents to submenus and cache it. jQuery('#sidebar').toggleClass('-hidden', true); - jQuery(".menu_root li").removeClass('open'); - jQuery(".menu_root").removeClass('open').addClass('closed'); + jQuery('.menu_root li').removeClass('open'); + jQuery('.menu_root').removeClass('open').addClass('closed'); const targetLi = target.closest('li'); targetLi @@ -47,17 +45,17 @@ export class MainMenuNavigationService { } private wrapMainItem() { - var mainItems = jQuery('#main-menu li > a').not('ul ul a'); + const mainItems = jQuery('#main-menu li > a').not('ul ul a'); mainItems.wrap((index:number) => { - var item = mainItems[index]; - var elementId = item.id; + const item = mainItems[index]; + const elementId = item.id; - var wrapperElement = jQuery('
'); + const wrapperElement = jQuery('
'); // inherit element id if (elementId) { - wrapperElement.attr('id', elementId + '-wrapper'); + wrapperElement.attr('id', `${elementId}-wrapper`); } return wrapperElement; @@ -65,13 +63,12 @@ export class MainMenuNavigationService { } register() { - // Wrap main item this.wrapMainItem(); // Scroll to the active item or if none found, the active menu wrapper - const selected = document.querySelector('.tree-menu--item.-selected') || - document.querySelector('.main-item-wrapper a.selected'); + const selected = document.querySelector('.tree-menu--item.-selected') + || document.querySelector('.main-item-wrapper a.selected'); selected?.scrollIntoView(); @@ -95,22 +92,23 @@ export class MainMenuNavigationService { function navigateUp(this:any, event:any) { event.preventDefault(); - var target = jQuery(this); + const target = jQuery(this); jQuery(target).parents('li').first().removeClass('open'); - jQuery(".menu_root").removeClass('closed').addClass('open'); + jQuery('.menu_root').removeClass('closed').addClass('open'); - target.parents('li').first().find('.toggler').first().focus(); + target.parents('li').first().find('.toggler').first() + .focus(); // TODO: Instead of hiding the sidebar move sidebar's contents to submenus and cache it. jQuery('#sidebar').toggleClass('-hidden', false); } - jQuery('#main-menu ul.main-menu--children').each(function(_i, child) { - var title = jQuery(child).parents('li').find('.main-item-wrapper .op-menu--item-title').contents()[0].textContent; - var parentURL = jQuery(child).parents('li').find('.main-item-wrapper > a').attr('href'); - var header = jQuery(''); - var upLink = jQuery(''); - var parentLink = jQuery('' + title + ''); + jQuery('#main-menu ul.main-menu--children').each((_i, child) => { + const title = jQuery(child).parents('li').find('.main-item-wrapper .op-menu--item-title').contents()[0].textContent; + const parentURL = jQuery(child).parents('li').find('.main-item-wrapper > a').attr('href'); + const header = jQuery(''); + const upLink = jQuery(''); + const parentLink = jQuery(`${title}`); upLink.attr('title', I18n.t('js.label_up')); upLink.on('click', navigateUp); header.append(upLink); @@ -123,5 +121,4 @@ export class MainMenuNavigationService { jQuery('#sidebar').toggleClass('-hidden', true); } } - } diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.component.ts b/frontend/src/app/core/main-menu/main-menu-toggle.component.ts index 28ccb0cc0f..16076948dd 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.component.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit, +} from '@angular/core'; import { distinctUntilChanged } from 'rxjs/operators'; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { MainMenuToggleService } from './main-menu-toggle.service'; export const mainMenuToggleSelector = 'main-menu-toggle'; @@ -55,17 +57,18 @@ export const mainMenuToggleSelector = 'main-menu-toggle'; - ` + `, }) export class MainMenuToggleComponent extends UntilDestroyedMixin implements OnInit { - toggleTitle = ""; + toggleTitle = ''; + @InjectField() currentProject:CurrentProjectService; constructor(readonly toggleService:MainMenuToggleService, - readonly cdRef:ChangeDetectorRef, - readonly deviceService:DeviceService, - readonly injector:Injector) { + readonly cdRef:ChangeDetectorRef, + readonly deviceService:DeviceService, + readonly injector:Injector) { super(); } @@ -75,13 +78,11 @@ export class MainMenuToggleComponent extends UntilDestroyedMixin implements OnIn this.toggleService.titleData$ .pipe( distinctUntilChanged(), - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(setToggleTitle => { + .subscribe((setToggleTitle) => { this.toggleTitle = setToggleTitle; this.cdRef.detectChanges(); }); } } - - diff --git a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts index 5829558c8e..23eabec4cb 100644 --- a/frontend/src/app/core/main-menu/main-menu-toggle.service.ts +++ b/frontend/src/app/core/main-menu/main-menu-toggle.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,38 +28,48 @@ import { Injectable, Injector } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Injectable({ providedIn: 'root' }) export class MainMenuToggleService { public toggleTitle:string; + private elementWidth:number; + private elementMinWidth = 11; + private readonly defaultWidth:number = 230; + private readonly localStorageKey:string = 'openProject-mainMenuWidth'; + private readonly localStorageStateKey:string = 'openProject-mainMenuCollapsed'; @InjectField() currentProject:CurrentProjectService; private global = (window as any); + private htmlNode = document.getElementsByTagName('html')[0]; - private mainMenu = jQuery('#main-menu')[0]; // main menu, containing sidebar and resizer + + private mainMenu = jQuery('#main-menu')[0]; // main menu, containing sidebar and resizer + private hideElements = jQuery('.can-hide-navigation'); // Title needs to be sync in main-menu-toggle.component.ts and main-menu-resizer.component.ts private titleData = new BehaviorSubject(''); + public titleData$ = this.titleData.asObservable(); // Notes all changes of the menu size (currently needed in wp-resizer.component.ts) private changeData = new BehaviorSubject({}); + public changeData$ = this.changeData.asObservable(); constructor(protected I18n:I18nService, - public injector:Injector, - readonly deviceService:DeviceService) { + public injector:Injector, + readonly deviceService:DeviceService) { } public initializeMenu():void { @@ -110,7 +120,7 @@ export class MainMenuToggleService { // Set focus on first visible main menu item. // This needs to be called after AngularJS has rendered the menu, which happens some when after(!) we leave this // method here. So we need to set the focus after a timeout. - setTimeout(function () { + setTimeout(() => { jQuery('#main-menu [class*="-menu-item"]:visible').first().focus(); }, 500); } @@ -127,6 +137,7 @@ export class MainMenuToggleService { window.OpenProject.guardedLocalStorage(this.localStorageStateKey, 'false'); } } + public saveWidth(width?:number):void { this.setWidth(width); window.OpenProject.guardedLocalStorage(this.localStorageKey, String(this.elementWidth)); @@ -149,11 +160,11 @@ export class MainMenuToggleService { this.toggleClassHidden(); this.global.showNavigation = this.showNavigation; - this.htmlNode.style.setProperty("--main-menu-width", this.elementWidth + 'px'); + this.htmlNode.style.setProperty('--main-menu-width', `${this.elementWidth}px`); // Send change event when size of menu is changing (menu toggled or resized) // Event should only be fired, when transition is finished - const changeEvent = jQuery.Event("change"); + const changeEvent = jQuery.Event('change'); jQuery('#content-wrapper').on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', () => { this.changeData.next(changeEvent); }); diff --git a/frontend/src/app/core/model-auth/model-auth.service.spec.ts b/frontend/src/app/core/model-auth/model-auth.service.spec.ts index d40f3b7b12..c3970b02a6 100644 --- a/frontend/src/app/core/model-auth/model-auth.service.spec.ts +++ b/frontend/src/app/core/model-auth/model-auth.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ import { AuthorisationService } from './model-auth.service'; -describe('authorisationService', function() { +describe('authorisationService', () => { const authorisationService:AuthorisationService = new AuthorisationService(); - describe('model action authorisation', function () { - beforeEach(function() { + describe('model action authorisation', () => { + beforeEach(() => { authorisationService.initModelAuth('query', { - create: '/queries' + create: '/queries', }); }); - it('should allow action', function() { + it('should allow action', () => { expect(authorisationService.can('query', 'create')).toBeTruthy(); expect(authorisationService.cannot('query', 'create')).toBeFalsy(); }); - it('should not allow action', function() { + it('should not allow action', () => { expect(authorisationService.can('query', 'delete')).toBeFalsy(); expect(authorisationService.cannot('query', 'delete')).toBeTruthy(); }); diff --git a/frontend/src/app/core/model-auth/model-auth.service.ts b/frontend/src/app/core/model-auth/model-auth.service.ts index 273044ea5f..beac1980c2 100644 --- a/frontend/src/app/core/model-auth/model-auth.service.ts +++ b/frontend/src/app/core/model-auth/model-auth.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ +import { Injectable } from '@angular/core'; +import { input } from 'reactivestates'; +import { Observable } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; -import { Injectable } from "@angular/core"; -import { input } from "reactivestates"; -import { Observable } from "rxjs"; -import { takeUntil } from "rxjs/operators"; - -export type ModelLinks = {[action:string]:any}; +export type ModelLinks = { [action:string]:any }; export type ModelLinksHash = { [model:string]:ModelLinks }; @Injectable({ providedIn: 'root' }) diff --git a/frontend/src/app/core/path-helper/path-helper.service.ts b/frontend/src/app/core/path-helper/path-helper.service.ts index c06da6e02d..4cae55e4ec 100644 --- a/frontend/src/app/core/path-helper/path-helper.service.ts +++ b/frontend/src/app/core/path-helper/path-helper.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,17 +27,17 @@ //++ import { Injectable } from '@angular/core'; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; class Apiv3Paths { readonly apiV3Base:string; constructor(basePath:string) { - this.apiV3Base = basePath + '/api/v3'; + this.apiV3Base = `${basePath}/api/v3`; } public get openApiSpecPath():string { - return this.apiV3Base + '/spec.json'; + return `${this.apiV3Base}/spec.json`; } /** @@ -53,9 +53,8 @@ class Apiv3Paths { if (context) { return `${base}?context=${context}`; - } else { - return base; } + return base; } /** @@ -81,17 +80,18 @@ class Apiv3Paths { filters.add('name', '~', [term]); } - return this.apiV3Base + - '/principals?' + - filters.toParams({ sortBy: '[["name","asc"]]', offset: '1', pageSize: '10' }); + return `${this.apiV3Base + }/principals?${ + filters.toParams({ sortBy: '[["name","asc"]]', offset: '1', pageSize: '10' })}`; } } @Injectable({ providedIn: 'root' }) export class PathHelperService { public readonly appBasePath = window.appBasePath || ''; + public readonly api = { - v3: new Apiv3Paths(this.appBasePath) + v3: new Apiv3Paths(this.appBasePath), }; public get staticBase() { @@ -103,9 +103,8 @@ export class PathHelperService { if (slug) { return `${path}/${slug}`; - } else { - return path; } + return path; } public attachmentContentPath(attachmentIdentifier:number|string) { @@ -217,9 +216,8 @@ export class PathHelperService { public projectBoardsPath(projectIdentifier:string|null) { if (projectIdentifier) { return `${this.projectPath(projectIdentifier)}/boards`; - } else { - return `${this.staticBase}/boards`; } + return `${this.staticBase}/boards`; } public projectDashboardsPath(projectIdentifier:string) { @@ -231,9 +229,8 @@ export class PathHelperService { if (workPackageId) { return this.workPackagePath(workPackageId) + suffix; - } else { - return this.staticBase + suffix; // time entries root path } + return this.staticBase + suffix; // time entries root path } public usersPath() { diff --git a/frontend/src/app/core/permissions/permissions.service.ts b/frontend/src/app/core/permissions/permissions.service.ts index 19ad1cad8b..85cdb64fa2 100644 --- a/frontend/src/app/core/permissions/permissions.service.ts +++ b/frontend/src/app/core/permissions/permissions.service.ts @@ -2,8 +2,8 @@ import { Injectable } from '@angular/core'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { Observable, of } from 'rxjs'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { map, catchError } from 'rxjs/operators'; -import { FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { catchError, map } from 'rxjs/operators'; +import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; @Injectable({ providedIn: 'root', @@ -23,7 +23,7 @@ export class PermissionsService { .available_projects .list({ filters }) .pipe( - map(collection => !!collection.elements.length), + map((collection) => !!collection.elements.length), catchError((error) => { console.error(error); return of(false); diff --git a/frontend/src/app/core/routing/base/application-base.component.ts b/frontend/src/app/core/routing/base/application-base.component.ts index 20ba5ae018..35c7c6df7c 100644 --- a/frontend/src/app/core/routing/base/application-base.component.ts +++ b/frontend/src/app/core/routing/base/application-base.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; +import { Component } from '@angular/core'; export const appBaseSelector = 'openproject-base'; @@ -36,8 +36,7 @@ export const appBaseSelector = 'openproject-base';
- ` + `, }) export class ApplicationBaseComponent { } - diff --git a/frontend/src/app/core/routing/first-route-service.ts b/frontend/src/app/core/routing/first-route-service.ts index d95d0f9765..b3bb54f2e6 100644 --- a/frontend/src/app/core/routing/first-route-service.ts +++ b/frontend/src/app/core/routing/first-route-service.ts @@ -1,5 +1,4 @@ - -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,11 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; @Injectable() export class FirstRouteService { public name:string; + public params:any; constructor() {} diff --git a/frontend/src/app/core/routing/openproject-router.module.ts b/frontend/src/app/core/routing/openproject-router.module.ts index 93ff927971..fc94152f7f 100644 --- a/frontend/src/app/core/routing/openproject-router.module.ts +++ b/frontend/src/app/core/routing/openproject-router.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,14 +27,14 @@ //++ import { Injector, NgModule } from '@angular/core'; -import { FirstRouteService } from "core-app/core/routing/first-route-service"; -import { UIRouterModule } from "@uirouter/angular"; -import { ApplicationBaseComponent } from "core-app/core/routing/base/application-base.component"; +import { FirstRouteService } from 'core-app/core/routing/first-route-service'; +import { UIRouterModule } from '@uirouter/angular'; +import { ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; import { initializeUiRouterListeners, OPENPROJECT_ROUTES, - uiRouterConfiguration -} from "core-app/core/routing/openproject.routes"; + uiRouterConfiguration, +} from 'core-app/core/routing/openproject.routes'; @NgModule({ imports: [ @@ -45,11 +45,11 @@ import { } as any), ], providers: [ - FirstRouteService + FirstRouteService, ], declarations: [ - ApplicationBaseComponent - ] + ApplicationBaseComponent, + ], }) export class OpenprojectRouterModule { constructor(injector:Injector) { diff --git a/frontend/src/app/core/routing/openproject.routes.ts b/frontend/src/app/core/routing/openproject.routes.ts index 6ede624083..d94d3b88a7 100644 --- a/frontend/src/app/core/routing/openproject.routes.ts +++ b/frontend/src/app/core/routing/openproject.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { StateDeclaration, StateService, Transition, TransitionService, UIRouter } from '@uirouter/core'; -import { INotification, NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { Injector } from "@angular/core"; -import { FirstRouteService } from "core-app/core/routing/first-route-service"; -import { Ng2StateDeclaration, StatesModule } from "@uirouter/angular"; -import { appBaseSelector, ApplicationBaseComponent } from "core-app/core/routing/base/application-base.component"; -import { BackRoutingService } from "core-app/features/work-packages/components/back-routing/back-routing.service"; -import { MY_ACCOUNT_LAZY_ROUTES } from "core-app/features/user-preferences/user-preferences.lazy-routes"; +import { + StateDeclaration, StateService, Transition, TransitionService, UIRouter, +} from '@uirouter/core'; +import { INotification, NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { Injector } from '@angular/core'; +import { FirstRouteService } from 'core-app/core/routing/first-route-service'; +import { Ng2StateDeclaration, StatesModule } from '@uirouter/angular'; +import { appBaseSelector, ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; +import { BackRoutingService } from 'core-app/features/work-packages/components/back-routing/back-routing.service'; +import { MY_ACCOUNT_LAZY_ROUTES } from 'core-app/features/user-preferences/user-preferences.lazy-routes'; export const OPENPROJECT_ROUTES:Ng2StateDeclaration[] = [ { name: 'new_project.**', url: '/projects/new', - loadChildren: () => import('../../features/projects/openproject-projects.module').then(m => m.OpenprojectProjectsModule) + loadChildren: () => import('../../features/projects/openproject-projects.module').then((m) => m.OpenprojectProjectsModule), }, { name: 'root', @@ -54,64 +56,64 @@ export const OPENPROJECT_ROUTES:Ng2StateDeclaration[] = [ projects: { type: 'path', value: null, squash: true }, // Allow passing of flash messages after routes load - flash_message: { dynamic: true, value: null, inherit: false } - } + flash_message: { dynamic: true, value: null, inherit: false }, + }, }, { name: 'api-docs.**', parent: 'root', url: '/api/docs', - loadChildren: () => import('../../features/api-docs/openproject-api-docs.module').then(m => m.OpenprojectApiDocsModule) + loadChildren: () => import('../../features/api-docs/openproject-api-docs.module').then((m) => m.OpenprojectApiDocsModule), }, { name: 'boards.**', parent: 'root', url: '/boards', - loadChildren: () => import('../../features/boards/openproject-boards.module').then(m => m.OpenprojectBoardsModule) + loadChildren: () => import('../../features/boards/openproject-boards.module').then((m) => m.OpenprojectBoardsModule), }, { name: 'bim.**', parent: 'root', url: '/bcf', - loadChildren: () => import('../../features/bim/ifc_models/openproject-ifc-models.module').then(m => m.OpenprojectIFCModelsModule) + loadChildren: () => import('../../features/bim/ifc_models/openproject-ifc-models.module').then((m) => m.OpenprojectIFCModelsModule), }, { name: 'backlogs.**', parent: 'root', url: '/backlogs', - loadChildren: () => import('../../features/backlogs/openproject-backlogs.module').then(m => m.OpenprojectBacklogsModule) + loadChildren: () => import('../../features/backlogs/openproject-backlogs.module').then((m) => m.OpenprojectBacklogsModule), }, { name: 'backlogs_sprint.**', parent: 'root', url: '/sprints', - loadChildren: () => import('../../features/backlogs/openproject-backlogs.module').then(m => m.OpenprojectBacklogsModule) + loadChildren: () => import('../../features/backlogs/openproject-backlogs.module').then((m) => m.OpenprojectBacklogsModule), }, { name: 'reporting.**', parent: 'root', url: '/cost_reports', - loadChildren: () => import('../../features/reporting/openproject-reporting.module').then(m => m.OpenprojectReportingModule) + loadChildren: () => import('../../features/reporting/openproject-reporting.module').then((m) => m.OpenprojectReportingModule), }, { name: 'job-statuses.**', parent: 'root', url: '/job_statuses', - loadChildren: () => import('../../features/job-status/openproject-job-status.module').then(m => m.OpenProjectJobStatusModule) + loadChildren: () => import('../../features/job-status/openproject-job-status.module').then((m) => m.OpenProjectJobStatusModule), }, { name: 'project_settings.**', parent: 'root', url: '/settings/generic', - loadChildren: () => import('../../features/projects/openproject-projects.module').then(m => m.OpenprojectProjectsModule) + loadChildren: () => import('../../features/projects/openproject-projects.module').then((m) => m.OpenprojectProjectsModule), }, { name: 'project_copy.**', parent: 'root', url: '/copy', - loadChildren: () => import('../../features/projects/openproject-projects.module').then(m => m.OpenprojectProjectsModule) + loadChildren: () => import('../../features/projects/openproject-projects.module').then((m) => m.OpenprojectProjectsModule), }, - ...MY_ACCOUNT_LAZY_ROUTES + ...MY_ACCOUNT_LAZY_ROUTES, ]; /** @@ -137,7 +139,7 @@ export function updateMenuItem(menuItemClass:string|undefined, action:'add'|'rem return; } - const menuItem = jQuery('#main-menu .' + menuItemClass)[0]; + const menuItem = jQuery(`#main-menu .${menuItemClass}`)[0]; if (!menuItem) { return; @@ -170,7 +172,7 @@ export function uiRouterConfiguration(uiRouter:UIRouter, injector:Injector, modu dynamic: true, is: (val:unknown) => typeof (val) === 'string', equals: (a:any, b:any) => _.isEqual(a, b), - } + }, ); } @@ -195,7 +197,7 @@ export function initializeUiRouterListeners(injector:Injector) { // but since AOT doesn't allow anonymous functions, we can't re-use them now. // The transition will only return the target state on `transition.to()`, // however the second parameter has the currently (e.g., parent) entering state chain. - $transitions.onEnter({}, function (transition:Transition, state:StateDeclaration) { + $transitions.onEnter({}, (transition:Transition, state:StateDeclaration) => { // Add body class when entering this state bodyClass(_.get(state, 'data.bodyClasses'), 'add'); if (transition.from().data && _.get(state, 'data.menuItem') !== transition.from().data.menuItem) { @@ -206,7 +208,7 @@ export function initializeUiRouterListeners(injector:Injector) { window.scrollTo(0, 0); }); - $transitions.onExit({}, function (transition:Transition, state:StateDeclaration) { + $transitions.onExit({}, (transition:Transition, state:StateDeclaration) => { // Remove body class when leaving this state bodyClass(_.get(state, 'data.bodyClasses'), 'remove'); if (transition.to().data && _.get(state, 'data.menuItem') !== transition.to().data.menuItem) { @@ -214,7 +216,7 @@ export function initializeUiRouterListeners(injector:Injector) { } }); - $transitions.onStart({}, function (transition:Transition) { + $transitions.onStart({}, (transition:Transition) => { const $state = transition.router.stateService; const toParams = transition.params('to'); const fromState = transition.from(); @@ -222,7 +224,7 @@ export function initializeUiRouterListeners(injector:Injector) { // Remove start_onboarding_tour param if set if (toParams.start_onboarding_tour && toState.name !== 'work-packages.partitioned.list') { - const paramsCopy = Object.assign({}, transition.params()); + const paramsCopy = { ...transition.params() }; paramsCopy.start_onboarding_tour = undefined; return $state.target(transition.to(), paramsCopy); } @@ -250,7 +252,6 @@ export function initializeUiRouterListeners(injector:Injector) { // but for pages without any angular routes, this will stay empty. // So we also allow routes to happen after some delay if (wpBase === null) { - // Get the current path and compare const path = window.location.pathname; const pathWithSearch = path + window.location.search; diff --git a/frontend/src/app/core/schemas/schema-cache.service.ts b/frontend/src/app/core/schemas/schema-cache.service.ts index 490f312a9f..00b737d6b6 100644 --- a/frontend/src/app/core/schemas/schema-cache.service.ts +++ b/frontend/src/app/core/schemas/schema-cache.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,21 +27,20 @@ //++ import { State } from 'reactivestates'; import { Injectable } from '@angular/core'; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Observable } from "rxjs"; -import { take } from "rxjs/operators"; -import { States } from "core-app/core/states/states.service"; -import { ISchemaProxy, SchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { WorkPackageSchemaProxy } from "core-app/features/hal/schemas/work-package-schema-proxy"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Observable } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { States } from 'core-app/core/states/states.service'; +import { ISchemaProxy, SchemaProxy } from 'core-app/features/hal/schemas/schema-proxy'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { WorkPackageSchemaProxy } from 'core-app/features/hal/schemas/work-package-schema-proxy'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; @Injectable() export class SchemaCacheService extends StateCacheService { - constructor(readonly states:States, - readonly halResourceService:HalResourceService) { + readonly halResourceService:HalResourceService) { super(states.schemas); } @@ -60,14 +59,13 @@ export class SchemaCacheService extends StateCacheService { const schema = this.state(resource).value; if (!schema) { - throw `Schema for resource ${resource} was expected to be loaded but isn't.`; + throw new Error(`Schema for resource ${resource} was expected to be loaded but isn't.`); } if (resource._type === 'WorkPackage') { return WorkPackageSchemaProxy.create(schema, resource); - } else { - return SchemaProxy.create(schema, resource); } + return SchemaProxy.create(schema, resource); } public getSchemaHref(resource:HalResource):string { @@ -91,7 +89,7 @@ export class SchemaCacheService extends StateCacheService { return this .requireAndStream(href) .pipe( - take(1) + take(1), ) .toPromise(); } @@ -110,7 +108,7 @@ export class SchemaCacheService extends StateCacheService { if (this.stale(href) || force) { this.clearAndLoad( href, - this.load(href) + this.load(href), ); } @@ -125,12 +123,12 @@ export class SchemaCacheService extends StateCacheService { .halResourceService .get(href) .pipe( - take(1) + take(1), ); } protected loadAll(hrefs:string[]):Promise { - return Promise.all(hrefs.map(href => this.load(href))); + return Promise.all(hrefs.map((href) => this.load(href))); } /** @@ -145,9 +143,7 @@ export class SchemaCacheService extends StateCacheService { private stateKey(id:string|HalResource):string { if (id instanceof HalResource) { return this.getSchemaHref(id); - } else { - return id; } + return id; } } - diff --git a/frontend/src/app/core/setup/global-dynamic-components.const.ts b/frontend/src/app/core/setup/global-dynamic-components.const.ts index d30a9fb2f9..104dbe7eff 100644 --- a/frontend/src/app/core/setup/global-dynamic-components.const.ts +++ b/frontend/src/app/core/setup/global-dynamic-components.const.ts @@ -1,183 +1,183 @@ -import { OptionalBootstrapDefinition } from "core-app/core/setup/globals/dynamic-bootstrapper"; -import { appBaseSelector, ApplicationBaseComponent } from "core-app/core/routing/base/application-base.component"; +import { OptionalBootstrapDefinition } from 'core-app/core/setup/globals/dynamic-bootstrapper'; +import { appBaseSelector, ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; import { EmbeddedTablesMacroComponent, - wpEmbeddedTableMacroSelector -} from "core-app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component"; + wpEmbeddedTableMacroSelector, +} from 'core-app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component'; import { - ColorsAutocompleter, - colorsAutocompleterSelector -} from "core-app/shared/components/colors/colors-autocompleter.component"; + ColorsAutocompleterComponent, + colorsAutocompleterSelector, +} from 'core-app/shared/components/colors/colors-autocompleter.component'; import { ZenModeButtonComponent, - zenModeComponentSelector -} from "core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component"; + zenModeComponentSelector, +} from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; import { AttachmentsComponent, - attachmentsSelector -} from "core-app/shared/components/attachments/attachments.component"; + attachmentsSelector, +} from 'core-app/shared/components/attachments/attachments.component'; import { UserAutocompleterComponent, - usersAutocompleterSelector -} from "core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component"; + usersAutocompleterSelector, +} from 'core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component'; import { GlobalSearchWorkPackagesComponent, - globalSearchWorkPackagesSelector -} from "core-app/core/global_search/global-search-work-packages.component"; + globalSearchWorkPackagesSelector, +} from 'core-app/core/global_search/global-search-work-packages.component'; import { CustomDateActionAdminComponent, - customDateActionAdminSelector -} from "core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component"; -import { BoardsMenuComponent, boardsMenuSelector } from "core-app/features/boards/boards-sidebar/boards-menu.component"; + customDateActionAdminSelector, +} from 'core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component'; +import { BoardsMenuComponent, boardsMenuSelector } from 'core-app/features/boards/boards-sidebar/boards-menu.component'; import { GlobalSearchWorkPackagesEntryComponent, - globalSearchWorkPackagesSelectorEntry -} from "core-app/core/global_search/global-search-work-packages-entry.component"; + globalSearchWorkPackagesSelectorEntry, +} from 'core-app/core/global_search/global-search-work-packages-entry.component'; import { NotificationsContainerComponent, - notificationsContainerSelector -} from "core-app/shared/components/notifications/notifications-container.component"; + notificationsContainerSelector, +} from 'core-app/shared/components/notifications/notifications-container.component'; import { CkeditorAugmentedTextareaComponent, - ckeditorAugmentedTextareaSelector -} from "core-app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component"; + ckeditorAugmentedTextareaSelector, +} from 'core-app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component'; import { PersistentToggleComponent, - persistentToggleSelector -} from "core-app/shared/components/persistent-toggle/persistent-toggle.component"; -import { OpPrincipalComponent, principalSelector } from "core-app/shared/components/principal/principal.component"; + persistentToggleSelector, +} from 'core-app/shared/components/persistent-toggle/persistent-toggle.component'; +import { OpPrincipalComponent, principalSelector } from 'core-app/shared/components/principal/principal.component'; import { HideSectionLinkComponent, - hideSectionLinkSelector -} from "core-app/shared/components/hide-section/hide-section-link/hide-section-link.component"; + hideSectionLinkSelector, +} from 'core-app/shared/components/hide-section/hide-section-link/hide-section-link.component'; import { ShowSectionDropdownComponent, - showSectionDropdownSelector -} from "core-app/shared/components/hide-section/show-section-dropdown.component"; + showSectionDropdownSelector, +} from 'core-app/shared/components/hide-section/show-section-dropdown.component'; import { AddSectionDropdownComponent, - addSectionDropdownSelector -} from "core-app/shared/components/hide-section/add-section-dropdown/add-section-dropdown.component"; + addSectionDropdownSelector, +} from 'core-app/shared/components/hide-section/add-section-dropdown/add-section-dropdown.component'; import { AutocompleteSelectDecorationComponent, - autocompleteSelectDecorationSelector -} from "core-app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component"; + autocompleteSelectDecorationSelector, +} from 'core-app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component'; import { ContentTabsComponent, - contentTabsSelector -} from "core-app/shared/components/tabs/content-tabs/content-tabs.component"; + contentTabsSelector, +} from 'core-app/shared/components/tabs/content-tabs/content-tabs.component'; import { CopyToClipboardDirective, - copyToClipboardSelector -} from "core-app/shared/components/copy-to-clipboard/copy-to-clipboard.directive"; + copyToClipboardSelector, +} from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.directive'; import { GlobalSearchInputComponent, - globalSearchSelector -} from "core-app/core/global_search/input/global-search-input.component"; + globalSearchSelector, +} from 'core-app/core/global_search/input/global-search-input.component'; import { collapsibleSectionAugmentSelector, - CollapsibleSectionComponent -} from "core-app/shared/components/collapsible-section/collapsible-section.component"; + CollapsibleSectionComponent, +} from 'core-app/shared/components/collapsible-section/collapsible-section.component'; import { ProjectMenuAutocompleteComponent, - projectMenuAutocompleteSelector -} from "core-app/shared/components/autocompleter//project-menu-autocomplete/project-menu-autocomplete.component"; + projectMenuAutocompleteSelector, +} from 'core-app/shared/components/autocompleter//project-menu-autocomplete/project-menu-autocomplete.component'; import { RemoteFieldUpdaterComponent, - remoteFieldUpdaterSelector -} from "core-app/shared/components/remote-field-updater/remote-field-updater.component"; + remoteFieldUpdaterSelector, +} from 'core-app/shared/components/remote-field-updater/remote-field-updater.component'; import { WorkPackageOverviewGraphComponent, - wpOverviewGraphSelector -} from "core-app/shared/components/work-package-graphs/overview/wp-overview-graph.component"; + wpOverviewGraphSelector, +} from 'core-app/shared/components/work-package-graphs/overview/wp-overview-graph.component'; import { WorkPackageQuerySelectDropdownComponent, - wpQuerySelectSelector -} from "core-app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component"; + wpQuerySelectSelector, +} from 'core-app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component'; import { GlobalSearchTitleComponent, - globalSearchTitleSelector -} from "core-app/core/global_search/title/global-search-title.component"; + globalSearchTitleSelector, +} from 'core-app/core/global_search/title/global-search-title.component'; import { GlobalSearchTabsComponent, - globalSearchTabsSelector -} from "core-app/core/global_search/tabs/global-search-tabs.component"; + globalSearchTabsSelector, +} from 'core-app/core/global_search/tabs/global-search-tabs.component'; import { MembersAutocompleterComponent, - membersAutocompleterSelector -} from "core-app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component"; + membersAutocompleterSelector, +} from 'core-app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component'; import { TriggerActionsEntryComponent, - triggerActionsEntryComponentSelector -} from "core-app/shared/components/time_entries/edit/trigger-actions-entry.component"; + triggerActionsEntryComponentSelector, +} from 'core-app/shared/components/time_entries/edit/trigger-actions-entry.component'; import { BacklogsPageComponent, - backlogsPageComponentSelector -} from "core-app/features/backlogs/backlogs-page/backlogs-page.component"; + backlogsPageComponentSelector, +} from 'core-app/features/backlogs/backlogs-page/backlogs-page.component'; import { attributeValueMacro, - AttributeValueMacroComponent -} from "core-app/shared/components/fields/macros/attribute-value-macro.component"; + AttributeValueMacroComponent, +} from 'core-app/shared/components/fields/macros/attribute-value-macro.component'; import { attributeLabelMacro, - AttributeLabelMacroComponent -} from "core-app/shared/components/fields/macros/attribute-label-macro.component"; + AttributeLabelMacroComponent, +} from 'core-app/shared/components/fields/macros/attribute-label-macro.component'; import { AttributeHelpTextComponent, - attributeHelpTextSelector -} from "core-app/shared/components/attribute-help-texts/attribute-help-text.component"; + attributeHelpTextSelector, +} from 'core-app/shared/components/attribute-help-texts/attribute-help-text.component'; import { quickInfoMacroSelector, - WorkPackageQuickinfoMacroComponent -} from "core-app/shared/components/fields/macros/work-package-quickinfo-macro.component"; + WorkPackageQuickinfoMacroComponent, +} from 'core-app/shared/components/fields/macros/work-package-quickinfo-macro.component'; import { SlideToggleComponent, - slideToggleSelector -} from "core-app/shared/components/slide-toggle/slide-toggle.component"; -import { BackupComponent, backupSelector } from "core-app/core/setup/globals/components/admin/backup.component"; + slideToggleSelector, +} from 'core-app/shared/components/slide-toggle/slide-toggle.component'; +import { BackupComponent, backupSelector } from 'core-app/core/setup/globals/components/admin/backup.component'; import { EnterpriseBaseComponent, enterpriseBaseSelector, -} from "core-app/features/enterprise/enterprise-base.component"; +} from 'core-app/features/enterprise/enterprise-base.component'; import { EEActiveSavedTrialComponent, enterpriseActiveSavedTrialSelector, -} from "core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component"; +} from 'core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component'; import { EnterpriseBannerBootstrapComponent, enterpriseBannerSelector, -} from "core-app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component"; +} from 'core-app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component'; import { HomescreenNewFeaturesBlockComponent, homescreenNewFeaturesBlockSelector, -} from "core-app/features/homescreen/blocks/new-features.component"; -import { MainMenuToggleComponent, mainMenuToggleSelector } from "core-app/core/main-menu/main-menu-toggle.component"; +} from 'core-app/features/homescreen/blocks/new-features.component'; +import { MainMenuToggleComponent, mainMenuToggleSelector } from 'core-app/core/main-menu/main-menu-toggle.component'; import { ConfirmFormSubmitController, confirmFormSubmitSelector, -} from "core-app/shared/components/modals/confirm-form-submit/confirm-form-submit.directive"; +} from 'core-app/shared/components/modals/confirm-form-submit/confirm-form-submit.directive'; import { MainMenuResizerComponent, mainMenuResizerSelector, -} from "core-app/shared/components/resizer/resizer/main-menu-resizer.component"; +} from 'core-app/shared/components/resizer/resizer/main-menu-resizer.component'; import { adminTypeFormConfigurationSelector, TypeFormConfigurationComponent, -} from "core-app/features/admin/types/type-form-configuration.component"; +} from 'core-app/features/admin/types/type-form-configuration.component'; import { EditableQueryPropsComponent, editableQueryPropsSelector, -} from "core-app/features/admin/editable-query-props/editable-query-props.component"; +} from 'core-app/features/admin/editable-query-props/editable-query-props.component'; import { InAppNotificationBellComponent, - opInAppNotificationBellSelector -} from "core-app/features/in-app-notifications/bell/in-app-notification-bell.component"; + opInAppNotificationBellSelector, +} from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component'; export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ { selector: appBaseSelector, cls: ApplicationBaseComponent }, { selector: attributeHelpTextSelector, cls: AttributeHelpTextComponent }, { selector: wpEmbeddedTableMacroSelector, cls: EmbeddedTablesMacroComponent, embeddable: true }, - { selector: colorsAutocompleterSelector, cls: ColorsAutocompleter }, + { selector: colorsAutocompleterSelector, cls: ColorsAutocompleterComponent }, { selector: zenModeComponentSelector, cls: ZenModeButtonComponent }, { selector: attachmentsSelector, cls: AttachmentsComponent, embeddable: true }, { selector: usersAutocompleterSelector, cls: UserAutocompleterComponent }, @@ -189,7 +189,7 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ { selector: boardsMenuSelector, cls: BoardsMenuComponent }, { selector: globalSearchWorkPackagesSelectorEntry, cls: GlobalSearchWorkPackagesEntryComponent }, { selector: notificationsContainerSelector, cls: NotificationsContainerComponent }, - { selector: adminTypeFormConfigurationSelector, cls: TypeFormConfigurationComponent, }, + { selector: adminTypeFormConfigurationSelector, cls: TypeFormConfigurationComponent }, { selector: ckeditorAugmentedTextareaSelector, cls: CkeditorAugmentedTextareaComponent, embeddable: true }, { selector: persistentToggleSelector, cls: PersistentToggleComponent }, { selector: principalSelector, cls: OpPrincipalComponent }, @@ -222,7 +222,3 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [ { selector: backupSelector, cls: BackupComponent }, { selector: opInAppNotificationBellSelector, cls: InAppNotificationBellComponent }, ]; - - - - diff --git a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts index d577b31d9f..0e8ce667dc 100644 --- a/frontend/src/app/core/setup/globals/components/admin/backup.component.ts +++ b/frontend/src/app/core/setup/globals/components/admin/backup.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,14 +27,16 @@ //++ import { HttpErrorResponse } from '@angular/common/http'; -import { AfterViewInit, Component, ElementRef, Injector, ViewChild } from '@angular/core'; +import { + AfterViewInit, Component, ElementRef, Injector, ViewChild, +} from '@angular/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { JobStatusModal } from "core-app/features/job-status/job-status-modal/job-status.modal"; +import { JobStatusModalComponent } from 'core-app/features/job-status/job-status-modal/job-status.modal'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { OpenProjectBackupService } from "core-app/core/backup/op-backup.service"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { OpenProjectBackupService } from 'core-app/core/backup/op-backup.service'; export const backupSelector = 'backup'; @@ -56,18 +58,23 @@ export class BackupComponent implements AfterViewInit { attachmentsDisabled: this.i18n.t('js.backup.attachments_disabled'), }; - public jobStatusId:string = this.elementRef.nativeElement.dataset['jobStatusId']; - public lastBackupDate:string = this.elementRef.nativeElement.dataset['lastBackupDate']; - public lastBackupAttachmentId:string = this.elementRef.nativeElement.dataset['lastBackupAttachmentId']; - public mayIncludeAttachments:boolean = this.elementRef.nativeElement.dataset['mayIncludeAttachments'] != "false"; + public jobStatusId:string = this.elementRef.nativeElement.dataset.jobStatusId; - public isInProgress:boolean = false; - public includeAttachments:boolean = true; - public backupToken:string = ""; + public lastBackupDate:string = this.elementRef.nativeElement.dataset.lastBackupDate; + + public lastBackupAttachmentId:string = this.elementRef.nativeElement.dataset.lastBackupAttachmentId; + + public mayIncludeAttachments:boolean = this.elementRef.nativeElement.dataset.mayIncludeAttachments != 'false'; + + public isInProgress = false; + + public includeAttachments = true; + + public backupToken = ''; @InjectField() opBackup:OpenProjectBackupService; - @ViewChild("backupTokenInput") backupTokenInput: ElementRef; + @ViewChild('backupTokenInput') backupTokenInput:ElementRef; constructor( readonly elementRef:ElementRef, @@ -75,7 +82,7 @@ export class BackupComponent implements AfterViewInit { protected i18n:I18nService, protected notificationsService:NotificationsService, protected opModalService:OpModalService, - protected pathHelper:PathHelperService + protected pathHelper:PathHelperService, ) { this.includeAttachments = this.mayIncludeAttachments; } @@ -85,8 +92,8 @@ export class BackupComponent implements AfterViewInit { } public isDownloadReady():boolean { - return this.jobStatusId !== undefined && this.jobStatusId !== "" && - this.lastBackupAttachmentId !== undefined && this.lastBackupAttachmentId !== ""; + return this.jobStatusId !== undefined && this.jobStatusId !== '' + && this.lastBackupAttachmentId !== undefined && this.lastBackupAttachmentId !== ''; } public getDownloadUrl():string { @@ -107,16 +114,16 @@ export class BackupComponent implements AfterViewInit { event.preventDefault(); } - var backupToken = this.backupToken; + const { backupToken } = this; - this.backupToken = ""; + this.backupToken = ''; this.opBackup .triggerBackup(backupToken, this.includeAttachments) .toPromise() .then((resp:any) => { this.jobStatusId = resp.jobStatusId; - this.opModalService.show(JobStatusModal, 'global', { jobId: resp.jobStatusId }); + this.opModalService.show(JobStatusModalComponent, 'global', { jobId: resp.jobStatusId }); }) .catch((error:HttpErrorResponse) => { this.notificationsService.addError(error.error); diff --git a/frontend/src/app/core/setup/globals/constants.const.ts b/frontend/src/app/core/setup/globals/constants.const.ts index a279cf68a7..1459fb8eef 100644 --- a/frontend/src/app/core/setup/globals/constants.const.ts +++ b/frontend/src/app/core/setup/globals/constants.const.ts @@ -1,6 +1,6 @@ -export const enterpriseEditionUrl = "https://www.openproject.org/enterprise-edition/?op_edition=community-edition"; +export const enterpriseEditionUrl = 'https://www.openproject.org/enterprise-edition/?op_edition=community-edition'; -export const contactUrl:{[locale:string]:string} = { - en: "https://www.openproject.org/contact-us/", - de: "https://www.openproject.org/kontakt/", +export const contactUrl:{ [locale:string]:string } = { + en: 'https://www.openproject.org/contact-us/', + de: 'https://www.openproject.org/kontakt/', }; diff --git a/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts b/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts index 654ad9c1d3..1cf986b5df 100644 --- a/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts +++ b/frontend/src/app/core/setup/globals/dynamic-bootstrapper.ts @@ -25,9 +25,8 @@ // // See docs/COPYRIGHT.rdoc for more details. -import { ComponentType } from "@angular/cdk/portal"; -import { ApplicationRef } from "@angular/core"; -import { filter, take } from "rxjs/operators"; +import { ComponentType } from '@angular/cdk/portal'; +import { ApplicationRef } from '@angular/core'; /** * Optional bootstrap definition to allow selecting all matching @@ -93,7 +92,7 @@ export class DynamicBootstrapper { * Get embeddable components */ public static getEmbeddable() { - return this.optionalBoostrapComponents.filter(el => el.embeddable); + return this.optionalBoostrapComponents.filter((el) => el.embeddable); } /** @@ -106,8 +105,7 @@ export class DynamicBootstrapper { */ private static performBootstrap(appRef:ApplicationRef, root:Document|HTMLElement, embedded:boolean, definitions:OptionalBootstrapDefinition[]) { definitions - .forEach(el => { - + .forEach((el) => { // Skip non-embeddable components in an embedded bootstrap. if (embedded && !el.embeddable) { return; diff --git a/frontend/src/app/core/setup/globals/global-helpers.ts b/frontend/src/app/core/setup/globals/global-helpers.ts index 667794730d..51df10e472 100644 --- a/frontend/src/app/core/setup/globals/global-helpers.ts +++ b/frontend/src/app/core/setup/globals/global-helpers.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -41,7 +41,7 @@ export class GlobalHelpers { public toggleCheckboxesBySelector(selector:any) { const boxes = jQuery(selector); - var all_checked = true; + let all_checked = true; for (let i = 0; i < boxes.length; i++) { if (boxes[i].checked === false) { all_checked = false; diff --git a/frontend/src/app/core/setup/globals/global-listeners.ts b/frontend/src/app/core/setup/globals/global-listeners.ts index b022731e2e..7fb845a9e2 100644 --- a/frontend/src/app/core/setup/globals/global-listeners.ts +++ b/frontend/src/app/core/setup/globals/global-listeners.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,28 +26,27 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { performAnchorHijacking } from "./global-listeners/link-hijacking"; -import { augmentedDatePicker } from "./global-listeners/augmented-date-picker"; import { refreshOnFormChanges } from 'core-app/core/setup/globals/global-listeners/refresh-on-form-changes'; -import { registerRequestForConfirmation } from "core-app/core/setup/globals/global-listeners/request-for-confirmation"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { scrollHeaderOnMobile } from "core-app/core/setup/globals/global-listeners/top-menu-scroll"; -import { setupToggableFieldsets } from "core-app/core/setup/globals/global-listeners/toggable-fieldset"; -import { TopMenu } from "core-app/core/setup/globals/global-listeners/top-menu"; -import { install_menu_logic } from "core-app/core/setup/globals/global-listeners/action-menu"; -import { makeColorPreviews } from "core-app/core/setup/globals/global-listeners/color-preview"; -import { dangerZoneValidation } from "core-app/core/setup/globals/global-listeners/danger-zone-validation"; -import { setupServerResponse } from "core-app/core/setup/globals/global-listeners/setup-server-response"; -import { listenToSettingChanges } from "core-app/core/setup/globals/global-listeners/settings"; -import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onboarding_tour_trigger"; +import { registerRequestForConfirmation } from 'core-app/core/setup/globals/global-listeners/request-for-confirmation'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { scrollHeaderOnMobile } from 'core-app/core/setup/globals/global-listeners/top-menu-scroll'; +import { setupToggableFieldsets } from 'core-app/core/setup/globals/global-listeners/toggable-fieldset'; +import { TopMenu } from 'core-app/core/setup/globals/global-listeners/top-menu'; +import { installMenuLogic } from 'core-app/core/setup/globals/global-listeners/action-menu'; +import { makeColorPreviews } from 'core-app/core/setup/globals/global-listeners/color-preview'; +import { dangerZoneValidation } from 'core-app/core/setup/globals/global-listeners/danger-zone-validation'; +import { setupServerResponse } from 'core-app/core/setup/globals/global-listeners/setup-server-response'; +import { listenToSettingChanges } from 'core-app/core/setup/globals/global-listeners/settings'; +import { detectOnboardingTour } from 'core-app/core/setup/globals/onboarding/onboarding_tour_trigger'; +import { augmentedDatePicker } from './global-listeners/augmented-date-picker'; +import { performAnchorHijacking } from './global-listeners/link-hijacking'; /** * A set of listeners that are relevant on every page to set sensible defaults */ (function ($:JQueryStatic) { - - $(function () { - $(document.documentElement!) + $(() => { + $(document.documentElement) .on('click', (evt:any) => { const target = jQuery(evt.target) as JQuery; @@ -62,7 +61,7 @@ import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onb }); // Jump to the element given by location.hash, if present - const hash = window.location.hash; + const { hash } = window.location; if (hash && hash.startsWith('#')) { try { const el = document.querySelector(hash); @@ -71,13 +70,13 @@ import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onb // This is very likely an invalid selector such as a Google Analytics tag. // We can safely ignore this and just not scroll in this case. // Still log the error so one can confirm the reason there is no scrolling. - console.log("Could not scroll to given location hash: " + hash + " ( " + e.message + ")"); + console.log(`Could not scroll to given location hash: ${hash} ( ${e.message})`); } } // Global submitting hook, // necessary to avoid a data loss warning on beforeunload - $(document).on('submit', 'form', function () { + $(document).on('submit', 'form', () => { window.OpenProject.pageIsSubmitted = true; }); @@ -96,12 +95,12 @@ import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onb // Cancel the event event.preventDefault(); // Chrome requires returnValue to be set - event.returnValue = I18n.t("js.work_packages.confirm_edit_cancel"); + event.returnValue = I18n.t('js.work_packages.confirm_edit_cancel'); } }); // Disable global drag & drop handling, which results in the browser loading the image and losing the page - $(document.documentElement!) + $(document.documentElement) .on('dragover drop', (evt:any) => { evt.preventDefault(); return false; @@ -134,8 +133,8 @@ import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onb new TopMenu(jQuery('.op-app-header')); // Action menu logic - jQuery('.project-actions, .toolbar-items').each(function (idx:number, menu:HTMLElement) { - install_menu_logic(jQuery(menu)); + jQuery('.project-actions, .toolbar-items').each((idx:number, menu:HTMLElement) => { + installMenuLogic(jQuery(menu)); }); // Legacy settings listener @@ -150,5 +149,4 @@ import { detectOnboardingTour } from "core-app/core/setup/globals/onboarding/onb // Bootstrap legacy app code setupServerResponse(); }); - }(jQuery)); diff --git a/frontend/src/app/core/setup/globals/global-listeners/action-menu.ts b/frontend/src/app/core/setup/globals/global-listeners/action-menu.ts index dfc47bb377..312bc077fa 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/action-menu.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/action-menu.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -41,51 +41,39 @@ The following code is responsible to open and close the "more functions" submenu. */ -import { ANIMATION_RATE_MS } from "core-app/core/setup/globals/global-listeners/top-menu"; +import { ANIMATION_RATE_MS } from 'core-app/core/setup/globals/global-listeners/top-menu'; import ClickEvent = JQuery.ClickEvent; -function menu_top_position(menu:JQuery) { - // if an h2 tag follows the submenu should unfold out at the border - var menu_start_position; - if (menu.next().get(0) !== undefined && (menu.next().get(0).tagName === 'H2')) { - menu_start_position = menu.next().innerHeight()! + menu.next().position().top; - } else if (menu.next().hasClass("wiki-content") && menu.next().children().next().first().get(0) != undefined && menu.next().children().next().first().get(0).tagName == 'H1') { - var wiki_heading = menu.next().children().next().first(); - menu_start_position = wiki_heading.innerHeight()! + wiki_heading.position().top; - } - return menu_start_position; -} - -function close_menu(event:any) { - var menu = jQuery(event.data.menu); +function closeMenu(event:any) { + const menu = jQuery(event.data.menu); // do not close the menu, if the user accidentally clicked next to a menu item (but still within the menu) - if (event.target !== menu.find(" > li.drop-down.open > ul").get(0)) { - menu.find(" > li.drop-down.open").removeClass("open").find("> ul").slideUp(ANIMATION_RATE_MS); + if (event.target !== menu.find(' > li.drop-down.open > ul').get(0)) { + menu.find(' > li.drop-down.open').removeClass('open').find('> ul').slideUp(ANIMATION_RATE_MS); // no need to watch for clicks, when the menu is already closed - jQuery('html').off('click', close_menu); + jQuery('html').off('click', closeMenu); } } -function open_menu(menu:JQuery) { - var drop_down = menu.find(" > li.drop-down"); +function openMenu(menu:JQuery) { + const dropDown = menu.find(' > li.drop-down'); // do not open a menu, which is already open - if (!drop_down.hasClass('open')) { - drop_down.find('> ul').slideDown(ANIMATION_RATE_MS, function () { - drop_down.find('li > a:first').focus(); + if (!dropDown.hasClass('open')) { + dropDown.find('> ul').slideDown(ANIMATION_RATE_MS, () => { + dropDown.find('li > a:first').focus(); // when clicking on something, which is not the menu, close the menu - jQuery('html').on('click', { menu: menu.get(0) }, close_menu); + jQuery('html').on('click', { menu: menu.get(0) }, closeMenu); }); - drop_down.addClass('open'); + dropDown.addClass('open'); } } // open the given submenu when clicking on it -export function install_menu_logic(menu:JQuery) { - menu.find(" > li.drop-down").on('click', (event:ClickEvent) => { - open_menu(menu); +export function installMenuLogic(menu:JQuery) { + menu.find(' > li.drop-down').on('click', (event:ClickEvent) => { + openMenu(menu); // and prevent default action (href) for that element // but not for the menu items. - var target = jQuery(event.target); + const target = jQuery(event.target); if (target.is('.drop-down') || target.closest('li, ul').is('.drop-down')) { event.preventDefault(); } diff --git a/frontend/src/app/core/setup/globals/global-listeners/augmented-date-picker.ts b/frontend/src/app/core/setup/globals/global-listeners/augmented-date-picker.ts index 68e44e521f..3220db24e8 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/augmented-date-picker.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/augmented-date-picker.ts @@ -1,4 +1,4 @@ -import { DatePicker } from "core-app/shared/components/op-date-picker/datepicker"; +import { DatePicker } from 'core-app/shared/components/op-date-picker/datepicker'; /** * Our application is still a hybrid one, meaning most routes are still @@ -13,18 +13,19 @@ export function augmentedDatePicker(evt:JQuery.TriggeredEvent, target:JQuery) { .attr('autocomplete', 'off'); // Disable autocomplete for those fields window.OpenProject.getPluginContext() - .then(context => { - var datePicker = new DatePicker( + .then((context) => { + const datePicker = new DatePicker( '.-augmented-datepicker', target.val(), { weekNumbers: true, - allowInput: true + allowInput: true, }, target[0], - context.services.configurationService + context.services.configurationService, ); datePicker.show(); - }); + }) + .catch(() => {}); } } diff --git a/frontend/src/app/core/setup/globals/global-listeners/color-preview.ts b/frontend/src/app/core/setup/globals/global-listeners/color-preview.ts index cc6302def7..b73a7434ba 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/color-preview.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/color-preview.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -36,7 +36,6 @@ export function makeColorPreviews() { jQuery('.color--preview').each(function () { const preview = jQuery(this); let input:any; - let func:any; const target = preview.data('target'); if (target) { @@ -49,13 +48,13 @@ export function makeColorPreviews() { return; } - func = function () { - var previewColor = ''; + const func = function () { + let previewColor = ''; if (input.val() && input.val().length > 0) { previewColor = input.val(); - } else if (input.attr('placeholder') && - input.attr('placeholder').length > 0) { + } else if (input.attr('placeholder') + && input.attr('placeholder').length > 0) { previewColor = input.attr('placeholder'); } diff --git a/frontend/src/app/core/setup/globals/global-listeners/danger-zone-validation.ts b/frontend/src/app/core/setup/globals/global-listeners/danger-zone-validation.ts index 685689d0ff..8d0c61e2fd 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/danger-zone-validation.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/danger-zone-validation.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,11 +30,11 @@ // Make the whole danger zone a component the next time this needs changes! export function dangerZoneValidation() { // This will only work iff there is a single danger zone on the page - var dangerZoneVerification = jQuery('.danger-zone--verification'); - var expectedValue = jQuery('.danger-zone--expected-value').text(); + const dangerZoneVerification = jQuery('.danger-zone--verification'); + const expectedValue = jQuery('.danger-zone--expected-value').text(); - dangerZoneVerification.find('input').on('input', function () { - var actualValue = dangerZoneVerification.find('input').val() as string; + dangerZoneVerification.find('input').on('input', () => { + const actualValue = dangerZoneVerification.find('input').val() as string; if (expectedValue.toLowerCase() === actualValue.toLowerCase()) { dangerZoneVerification.find('button').prop('disabled', false); } else { diff --git a/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts b/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts index a50a7c8d2d..c1ab3b75d6 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Injectable, Injector, NgZone } from "@angular/core"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { WpPreviewModal } from "core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal"; +import { Injectable, Injector, NgZone } from '@angular/core'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { WpPreviewModalComponent } from 'core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal'; @Injectable({ providedIn: 'root' }) export class PreviewTriggerService { - private previewModal:WpPreviewModal; + private previewModal:WpPreviewModalComponent; + private modalElement:HTMLElement; + private mouseInModal = false; constructor(readonly opModalService:OpModalService, - readonly ngZone:NgZone, - readonly injector:Injector) { + readonly ngZone:NgZone, + readonly injector:Injector) { } setupListener() { @@ -54,7 +55,7 @@ export class PreviewTriggerService { } this.previewModal = this.opModalService.show( - WpPreviewModal, + WpPreviewModalComponent, this.injector, { workPackageLink: href, event: e }, true, @@ -94,13 +95,12 @@ export class PreviewTriggerService { const previewElement = jQuery(this.modalElement.children[0]); if (previewElement && previewElement.offset()) { - const horizontalHover = e.pageX >= Math.floor(previewElement.offset()!.left) && - e.pageX < previewElement.offset()!.left + previewElement.width()!; - const verticalHover = e.pageY >= Math.floor(previewElement.offset()!.top) && - e.pageY < previewElement.offset()!.top + previewElement.height()!; + const horizontalHover = e.pageX >= Math.floor(previewElement.offset()!.left) + && e.pageX < previewElement.offset()!.left + previewElement.width()!; + const verticalHover = e.pageY >= Math.floor(previewElement.offset()!.top) + && e.pageY < previewElement.offset()!.top + previewElement.height()!; return horizontalHover && verticalHover; } return false; } - } diff --git a/frontend/src/app/core/setup/globals/global-listeners/refresh-on-form-changes.ts b/frontend/src/app/core/setup/globals/global-listeners/refresh-on-form-changes.ts index 14272a3613..616102faae 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/refresh-on-form-changes.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/refresh-on-form-changes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -38,7 +38,7 @@ export function refreshOnFormChanges() { form .find(inputId) .on('change', () => { - window.location.href = url + '?' + form.serialize(); + window.location.href = `${url}?${form.serialize()}`; }); } } diff --git a/frontend/src/app/core/setup/globals/global-listeners/request-for-confirmation.ts b/frontend/src/app/core/setup/globals/global-listeners/request-for-confirmation.ts index b507c3b695..c8d6188db0 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/request-for-confirmation.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/request-for-confirmation.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { PasswordConfirmationModal } from "core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { PasswordConfirmationModalComponent } from 'core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal'; function registerListener( form:JQuery, $event:JQuery.TriggeredEvent, opModalService:OpModalService, - modal:typeof PasswordConfirmationModal) { + modal:typeof PasswordConfirmationModalComponent, +) { const passwordConfirm = form.find('_password_confirmation'); if (passwordConfirm.length > 0) { @@ -41,14 +42,14 @@ function registerListener( } $event.preventDefault(); - const confirmModal = opModalService.show(modal, 'global'); - confirmModal.closingEvent.subscribe((modal:any) => { - if (modal.confirmed) { + const modalComponent = opModalService.show(modal, 'global'); + modalComponent.closingEvent.subscribe((confirmModal:any) => { + if (confirmModal.confirmed) { jQuery('') .attr({ type: 'hidden', name: '_password_confirmation', - value: modal.password_confirmation + value: confirmModal.password_confirmation, }) .appendTo(form); @@ -63,13 +64,13 @@ export function registerRequestForConfirmation($:JQueryStatic) { window.OpenProject .getPluginContext() .then((context) => { - const opModalService = context.services.opModalService; + const { opModalService } = context.services; const passwordConfirmationModal = context.classes.modals.passwordConfirmation; $(document).on( 'submit', 'form[data-request-for-confirmation]', - function(this:any, $event:JQuery.TriggeredEvent) { + function (this:any, $event:JQuery.TriggeredEvent) { const form = jQuery(this); if (form.find('input[name="_password_confirmation"]').length) { @@ -77,6 +78,8 @@ export function registerRequestForConfirmation($:JQueryStatic) { } return registerListener(form, $event, opModalService, passwordConfirmationModal); - }); - }); + }, + ); + }) + .catch(() => {}); } diff --git a/frontend/src/app/core/setup/globals/global-listeners/settings.ts b/frontend/src/app/core/setup/globals/global-listeners/settings.ts index af519fa197..30d86f1f99 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/settings.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/settings.ts @@ -8,13 +8,12 @@ export function listenToSettingChanges() { jQuery('#settings_session_ttl_container').toggle(jQuery(this).is(':checked')); }).trigger('change'); - /** Sync SCM vendor select when enabled SCMs are changed */ jQuery('[name="settings[enabled_scm][]"]').change(function (this:HTMLInputElement) { - var wasDisabled = !this.checked, - vendor = this.value, - select = jQuery('#settings_repositories_automatic_managed_vendor'), - option = select.find('option[value="' + vendor + '"]'); + const wasDisabled = !this.checked; + const vendor = this.value; + const select = jQuery('#settings_repositories_automatic_managed_vendor'); + const option = select.find(`option[value="${vendor}"]`); // Skip non-manageable SCMs if (option.length === 0) { @@ -30,13 +29,15 @@ export function listenToSettingChanges() { /* Javascript for Settings::TextSettingCell */ const langSelectSwitchData = function (select:any) { const self = jQuery(select); - const id:string = self.attr("id") || ''; + const id:string = self.attr('id') || ''; const settingName = id.replace('lang-for-', ''); const newLang = self.val(); const textArea = jQuery(`#settings-${settingName}`); const editor = textArea.siblings('ckeditor-augmented-textarea').data('editor'); - return { id: id, settingName: settingName, newLang: newLang, textArea: textArea, editor: editor }; + return { + id, settingName, newLang, textArea, editor, + }; }; // Upon focusing: @@ -45,7 +46,7 @@ export function listenToSettingChanges() { // * get the current value from the hidden field for that lang and set the editor text to that value. // * Set the name of the textarea to reflect the current lang so that the value stored in the hidden field // is overwritten. - jQuery(".lang-select-switch") + jQuery('.lang-select-switch') .focus(function () { const data = langSelectSwitchData(this); @@ -61,7 +62,7 @@ export function listenToSettingChanges() { }); /* end Javascript for Settings::TextSettingCell */ - jQuery('.admin-settings--form').submit(function () { + jQuery('.admin-settings--form').submit(() => { /* Update consent time if consent required */ if (jQuery('#settings_consent_required').is(':checked') && jQuery('#toggle_consent_time').is(':checked')) { jQuery('#settings_consent_time') @@ -73,14 +74,14 @@ export function listenToSettingChanges() { }); /** Toggle notification settings fields */ - jQuery("#email_delivery_method_switch").on("change", function () { + jQuery('#email_delivery_method_switch').on('change', function () { const delivery_method = jQuery(this).val(); - jQuery(".email_delivery_method_settings").hide(); - jQuery("#email_delivery_method_" + delivery_method).show(); - }).trigger("change"); + jQuery('.email_delivery_method_settings').hide(); + jQuery(`#email_delivery_method_${delivery_method}`).show(); + }).trigger('change'); jQuery('#settings_smtp_authentication').on('change', function () { - var isNone = jQuery(this).val() === 'none'; + const isNone = jQuery(this).val() === 'none'; jQuery('#settings_smtp_user_name,#settings_smtp_password') .closest('.form--field') .toggle(!isNone); @@ -88,8 +89,8 @@ export function listenToSettingChanges() { /** Toggle repository checkout fieldsets required when option is disabled */ jQuery('.settings-repositories--checkout-toggle').change(function (this:HTMLInputElement) { - var wasChecked = this.checked, - fieldset = jQuery(this).closest('fieldset'); + const wasChecked = this.checked; + const fieldset = jQuery(this).closest('fieldset'); fieldset .find('input,select') @@ -99,24 +100,24 @@ export function listenToSettingChanges() { .prop('required', wasChecked); }); - /** Toggle highlighted attributes visibility depending on if the highlighting mode 'inline' was selected*/ + /** Toggle highlighted attributes visibility depending on if the highlighting mode 'inline' was selected */ jQuery('.settings--highlighting-mode select').change(function () { - var highlightingMode = jQuery(this).val(); - jQuery(".settings--highlighted-attributes").toggle(highlightingMode === "inline"); + const highlightingMode = jQuery(this).val(); + jQuery('.settings--highlighted-attributes').toggle(highlightingMode === 'inline'); }); /** Initialize hightlighted attributes checkboxes. If none is selected, it means we want them all. So let's * show them all as selected. * On submitting the form, we remove all checkboxes before sending to communicate, we actually want all and not - * only the selected.*/ + * only the selected. */ if (jQuery(".settings--highlighted-attributes input[type='checkbox']:checked").length === 0) { - jQuery(".settings--highlighted-attributes input[type='checkbox']").prop("checked", true); + jQuery(".settings--highlighted-attributes input[type='checkbox']").prop('checked', true); } - jQuery('#tab-content-work_packages form').submit(function () { - var availableAttributes = jQuery(".settings--highlighted-attributes input[type='checkbox']"); - var selectedAttributes = jQuery(".settings--highlighted-attributes input[type='checkbox']:checked"); + jQuery('#tab-content-work_packages form').submit(() => { + const availableAttributes = jQuery(".settings--highlighted-attributes input[type='checkbox']"); + const selectedAttributes = jQuery(".settings--highlighted-attributes input[type='checkbox']:checked"); if (selectedAttributes.length === availableAttributes.length) { - availableAttributes.prop("checked", false); + availableAttributes.prop('checked', false); } }); } diff --git a/frontend/src/app/core/setup/globals/global-listeners/setup-server-response.ts b/frontend/src/app/core/setup/globals/global-listeners/setup-server-response.ts index e27a72cc22..f0341fb20e 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/setup-server-response.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/setup-server-response.ts @@ -18,26 +18,26 @@ export function setupServerResponse() { * CVE-2011-0447 * 2 - shows and hides ajax indicator */ - jQuery(document).ajaxSend(function (event, request) { - if (jQuery(event.target.activeElement!).closest('[ajax-indicated]').length && - jQuery('ajax-indicator')) { + jQuery(document).ajaxSend((event, request) => { + if (jQuery(event.target.activeElement!).closest('[ajax-indicated]').length + && jQuery('ajax-indicator')) { jQuery('#ajax-indicator').show(); } - var csrf_meta_tag = jQuery('meta[name=csrf-token]'); + const csrf_meta_tag = jQuery('meta[name=csrf-token]'); if (csrf_meta_tag) { - var header = 'X-CSRF-Token', - token = csrf_meta_tag.attr('content'); + const header = 'X-CSRF-Token'; + const token = csrf_meta_tag.attr('content'); request.setRequestHeader(header, token!); } - request.setRequestHeader('X-Authentication-Scheme', "Session"); + request.setRequestHeader('X-Authentication-Scheme', 'Session'); }); // ajaxStop gets called when ALL Requests finish, so we won't need a counter as in PT - jQuery(document).ajaxStop(function () { + jQuery(document).ajaxStop(() => { if (jQuery('#ajax-indicator')) { jQuery('#ajax-indicator').hide(); } @@ -45,36 +45,36 @@ export function setupServerResponse() { }); // show/hide the files table - jQuery(".attachments h4").click(function () { - jQuery(this).toggleClass("closed").next().slideToggle(100); + jQuery('.attachments h4').click(function () { + jQuery(this).toggleClass('closed').next().slideToggle(100); }); let resizeTo:any = null; - jQuery(window).on('resize', function () { + jQuery(window).on('resize', () => { // wait 200 milliseconds for no further resize event // then readjust breadcrumb if (resizeTo) { clearTimeout(resizeTo); } - resizeTo = setTimeout(function () { + resizeTo = setTimeout(() => { jQuery(window).trigger('resizeEnd'); }, 200); }); // Do not close the login window when using it - jQuery('#nav-login-content').click(function (event) { + jQuery('#nav-login-content').click((event) => { event.stopPropagation(); }); // Set focus on first error message - var error_focus = jQuery('a.afocus').first(); - var input_focus = jQuery('.autofocus').first(); + const error_focus = jQuery('a.afocus').first(); + const input_focus = jQuery('.autofocus').first(); if (error_focus !== undefined) { error_focus.focus(); } else if (input_focus !== undefined) { input_focus.focus(); - if (input_focus[0].tagName === "INPUT") { + if (input_focus[0].tagName === 'INPUT') { input_focus.select(); } } @@ -82,11 +82,10 @@ export function setupServerResponse() { addClickEventToAllErrorMessages(); // Click handler for formatting help - jQuery(document.body).on('click', '.formatting-help-link-button', function () { - window.open(window.appBasePath + '/help/wiki_syntax', - "", - "resizable=yes, location=no, width=600, height=640, menubar=no, status=no, scrollbars=yes" - ); + jQuery(document.body).on('click', '.formatting-help-link-button', () => { + window.open(`${window.appBasePath}/help/wiki_syntax`, + '', + 'resizable=yes, location=no, width=600, height=640, menubar=no, status=no, scrollbars=yes'); return false; }); } @@ -102,19 +101,19 @@ function flashCloseHandler() { } function autoHideFlashMessage() { - setTimeout(function () { + setTimeout(() => { jQuery('.flash.autohide-notification').remove(); }, 5000); } function addClickEventToAllErrorMessages() { jQuery('a.afocus').each(function () { - var target = jQuery(this); - target.click(function (evt) { - var field = jQuery('#' + target.attr('href')!.substr(1)); + const target = jQuery(this); + target.click((evt) => { + let field = jQuery(`#${target.attr('href')!.substr(1)}`); if (field === null) { // Cut off '_id' (necessary for select boxes) - field = jQuery('#' + target.attr('href')!.substr(1).concat('_id')); + field = jQuery(`#${target.attr('href')!.substr(1).concat('_id')}`); } target.unbind(evt); return false; @@ -134,14 +133,13 @@ function initMainMenuExpandStatus() { function activateFlash(selector:any) { const flashMessages = jQuery(selector); - flashMessages.each(function (ix, e) { + flashMessages.each((ix, e) => { const flashMessage = jQuery(e); flashMessage.show(); }); } function activateFlashNotice() { - activateFlash('.flash'); } @@ -151,8 +149,7 @@ function activateFlashError() { function focusFirstErroneousField() { const firstErrorSpan = jQuery('span.errorSpan').first(); - const erroneousInput = firstErrorSpan.find('*').filter(":input"); + const erroneousInput = firstErrorSpan.find('*').filter(':input'); erroneousInput.trigger('focus'); } - diff --git a/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts b/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts index 154a61c86a..dbc1c6f6ee 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/toggable-fieldset.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,23 +27,22 @@ //++ function createFieldsetToggleStateLabel(legend:JQuery, text:string) { - var labelClass = 'fieldset-toggle-state-label'; - var toggleLabel = legend.find('a span.' + labelClass); - var legendLink = legend.children('a'); + const labelClass = 'fieldset-toggle-state-label'; + let toggleLabel = legend.find(`a span.${labelClass}`); + const legendLink = legend.children('a'); if (toggleLabel.length === 0) { - toggleLabel = jQuery("").addClass(labelClass) - .addClass("hidden-for-sighted"); + toggleLabel = jQuery('').addClass(labelClass) + .addClass('hidden-for-sighted'); legendLink.append(toggleLabel); } - toggleLabel.text(' ' + text); + toggleLabel.text(` ${text}`); } function setFieldsetToggleState(fieldset:JQuery) { - var legend = fieldset.children('legend'); - + const legend = fieldset.children('legend'); if (fieldset.hasClass('collapsed')) { createFieldsetToggleStateLabel(legend, I18n.t('js.label_collapsed')); @@ -53,22 +52,22 @@ function setFieldsetToggleState(fieldset:JQuery) { } function getFieldset(el:HTMLElement) { - var element = jQuery(el); + const element = jQuery(el); if (element.is('legend')) { return jQuery(el).parent(); - } else if (element.is('fieldset')) { + } if (element.is('fieldset')) { return element; } - throw "Cannot derive fieldset from element!"; + throw new Error('Cannot derive fieldset from element!'); } function toggleFieldset(el:HTMLElement) { - var fieldset = getFieldset(el); + const fieldset = getFieldset(el); // Mark the fieldset that the user has touched it at least once fieldset.attr('data-touched', 'true'); - var contentArea = fieldset.find('> div').not('.form--toolbar'); + const contentArea = fieldset.find('> div').not('.form--toolbar'); fieldset.toggleClass('collapsed'); contentArea.slideToggle('fast'); @@ -80,7 +79,7 @@ export function setupToggableFieldsets() { const fieldsets = jQuery('fieldset.form--fieldset.-collapsible'); // Toggle on click - fieldsets.on('click', '.form--fieldset-legend', function(evt) { + fieldsets.on('click', '.form--fieldset-legend', function (evt) { toggleFieldset(this); evt.preventDefault(); evt.stopPropagation(); @@ -89,8 +88,8 @@ export function setupToggableFieldsets() { // Set initial state fieldsets - .each(function() { - var fieldset = getFieldset(this); + .each(function () { + const fieldset = getFieldset(this); const contentArea = fieldset.find('> div'); if (fieldset.hasClass('collapsed')) { diff --git a/frontend/src/app/core/setup/globals/global-listeners/top-menu-scroll.ts b/frontend/src/app/core/setup/globals/global-listeners/top-menu-scroll.ts index 236a9e332b..9934b3d01b 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/top-menu-scroll.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/top-menu-scroll.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - // Scroll header on mobile in and out when user scrolls the container export function scrollHeaderOnMobile() { const headerHeight = 55; let prevScrollPos = window.scrollY; - window.addEventListener('scroll', function() { + window.addEventListener('scroll', () => { // Condition needed for safari browser to avoid negative positions - const currentScrollPos = window.scrollY < 0 ? 0 : window.scrollY; + const currentScrollPos = window.scrollY < 0 ? 0 : window.scrollY; // Only if sidebar is not opened or search bar is opened - if (!(jQuery('#main').hasClass('hidden-navigation')) || - jQuery('#top-menu').hasClass('-global-search-expanded') || - Math.abs(currentScrollPos - prevScrollPos) <= headerHeight) { // to avoid flickering at the end of the page + if (!(jQuery('#main').hasClass('hidden-navigation')) + || jQuery('#top-menu').hasClass('-global-search-expanded') + || Math.abs(currentScrollPos - prevScrollPos) <= headerHeight) { // to avoid flickering at the end of the page return; } diff --git a/frontend/src/app/core/setup/globals/global-listeners/top-menu.ts b/frontend/src/app/core/setup/globals/global-listeners/top-menu.ts index 127d8db4b2..57a70d282e 100644 --- a/frontend/src/app/core/setup/globals/global-listeners/top-menu.ts +++ b/frontend/src/app/core/setup/globals/global-listeners/top-menu.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,6 +29,7 @@ export const ANIMATION_RATE_MS = 100; export class TopMenu { private hover = false; + private menuIsOpen = false; constructor(readonly menuContainer:JQuery) { @@ -56,7 +57,7 @@ export class TopMenu { } accessibility() { - jQuery(".op-app-menu--dropdown").attr("aria-expanded", "false"); + jQuery('.op-app-menu--dropdown').attr('aria-expanded', 'false'); } toggleClick(dropdown:JQuery) { @@ -76,7 +77,7 @@ export class TopMenu { opening() { this.startHover(); this.menuIsOpen = true; - this.menuContainer.trigger("openedMenu", this.menuContainer); + this.menuContainer.trigger('openedMenu', this.menuContainer); } // the entire menu gets closed, no hover possible afterwards @@ -84,17 +85,17 @@ export class TopMenu { this.stopHover(); this.closeAllItems(); this.menuIsOpen = false; - this.menuContainer.trigger("closedMenu", this.menuContainer); + this.menuContainer.trigger('closedMenu', this.menuContainer); } stopHover() { this.hover = false; - this.menuContainer.removeClass("hover"); + this.menuContainer.removeClass('hover'); } startHover() { this.hover = true; - this.menuContainer.addClass("hover"); + this.menuContainer.addClass('hover'); } closeAllItems() { @@ -117,24 +118,26 @@ export class TopMenu { } openDropdowns() { - return this.menuContainer.find(".op-app-menu--item_dropdown-open"); + return this.menuContainer.find('.op-app-menu--item_dropdown-open'); } dropdowns() { - return this.menuContainer.find(".op-app-menu--item_has-dropdown"); + return this.menuContainer.find('.op-app-menu--item_has-dropdown'); } withHeadingFoldOutAtBorder() { - let menu_start_position; + let menuStartPosition; if (this.menuContainer.next().get(0) !== undefined && (this.menuContainer.next().get(0).tagName === 'H2')) { - menu_start_position = this.menuContainer.next().innerHeight()! + this.menuContainer.next().position().top; - this.menuContainer.find(".op-app-menu--body").css({ top: menu_start_position }); - } else if (this.menuContainer.next().hasClass("wiki-content") && - this.menuContainer.next().children().next().first().get(0) !== undefined && - this.menuContainer.next().children().next().first().get(0).tagName === 'H1') { - var wiki_heading = this.menuContainer.next().children().next().first(); - menu_start_position = wiki_heading.innerHeight()! + wiki_heading.position().top; - this.menuContainer.find(".op-app-menu--body").css({ top: menu_start_position }); + menuStartPosition = this.menuContainer.next().innerHeight()! + this.menuContainer.next().position().top; + this.menuContainer.find('.op-app-menu--body').css({ top: menuStartPosition }); + } else if (this.menuContainer.next().hasClass('wiki-content') + && this.menuContainer.next().children().next().first() + .get(0) !== undefined + && this.menuContainer.next().children().next().first() + .get(0).tagName === 'H1') { + const wikiHeading = this.menuContainer.next().children().next().first(); + menuStartPosition = wikiHeading.innerHeight()! + wikiHeading.position().top; + this.menuContainer.find('.op-app-menu--body').css({ top: menuStartPosition }); } } @@ -144,7 +147,7 @@ export class TopMenu { this.toggleClick(jQuery(it)); return false; }); - jQuery(it).find('op-app-menu--item-action').on('touchstart', function (e) { + jQuery(it).find('op-app-menu--item-action').on('touchstart', (e) => { // This shall avoid the hover event is fired, // which would otherwise lead to menu being closed directly after its opened. // Ignore clicks from within the dropdown @@ -159,7 +162,7 @@ export class TopMenu { } isOpen(dropdown:JQuery) { - return dropdown.filter(".op-app-menu--item_dropdown-open").length === 1; + return dropdown.filter('.op-app-menu--item_dropdown-open').length === 1; } isClosed(dropdown:JQuery) { @@ -169,14 +172,14 @@ export class TopMenu { open(dropdown:JQuery) { this.dontCloseWhenUsing(dropdown); this.closeOtherItems(dropdown); - this.slideAndFocus(dropdown, function () { - dropdown.trigger("opened", dropdown); + this.slideAndFocus(dropdown, () => { + dropdown.trigger('opened', dropdown); }); } close(dropdown:JQuery, immediate?:any) { this.slideUp(dropdown, immediate); - dropdown.trigger("closed", dropdown); + dropdown.trigger('closed', dropdown); } closeOtherItems(dropdown:JQuery) { @@ -188,10 +191,10 @@ export class TopMenu { } dontCloseWhenUsing(dropdown:JQuery) { - jQuery(dropdown).find("li").click(function (event) { + jQuery(dropdown).find('li').click((event) => { event.stopPropagation(); }); - jQuery(dropdown).bind("mousedown mouseup click", function (event) { + jQuery(dropdown).bind('mousedown mouseup click', (event) => { event.stopPropagation(); }); } @@ -202,14 +205,14 @@ export class TopMenu { } slideDown(dropdown:JQuery, callback:any) { - const toDrop = dropdown.find(".op-app-menu--dropdown"); - dropdown.addClass("op-app-menu--item_dropdown-open"); - toDrop.slideDown(ANIMATION_RATE_MS, callback).attr("aria-expanded", "true"); + const toDrop = dropdown.find('.op-app-menu--dropdown'); + dropdown.addClass('op-app-menu--item_dropdown-open'); + toDrop.slideDown(ANIMATION_RATE_MS, callback).attr('aria-expanded', 'true'); } slideUp(dropdown:JQuery, immediate:any) { - const toDrop = jQuery(dropdown).find(".op-app-menu--dropdown"); - dropdown.removeClass("op-app-menu--item_dropdown-open"); + const toDrop = jQuery(dropdown).find('.op-app-menu--dropdown'); + dropdown.removeClass('op-app-menu--item_dropdown-open'); if (immediate) { toDrop.hide(); @@ -217,40 +220,40 @@ export class TopMenu { toDrop.slideUp(ANIMATION_RATE_MS); } - toDrop.attr("aria-expanded", "false"); + toDrop.attr('aria-expanded', 'false'); } // If there is ANY input, it will have precedence over links, // i.e. links will only get focussed, if there is NO input whatsoever focusFirstInputOrLink(dropdown:JQuery) { - var toFocus = dropdown.find("ul :input:visible:first"); + let toFocus = dropdown.find('ul :input:visible:first'); if (toFocus.length === 0) { - toFocus = dropdown.find("ul a:visible:first"); + toFocus = dropdown.find('ul a:visible:first'); } // actually a simple focus should be enough. // The rest is only there to work around a rendering bug in webkit (as of Oct 2011), // occuring mostly inside the login/signup dropdown. toFocus.blur(); - setTimeout(function () { + setTimeout(() => { toFocus.focus(); }, 10); } registerEventHandlers() { - const toggler = jQuery("#main-menu-toggle"); + const toggler = jQuery('#main-menu-toggle'); - this.menuContainer.on("closeDropDown", (event: Event) => { + this.menuContainer.on('closeDropDown', (event:Event) => { this.close(jQuery(event.target as HTMLElement)); - }).on("openDropDown", (event) => { - this.open(jQuery(event.target as HTMLElement)); - }).on("closeMenu", () => { + }).on('openDropDown', (event) => { + this.open(jQuery(event.target)); + }).on('closeMenu', () => { this.closing(); - }).on("openMenu", () => { + }).on('openMenu', () => { this.open(this.dropdowns().first()); this.opening(); }); - toggler.on("click", () => { // click on hamburger icon is closing other menu + toggler.on('click', () => { // click on hamburger icon is closing other menu this.closing(); }); } diff --git a/frontend/src/app/core/setup/globals/onboarding/helpers.ts b/frontend/src/app/core/setup/globals/onboarding/helpers.ts index 8134a6de6d..6e809663b0 100644 --- a/frontend/src/app/core/setup/globals/onboarding/helpers.ts +++ b/frontend/src/app/core/setup/globals/onboarding/helpers.ts @@ -5,23 +5,22 @@ export type OnboardingTourNames = 'backlogs'|'taskboard'|'homescreen'|'main'; export function waitForElement(element:string, container:string, execFunction:Function) { // Wait for the element to be ready - var observer = new MutationObserver(function (mutations, observerInstance) { + const observer = new MutationObserver((mutations, observerInstance) => { if (jQuery(element).length) { observerInstance.disconnect(); // stop observing execFunction(); - return; } }); observer.observe(jQuery(container)[0], { childList: true, - subtree: true + subtree: true, }); } export function demoProjectsLinks() { const demoProjects = []; - const demoProjectsLink = jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")"); - const scrumDemoProjectsLink = jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")"); + const demoProjectsLink = jQuery(`.widget-box.welcome a:contains(${demoProjectName})`); + const scrumDemoProjectsLink = jQuery(`.widget-box.welcome a:contains(${scrumDemoProjectName})`); if (demoProjectsLink.length) { demoProjects.push(demoProjectsLink); diff --git a/frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts b/frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts index 8aecb2f2f0..8d266e3aea 100644 --- a/frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts @@ -1,70 +1,69 @@ -import { wpOnboardingTourSteps } from "core-app/core/setup/globals/onboarding/tours/work_package_tour"; +import { wpOnboardingTourSteps } from 'core-app/core/setup/globals/onboarding/tours/work_package_tour'; import { - demoProjectsLinks, OnboardingTourNames, + demoProjectsLinks, + OnboardingTourNames, onboardingTourStorageKey, preventClickHandler, - waitForElement -} from "core-app/core/setup/globals/onboarding/helpers"; -import { boardTourSteps } from "core-app/core/setup/globals/onboarding/tours/boards_tour"; -import { menuTourSteps } from "core-app/core/setup/globals/onboarding/tours/menu_tour"; -import { homescreenOnboardingTourSteps } from "core-app/core/setup/globals/onboarding/tours/homescreen_tour"; -import { scrumBacklogsTourSteps, scrumTaskBoardTourSteps } from "core-app/core/setup/globals/onboarding/tours/backlogs_tour"; -import { Injector } from "@angular/core"; + waitForElement, +} from 'core-app/core/setup/globals/onboarding/helpers'; +import { boardTourSteps } from 'core-app/core/setup/globals/onboarding/tours/boards_tour'; +import { menuTourSteps } from 'core-app/core/setup/globals/onboarding/tours/menu_tour'; +import { homescreenOnboardingTourSteps } from 'core-app/core/setup/globals/onboarding/tours/homescreen_tour'; +import { + scrumBacklogsTourSteps, + scrumTaskBoardTourSteps, +} from 'core-app/core/setup/globals/onboarding/tours/backlogs_tour'; require('core-vendor/enjoyhint'); - declare global { interface Window { EnjoyHint:any; } } - - export function start(name:OnboardingTourNames) { console.log('star tour', name); switch (name) { - case 'backlogs': - initializeTour('startTaskBoardTour'); - startTour(scrumBacklogsTourSteps()); - break; - case 'taskboard': - initializeTour('startMainTourFromBacklogs'); - startTour(scrumTaskBoardTourSteps()); - break; - case 'homescreen': - initializeTour('startProjectTour', '.widget-box--blocks--buttons a', true); - startTour(homescreenOnboardingTourSteps()); - break; - case 'main': - mainTour(); - break; - + case 'backlogs': + initializeTour('startTaskBoardTour'); + startTour(scrumBacklogsTourSteps()); + break; + case 'taskboard': + initializeTour('startMainTourFromBacklogs'); + startTour(scrumTaskBoardTourSteps()); + break; + case 'homescreen': + initializeTour('startProjectTour', '.widget-box--blocks--buttons a', true); + startTour(homescreenOnboardingTourSteps()); + break; + case 'main': + mainTour(); + break; } } function initializeTour(storageValue:any, disabledElements?:any, projectSelection?:any) { window.onboardingTourInstance = new window.EnjoyHint({ - onStart: function () { + onStart() { jQuery('#content-wrapper, #menu-sidebar').addClass('-hidden-overflow'); }, - onEnd: function () { + onEnd() { sessionStorage.setItem(onboardingTourStorageKey, storageValue); jQuery('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow'); }, - onSkip: function () { + onSkip() { sessionStorage.setItem(onboardingTourStorageKey, 'skipped'); if (disabledElements) { jQuery(disabledElements).removeClass('-disabled').unbind('click', preventClickHandler); } if (projectSelection) { - jQuery.each(demoProjectsLinks(), function (i, e) { + jQuery.each(demoProjectsLinks(), (i, e) => { jQuery(e).off('click'); }); } jQuery('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow'); - } + }, }); } @@ -77,10 +76,10 @@ function startTour(steps:any) { function mainTour() { initializeTour('mainTourFinished'); - const boardsDemoDataAvailable = jQuery('meta[name=boards_demo_data_available]').attr('content') === "true"; + const boardsDemoDataAvailable = jQuery('meta[name=boards_demo_data_available]').attr('content') === 'true'; const eeTokenAvailable = !jQuery('body').hasClass('ee-banners-visible'); - waitForElement('.work-package--results-tbody', '#content', function () { + waitForElement('.work-package--results-tbody', '#content', () => { let steps:any[]; // Check for EE edition, and available seed data of boards. diff --git a/frontend/src/app/core/setup/globals/onboarding/onboarding_tour_trigger.ts b/frontend/src/app/core/setup/globals/onboarding/onboarding_tour_trigger.ts index bed3a5fba2..e1890fd3db 100644 --- a/frontend/src/app/core/setup/globals/onboarding/onboarding_tour_trigger.ts +++ b/frontend/src/app/core/setup/globals/onboarding/onboarding_tour_trigger.ts @@ -1,35 +1,38 @@ // Dynamically loads and triggers the onboarding tour // when on the correct spots -import { demoProjectsLinks, OnboardingTourNames, onboardingTourStorageKey } from "core-app/core/setup/globals/onboarding/helpers"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { + demoProjectsLinks, + OnboardingTourNames, + onboardingTourStorageKey, +} from 'core-app/core/setup/globals/onboarding/helpers'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; export function detectOnboardingTour() { // ------------------------------- Global ------------------------------- const url = new URL(window.location.href); const isMobile = document.body.classList.contains('-browser-mobile'); - const demoProjectsAvailable = jQuery('meta[name=demo_projects_available]').attr('content') === "true"; + const demoProjectsAvailable = jQuery('meta[name=demo_projects_available]').attr('content') === 'true'; let currentTourPart = sessionStorage.getItem(onboardingTourStorageKey); let tourCancelled = false; // ------------------------------- Initial start ------------------------------- // Do not show the tutorial on mobile or when the demo data has been deleted if (!isMobile && demoProjectsAvailable) { - // Start after the intro modal (language selection) // This has to be changed once the project selection is implemented - if (url.searchParams.get("first_time_user") && demoProjectsLinks().length === 2) { + if (url.searchParams.get('first_time_user') && demoProjectsLinks().length === 2) { currentTourPart = ''; sessionStorage.setItem(onboardingTourStorageKey, 'readyToStart'); // Start automatically when the language selection is closed - jQuery('.op-modal--close-button').click(function () { + jQuery('.op-modal--close-button').click(() => { tourCancelled = true; triggerTour('homescreen'); }); - //Start automatically when the escape button is pressed - document.addEventListener('keydown', function (event) { - if (event.key === "Escape" && !tourCancelled) { + // Start automatically when the escape button is pressed + document.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && !tourCancelled) { tourCancelled = true; triggerTour('homescreen'); } @@ -37,32 +40,31 @@ export function detectOnboardingTour() { } // ------------------------------- Tutorial Homescreen page ------------------------------- - if (currentTourPart === "readyToStart") { + if (currentTourPart === 'readyToStart') { triggerTour('homescreen'); } // ------------------------------- Tutorial WP page ------------------------------- - if (currentTourPart === "startMainTourFromBacklogs" || url.searchParams.get("start_onboarding_tour")) { + if (currentTourPart === 'startMainTourFromBacklogs' || url.searchParams.get('start_onboarding_tour')) { triggerTour('main'); } // ------------------------------- Tutorial Backlogs page ------------------------------- - if (url.searchParams.get("start_scrum_onboarding_tour")) { + if (url.searchParams.get('start_scrum_onboarding_tour')) { if (jQuery('.backlogs-menu-item').length > 0) { triggerTour('backlogs'); } } // ------------------------------- Tutorial Task Board page ------------------------------- - if (currentTourPart === "startTaskBoardTour") { + if (currentTourPart === 'startTaskBoardTour') { triggerTour('taskboard'); } } } async function triggerTour(name:OnboardingTourNames) { - debugLog("Loading and triggering onboarding tour " + name); + debugLog(`Loading and triggering onboarding tour ${name}`); const tour = await import(/* webpackChunkName: "onboarding-tour" */ './onboarding_tour'); tour.start(name); } - diff --git a/frontend/src/app/core/setup/globals/onboarding/tours/backlogs_tour.ts b/frontend/src/app/core/setup/globals/onboarding/tours/backlogs_tour.ts index 05e46442ba..787326e985 100644 --- a/frontend/src/app/core/setup/globals/onboarding/tours/backlogs_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/tours/backlogs_tour.ts @@ -1,34 +1,32 @@ -import { onboardingTourStorageKey } from "core-app/core/setup/globals/onboarding/helpers"; - export function scrumBacklogsTourSteps() { return [ { 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs.overview'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow' + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', }, { - 'event_type': 'next', - 'selector': '#sprint_backlogs_container .backlog .menu-trigger', - 'description': I18n.t('js.onboarding.steps.backlogs.task_board_arrow'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { + event_type: 'next', + selector: '#sprint_backlogs_container .backlog .menu-trigger', + description: I18n.t('js.onboarding.steps.backlogs.task_board_arrow'), + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { jQuery('#sprint_backlogs_container .backlog .menu-trigger')[0].click(); - } + }, }, { - 'event_type': 'next', - 'selector': '#sprint_backlogs_container .backlog .menu .items', - 'description': I18n.t('js.onboarding.steps.backlogs.task_board_select'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark', - onNext: function () { + event_type: 'next', + selector: '#sprint_backlogs_container .backlog .menu .items', + description: I18n.t('js.onboarding.steps.backlogs.task_board_select'), + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark', + onNext() { jQuery('#sprint_backlogs_container .backlog .show_task_board')[0].click(); - } - } + }, + }, ]; } @@ -36,17 +34,17 @@ export function scrumTaskBoardTourSteps() { return [ { 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs.task_board'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow' + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', }, { 'next #main-menu-work-packages-wrapper': I18n.t('js.onboarding.steps.wp.toggler'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { jQuery('#main-menu-work-packages')[0].click(); - } + }, }, ]; } diff --git a/frontend/src/app/core/setup/globals/onboarding/tours/boards_tour.ts b/frontend/src/app/core/setup/globals/onboarding/tours/boards_tour.ts index f778894531..6e644a4e43 100644 --- a/frontend/src/app/core/setup/globals/onboarding/tours/boards_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/tours/boards_tour.ts @@ -1,47 +1,49 @@ -import { waitForElement } from "core-app/core/setup/globals/onboarding/helpers"; +import { waitForElement } from 'core-app/core/setup/globals/onboarding/helpers'; export function boardTourSteps() { return [ { 'next .board-view-menu-item': I18n.t('js.onboarding.steps.boards.overview'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { jQuery('.board-view-menu-item ~ .toggler')[0].click(); - waitForElement('.boards--menu-items', '#main-menu', function () { + waitForElement('.boards--menu-items', '#main-menu', () => { jQuery(".main-menu--children-sub-item:contains('Kanban')")[0].click(); }); - } + }, }, { 'next .board-list--container': I18n.t('js.onboarding.steps.boards.lists'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow', - 'timeout': function () { - return new Promise(function (resolve) { - waitForElement('.wp-card', '#content', function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', + timeout() { + return new Promise((resolve) => { + waitForElement('.wp-card', '#content', () => { resolve(undefined); }); }); - } + }, }, { 'next .board-list--add-button': I18n.t('js.onboarding.steps.boards.add'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, }, { 'next .boards-list--container': I18n.t('js.onboarding.steps.boards.drag'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow', - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', + onNext() { const backArrows = Array.from(document.getElementsByClassName('main-menu--arrow-left-to-project')); const boardsBackArrow = backArrows.find((backArrow) => (backArrow.nextElementSibling as HTMLElement).innerText === 'Boards') as HTMLElement; - boardsBackArrow && boardsBackArrow.click(); - } - } + if (boardsBackArrow) { + boardsBackArrow.click(); + } + }, + }, ]; } diff --git a/frontend/src/app/core/setup/globals/onboarding/tours/homescreen_tour.ts b/frontend/src/app/core/setup/globals/onboarding/tours/homescreen_tour.ts index cc74e40a59..0d5c8c7df4 100644 --- a/frontend/src/app/core/setup/globals/onboarding/tours/homescreen_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/tours/homescreen_tour.ts @@ -1,35 +1,39 @@ -import { demoProjectName, preventClickHandler, scrumDemoProjectName } from "core-app/core/setup/globals/onboarding/helpers"; +import { + demoProjectName, + preventClickHandler, + scrumDemoProjectName, +} from 'core-app/core/setup/globals/onboarding/helpers'; export function homescreenOnboardingTourSteps() { return [ { 'next .op-app-header': I18n.t('js.onboarding.steps.welcome'), - 'skipButton': { className: 'enjoyhint_btn-transparent', text: I18n.t('js.onboarding.buttons.skip') }, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-hidden-arrow', - 'bottom': 7 + skipButton: { className: 'enjoyhint_btn-transparent', text: I18n.t('js.onboarding.buttons.skip') }, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-hidden-arrow', + bottom: 7, }, { - 'description': I18n.t('js.onboarding.steps.project_selection'), - 'selector': '.widget-box.welcome', - 'event': 'custom', - 'showSkip': false, - 'containerClass': '-dark -hidden-arrow', - 'clickable': true, - onBeforeStart: function () { + description: I18n.t('js.onboarding.steps.project_selection'), + selector: '.widget-box.welcome', + event: 'custom', + showSkip: false, + containerClass: '-dark -hidden-arrow', + clickable: true, + onBeforeStart() { // Handle the correct project selection and redirection // This will be removed once the project selection is implemented - jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")").click(function (this:HTMLAnchorElement) { + jQuery(`.widget-box.welcome a:contains(${scrumDemoProjectName})`).click(function (this:HTMLAnchorElement) { window.onboardingTourInstance.trigger('next'); - window.location.href = this.href + '/backlogs/?start_scrum_onboarding_tour=true'; + window.location.href = `${this.href}/backlogs/?start_scrum_onboarding_tour=true`; }); - jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")").click(function (this:HTMLAnchorElement) { + jQuery(`.widget-box.welcome a:contains(${demoProjectName})`).click(function (this:HTMLAnchorElement) { window.onboardingTourInstance.trigger('next'); - window.location.href = this.href + '/work_packages/?start_onboarding_tour=true'; + window.location.href = `${this.href}/work_packages/?start_onboarding_tour=true`; }); // Disable clicks on other links jQuery('.widget-box.welcome a').addClass('-disabled').bind('click', preventClickHandler); - } - } + }, + }, ]; } diff --git a/frontend/src/app/core/setup/globals/onboarding/tours/menu_tour.ts b/frontend/src/app/core/setup/globals/onboarding/tours/menu_tour.ts index 623f26c806..3523298d49 100644 --- a/frontend/src/app/core/setup/globals/onboarding/tours/menu_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/tours/menu_tour.ts @@ -2,24 +2,24 @@ export function menuTourSteps() { return [ { 'next .members-menu-item': I18n.t('js.onboarding.steps.members'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, }, { 'next .wiki-menu--main-item': I18n.t('js.onboarding.steps.wiki'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, }, { 'next .op-quick-add-menu': I18n.t('js.onboarding.steps.quick_add_button'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, }, { 'next .op-app-help': I18n.t('js.onboarding.steps.help_menu'), - 'shape': 'circle', - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.got_it') } - } + shape: 'circle', + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.got_it') }, + }, ]; } diff --git a/frontend/src/app/core/setup/globals/onboarding/tours/work_package_tour.ts b/frontend/src/app/core/setup/globals/onboarding/tours/work_package_tour.ts index 068e3bc711..5e0f5a0a4b 100644 --- a/frontend/src/app/core/setup/globals/onboarding/tours/work_package_tour.ts +++ b/frontend/src/app/core/setup/globals/onboarding/tours/work_package_tour.ts @@ -1,70 +1,70 @@ -import { waitForElement } from "core-app/core/setup/globals/onboarding/helpers"; +import { waitForElement } from 'core-app/core/setup/globals/onboarding/helpers'; export function wpOnboardingTourSteps():any[] { return [ { 'next .wp-table--row': I18n.t('js.onboarding.steps.wp.list'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { - jQuery(".inline-edit--display-field.id a ")[0].click(); - } + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { + jQuery('.inline-edit--display-field.id a ')[0].click(); + }, }, { 'next .work-packages-full-view--split-left': I18n.t('js.onboarding.steps.wp.full_view'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow' + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', }, { 'next .work-packages-back-button': I18n.t('js.onboarding.steps.wp.back_button'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { jQuery('.work-packages-back-button')[0].click(); - } + }, }, { 'next .add-work-package': I18n.t('js.onboarding.steps.wp.create_button'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'shape': 'circle', - 'timeout': function () { - return new Promise(function (resolve) { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + shape: 'circle', + timeout() { + return new Promise((resolve) => { // We are waiting here for the badge to appear, // because its the last that appears and it shifts the WP create button to the left. // Thus it is important that the tour rendering starts after the badge is visible - waitForElement('#work-packages-filter-toggle-button .badge', '#content', function () { + waitForElement('#work-packages-filter-toggle-button .badge', '#content', () => { resolve(undefined); }); }); }, - onNext: function () { + onNext() { jQuery('#wp-view-toggle-button').click(); - } + }, }, { 'next #wp-view-toggle-button': I18n.t('js.onboarding.steps.wp.timeline_button'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'bottom': '-64', - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + bottom: '-64', + onNext() { jQuery('#wp-view-context-menu .icon-view-timeline')[0].click(); - } + }, }, { 'next .work-packages-tabletimeline--timeline-side': I18n.t('js.onboarding.steps.wp.timeline'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - 'containerClass': '-dark -hidden-arrow' + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + containerClass: '-dark -hidden-arrow', }, { 'next .main-menu--arrow-left-to-project': I18n.t('js.onboarding.steps.sidebar_arrow'), - 'showSkip': false, - 'nextButton': { text: I18n.t('js.onboarding.buttons.next') }, - onNext: function () { + showSkip: false, + nextButton: { text: I18n.t('js.onboarding.buttons.next') }, + onNext() { jQuery('.main-menu--arrow-left-to-project')[0].click(); - } - } + }, + }, ]; } diff --git a/frontend/src/app/core/setup/globals/openproject.ts b/frontend/src/app/core/setup/globals/openproject.ts index 96d83248b9..36ae918aae 100644 --- a/frontend/src/app/core/setup/globals/openproject.ts +++ b/frontend/src/app/core/setup/globals/openproject.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,22 +29,23 @@ import { OpenProjectPluginContext } from 'core-app/features/plugins/plugin-context'; import { input, InputState } from 'reactivestates'; import { take } from 'rxjs/operators'; -import { GlobalHelpers } from "core-app/core/setup/globals/global-helpers"; +import { GlobalHelpers } from 'core-app/core/setup/globals/global-helpers'; /** * OpenProject instance methods */ export class OpenProject { - public pluginContext:InputState = input(); public helpers = new GlobalHelpers(); /** Globally setable variable whether the page was edited */ public pageWasEdited = false; + /** Globally setable variable whether the page form is submitted. * Necessary to avoid a data loss warning on beforeunload */ public pageIsSubmitted = false; + /** Globally setable variable whether any of the EditFormComponent * contain changes. * Necessary to show a data loss warning on beforeunload when clicking @@ -72,11 +73,11 @@ export class OpenProject { } public get isStandardEdition():boolean { - return this.edition === "standard"; + return this.edition === 'standard'; } public get isBimEdition():boolean { - return this.edition === "bim"; + return this.edition === 'bim'; } /** diff --git a/frontend/src/app/core/setup/globals/tree-menu.ts b/frontend/src/app/core/setup/globals/tree-menu.ts index 0767294bb1..b41a761968 100644 --- a/frontend/src/app/core/setup/globals/tree-menu.ts +++ b/frontend/src/app/core/setup/globals/tree-menu.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,15 +27,13 @@ //++ (function ($) { - "use strict"; - - $(function() { + $(() => { // set selected page for menu tree if provided. - $('[data-selected-page]').closest('.tree-menu--container').each(function(_i:number, tree:HTMLElement) { + $('[data-selected-page]').closest('.tree-menu--container').each((_i:number, tree:HTMLElement) => { const selectedPage = $(tree).data('selected-page'); if (selectedPage) { - const selected = $('[slug="' + selectedPage + '"]', tree); + const selected = $(`[slug="${selectedPage}"]`, tree); selected.toggleClass('-selected', true); if (selected.length > 1) { selected[0].scrollIntoView(); @@ -43,7 +41,7 @@ } }); - function toggle (event:any) { + function toggle(event:any) { // ignore the event if a key different from ENTER was pressed. if (event.type === 'keypress' && event.which !== 13) { return false; diff --git a/frontend/src/app/core/setup/init-globals.ts b/frontend/src/app/core/setup/init-globals.ts index 63f8ab8577..481f5c6d7c 100644 --- a/frontend/src/app/core/setup/init-globals.ts +++ b/frontend/src/app/core/setup/init-globals.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,4 +34,3 @@ import './globals/dynamic-bootstrapper'; import './globals/global-listeners'; import './globals/openproject'; import './globals/tree-menu'; - diff --git a/frontend/src/app/core/setup/init-locale.ts b/frontend/src/app/core/setup/init-locale.ts index 9f3871a4a8..33f05fbbfb 100644 --- a/frontend/src/app/core/setup/init-locale.ts +++ b/frontend/src/app/core/setup/init-locale.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import * as moment from "moment"; +import * as moment from 'moment'; export function initializeLocale() { const meta = document.querySelector('meta[name=openproject_initializer]') as HTMLMetaElement; @@ -37,28 +37,28 @@ export function initializeLocale() { I18n.locale = locale; I18n.firstDayOfWeek = firstDayOfWeek; - if (!isNaN(firstDayOfWeek) && !isNaN(firstWeekOfYear)) { + if (!Number.isNaN(firstDayOfWeek) && !Number.isNaN(firstWeekOfYear)) { moment.updateLocale(locale, { week: { dow: firstDayOfWeek, - doy: 7 + firstDayOfWeek - firstWeekOfYear - } + doy: 7 + firstDayOfWeek - firstWeekOfYear, + }, }); } // Override the default pluralization function to allow // "other" to be used as a fallback for "one" in languages where one is not set // (japanese, for example) - I18n.pluralization["default"] = function (count:number) { + I18n.pluralization.default = function (count:number) { switch (count) { case 0: - return ["zero", "other"]; + return ['zero', 'other']; case 1: - return ["one", "other"]; + return ['one', 'other']; default: - return ["other"]; + return ['other']; } }; return import(/* webpackChunkName: "locale" */ `../../../locales/${I18n.locale}.js`); -} \ No newline at end of file +} diff --git a/frontend/src/app/core/setup/init-vendors.ts b/frontend/src/app/core/setup/init-vendors.ts index c94d4b1241..9ed82ad0ed 100644 --- a/frontend/src/app/core/setup/init-vendors.ts +++ b/frontend/src/app/core/setup/init-vendors.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -67,8 +67,7 @@ require('moment-timezone/builds/moment-timezone-with-data.min.js'); require('expose-loader?URI!urijs'); require('urijs/src/URITemplate'); -require("expose-loader?I18n!core-vendor/i18n"); +require('expose-loader?I18n!core-vendor/i18n'); // Localization for fullcalendar -require("@fullcalendar/core/locales-all"); - +require('@fullcalendar/core/locales-all'); diff --git a/frontend/src/app/core/states/states.service.ts b/frontend/src/app/core/states/states.service.ts index 372a2ec437..ff9ab6c576 100644 --- a/frontend/src/app/core/states/states.service.ts +++ b/frontend/src/app/core/states/states.service.ts @@ -1,23 +1,25 @@ -import { input, InputState, multiInput, MultiInputState, StatesGroup } from 'reactivestates'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Subject } from "rxjs"; -import { WorkPackageDisplayRepresentationValue } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { CapabilityResource } from "core-app/features/hal/resources/capability-resource"; -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { StatusResource } from "core-app/features/hal/resources/status-resource"; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { PlaceholderUserResource } from "core-app/features/hal/resources/placeholder-user-resource"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; -import { RoleResource } from "core-app/features/hal/resources/role-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { PostResource } from "core-app/features/hal/resources/post-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { + input, InputState, multiInput, MultiInputState, StatesGroup, +} from 'reactivestates'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Subject } from 'rxjs'; +import { WorkPackageDisplayRepresentationValue } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { CapabilityResource } from 'core-app/features/hal/resources/capability-resource'; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { PlaceholderUserResource } from 'core-app/features/hal/resources/placeholder-user-resource'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { PostResource } from 'core-app/features/hal/resources/post-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export class States extends StatesGroup { name = 'MainStore'; @@ -58,7 +60,6 @@ export class States extends StatesGroup { /* /api/v3/roles */ roles = multiInput(); - // Work Package query states queries = new QueryAvailableDataStates(); @@ -75,11 +76,11 @@ export class States extends StatesGroup { state = this.additional[stateName] = multiInput(); } - return state as any; + return state; } forResource(resource:T):InputState|undefined { - const stateName = _.camelCase(resource._type) + 's'; + const stateName = `${_.camelCase(resource._type)}s`; const state = this.forType(stateName); return state && state.get(resource.id!); diff --git a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts index 78156755c2..8197f534db 100644 --- a/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts +++ b/frontend/src/app/features/admin/editable-query-props/editable-query-props.component.ts @@ -1,7 +1,9 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { ExternalQueryConfigurationService } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ExternalQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; export const editableQueryPropsSelector = 'editable-query-props'; @@ -12,20 +14,22 @@ export const editableQueryPropsSelector = 'editable-query-props'; }) export class EditableQueryPropsComponent implements OnInit { id:string|null; + name:string|null; + urlParams = false; queryProps:string; text = { - edit_query: this.I18n.t('js.admin.type_form.edit_query') + edit_query: this.I18n.t('js.admin.type_form.edit_query'), }; constructor(private elementRef:ElementRef, - private I18n:I18nService, - private cdRef:ChangeDetectorRef, - private urlParamsHelper:UrlParamsHelperService, - private externalQuery:ExternalQueryConfigurationService) { + private I18n:I18nService, + private cdRef:ChangeDetectorRef, + private urlParamsHelper:UrlParamsHelperService, + private externalQuery:ExternalQueryConfigurationService) { } ngOnInit() { @@ -38,24 +42,26 @@ export class EditableQueryPropsComponent implements OnInit { } public editQuery() { - let queryProps:any = this.queryProps; + const queryProperties = (() => { + if (this.urlParams) { + return this.queryProps; + } - if (!this.urlParams) { try { - queryProps = JSON.parse(this.queryProps); + return JSON.parse(this.queryProps); } catch (e) { console.error(`Failed to parse query props from ${this.queryProps}: ${e}`); - queryProps = {}; + return {}; } - } + })(); this.externalQuery.show({ - currentQuery: queryProps, + currentQuery: queryProperties, urlParams: this.urlParams, callback: (queryProps:any) => { this.queryProps = this.urlParams ? queryProps : JSON.stringify(queryProps); this.cdRef.detectChanges(); - } + }, }); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/admin/openproject-admin.module.ts b/frontend/src/app/features/admin/openproject-admin.module.ts index 7adaf45c54..0472d27082 100644 --- a/frontend/src/app/features/admin/openproject-admin.module.ts +++ b/frontend/src/app/features/admin/openproject-admin.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,20 +27,20 @@ //++ import { NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; +import { OPSharedModule } from 'core-app/shared/shared.module'; import { DragulaModule } from 'ng2-dragula'; -import { TypeFormAttributeGroupComponent } from "core-app/features/admin/types/attribute-group.component"; -import { TypeFormConfigurationComponent } from "core-app/features/admin/types/type-form-configuration.component"; -import { TypeFormQueryGroupComponent } from "core-app/features/admin/types/query-group.component"; -import { GroupEditInPlaceComponent } from "core-app/features/admin/types/group-edit-in-place.component"; -import { OpenprojectAccessibilityModule } from "core-app/shared/directives/a11y/openproject-a11y.module"; -import { EditableQueryPropsComponent } from "core-app/features/admin/editable-query-props/editable-query-props.component"; +import { TypeFormAttributeGroupComponent } from 'core-app/features/admin/types/attribute-group.component'; +import { TypeFormConfigurationComponent } from 'core-app/features/admin/types/type-form-configuration.component'; +import { TypeFormQueryGroupComponent } from 'core-app/features/admin/types/query-group.component'; +import { GroupEditInPlaceComponent } from 'core-app/features/admin/types/group-edit-in-place.component'; +import { OpenprojectAccessibilityModule } from 'core-app/shared/directives/a11y/openproject-a11y.module'; +import { EditableQueryPropsComponent } from 'core-app/features/admin/editable-query-props/editable-query-props.component'; @NgModule({ imports: [ DragulaModule.forRoot(), OPSharedModule, - OpenprojectAccessibilityModule + OpenprojectAccessibilityModule, ], providers: [ ], @@ -50,6 +50,6 @@ import { EditableQueryPropsComponent } from "core-app/features/admin/editable-qu TypeFormConfigurationComponent, GroupEditInPlaceComponent, EditableQueryPropsComponent, - ] + ], }) export class OpenprojectAdminModule { } diff --git a/frontend/src/app/features/admin/types/attribute-group.component.html b/frontend/src/app/features/admin/types/attribute-group.component.html index 482a206527..64d59fd0c3 100644 --- a/frontend/src/app/features/admin/types/attribute-group.component.html +++ b/frontend/src/app/features/admin/types/attribute-group.component.html @@ -1,11 +1,11 @@
- - +
diff --git a/frontend/src/app/features/admin/types/attribute-group.component.ts b/frontend/src/app/features/admin/types/attribute-group.component.ts index 7fd8b55dcd..69d04294ab 100644 --- a/frontend/src/app/features/admin/types/attribute-group.component.ts +++ b/frontend/src/app/features/admin/types/attribute-group.component.ts @@ -1,24 +1,27 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { TypeFormAttribute, TypeGroup } from "core-app/features/admin/types/type-form-configuration.component"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { TypeFormAttribute, TypeGroup } from 'core-app/features/admin/types/type-form-configuration.component'; @Component({ - selector: 'type-form-attribute-group', + selector: 'op-type-form-attribute-group', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './attribute-group.component.html' + templateUrl: './attribute-group.component.html', }) export class TypeFormAttributeGroupComponent { @Input() public group:TypeGroup; @Output() public deleteGroup = new EventEmitter(); + @Output() public removeAttribute = new EventEmitter(); text = { - custom_field: this.I18n.t('js.admin.type_form.custom_field') + custom_field: this.I18n.t('js.admin.type_form.custom_field'), }; constructor(private I18n:I18nService, - private cdRef:ChangeDetectorRef) { + private cdRef:ChangeDetectorRef) { } rename(newValue:string) { @@ -28,7 +31,7 @@ export class TypeFormAttributeGroupComponent { } removeFromGroup(attribute:TypeFormAttribute) { - this.group.attributes = this.group.attributes.filter(a => a !== attribute); + this.group.attributes = this.group.attributes.filter((a) => a !== attribute); this.removeAttribute.emit(attribute); } } diff --git a/frontend/src/app/features/admin/types/group-edit-in-place.component.ts b/frontend/src/app/features/admin/types/group-edit-in-place.component.ts index 356db4fc96..a9f9bb3f56 100644 --- a/frontend/src/app/features/admin/types/group-edit-in-place.component.ts +++ b/frontend/src/app/features/admin/types/group-edit-in-place.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -33,18 +33,18 @@ import { EventEmitter, Input, OnInit, - Output + Output, } from '@angular/core'; -import { TypeBannerService } from "core-app/features/admin/types/type-banner.service"; - +import { TypeBannerService } from 'core-app/features/admin/types/type-banner.service'; @Component({ - selector: 'group-edit-in-place', + selector: 'op-group-edit-in-place', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './group-edit-in-place.html' + templateUrl: './group-edit-in-place.html', }) export class GroupEditInPlaceComponent implements OnInit { @Input() public placeholder = ''; + @Input() public name:string; @Output() public onValueChange = new EventEmitter(); @@ -54,7 +54,7 @@ export class GroupEditInPlaceComponent implements OnInit { public editedName:string; constructor(private bannerService:TypeBannerService, - protected readonly cdRef:ChangeDetectorRef) { + protected readonly cdRef:ChangeDetectorRef) { } ngOnInit():void { @@ -71,7 +71,7 @@ export class GroupEditInPlaceComponent implements OnInit { () => this.bannerService.showEEOnlyHint(), () => { this.editing = true; - } + }, ); } diff --git a/frontend/src/app/features/admin/types/query-group.component.html b/frontend/src/app/features/admin/types/query-group.component.html index 52d7bb8f2a..9baf9efca6 100644 --- a/frontend/src/app/features/admin/types/query-group.component.html +++ b/frontend/src/app/features/admin/types/query-group.component.html @@ -1,11 +1,11 @@
- - +
diff --git a/frontend/src/app/features/admin/types/query-group.component.ts b/frontend/src/app/features/admin/types/query-group.component.ts index 13a8b40df6..402a3c0192 100644 --- a/frontend/src/app/features/admin/types/query-group.component.ts +++ b/frontend/src/app/features/admin/types/query-group.component.ts @@ -1,23 +1,26 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ - selector: 'type-form-query-group', + selector: 'op-type-form-query-group', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './query-group.component.html' + templateUrl: './query-group.component.html', }) export class TypeFormQueryGroupComponent { - text = { - edit_query: this.I18n.t('js.admin.type_form.edit_query') + edit_query: this.I18n.t('js.admin.type_form.edit_query'), }; @Input() public group:any; + @Output() public editQuery = new EventEmitter(); + @Output() public deleteGroup = new EventEmitter(); constructor(private I18n:I18nService, - private cdRef:ChangeDetectorRef) { + private cdRef:ChangeDetectorRef) { } rename(newValue:string) { diff --git a/frontend/src/app/features/admin/types/type-banner.service.ts b/frontend/src/app/features/admin/types/type-banner.service.ts index 50047bf2ed..8024f83582 100644 --- a/frontend/src/app/features/admin/types/type-banner.service.ts +++ b/frontend/src/app/features/admin/types/type-banner.service.ts @@ -2,14 +2,13 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { BannersService } from 'core-app/core/enterprise/banners.service'; import { Inject, Injectable } from '@angular/core'; import { DOCUMENT } from '@angular/common'; -import { ConfirmDialogService } from "core-app/shared/components/modals/confirm-dialog/confirm-dialog.service"; +import { ConfirmDialogService } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.service'; @Injectable() export class TypeBannerService extends BannersService { - constructor(@Inject(DOCUMENT) protected documentElement:Document, - private confirmDialog:ConfirmDialogService, - private I18n:I18nService) { + private confirmDialog:ConfirmDialogService, + private I18n:I18nService) { super(documentElement); } @@ -19,11 +18,11 @@ export class TypeBannerService extends BannersService { title: this.I18n.t('js.types.attribute_groups.upgrade_to_ee'), text: this.I18n.t('js.types.attribute_groups.upgrade_to_ee_text'), button_continue: this.I18n.t('js.types.attribute_groups.more_information'), - button_cancel: this.I18n.t('js.types.attribute_groups.nevermind') - } + button_cancel: this.I18n.t('js.types.attribute_groups.nevermind'), + }, }).then(() => { window.location.href = 'https://www.openproject.org/enterprise-edition/?utm_source=unknown&utm_medium=community-edition&utm_campaign=form-configuration'; - }); + }) + .catch(() => {}); } } - diff --git a/frontend/src/app/features/admin/types/type-form-configuration.component.ts b/frontend/src/app/features/admin/types/type-form-configuration.component.ts index 2409a5d563..03e106d991 100644 --- a/frontend/src/app/features/admin/types/type-form-configuration.component.ts +++ b/frontend/src/app/features/admin/types/type-form-configuration.component.ts @@ -1,15 +1,16 @@ -import { AfterViewInit, Component, ElementRef, OnInit } from '@angular/core'; +import { + AfterViewInit, Component, ElementRef, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; import { ExternalRelationQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; import { DomAutoscrollService } from 'core-app/shared/helpers/drag-and-drop/dom-autoscroll.service'; import { DragulaService, DrakeWithModels } from 'ng2-dragula'; -import { Drake } from 'dragula'; -import { GonService } from "core-app/core/gon/gon.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { install_menu_logic } from "core-app/core/setup/globals/global-listeners/action-menu"; -import { ConfirmDialogService } from "core-app/shared/components/modals/confirm-dialog/confirm-dialog.service"; -import { TypeBannerService } from "core-app/features/admin/types/type-banner.service"; +import { GonService } from 'core-app/core/gon/gon.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { installMenuLogic } from 'core-app/core/setup/globals/global-listeners/action-menu'; +import { ConfirmDialogService } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.service'; +import { TypeBannerService } from 'core-app/features/admin/types/type-banner.service'; export type TypeGroupType = 'attribute'|'query'; @@ -37,10 +38,9 @@ export const emptyTypeGroup = '__empty'; templateUrl: './type-form-configuration.html', providers: [ TypeBannerService, - ] + ], }) export class TypeFormConfigurationComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { - public text = { drag_to_activate: this.I18n.t('js.admin.type_form.drag_to_activate'), reset: this.I18n.t('js.admin.type_form.reset_to_defaults'), @@ -53,25 +53,30 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen }; private autoscroll:any; + private element:HTMLElement; + private form:JQuery; + private submit:JQuery; public groups:TypeGroup[] = []; + public inactives:TypeFormAttribute[] = []; private attributeDrake:DrakeWithModels; + private groupsDrake:DrakeWithModels; private no_filter_query:string; constructor(private elementRef:ElementRef, - private I18n:I18nService, - private Gon:GonService, - private dragula:DragulaService, - private confirmDialog:ConfirmDialogService, - private notificationsService:NotificationsService, - private externalRelationQuery:ExternalRelationQueryConfigurationService) { + private I18n:I18nService, + private Gon:GonService, + private dragula:DragulaService, + private confirmDialog:ConfirmDialogService, + private notificationsService:NotificationsService, + private externalRelationQuery:ExternalRelationQueryConfigurationService) { super(); } @@ -87,12 +92,12 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen // enough, we need to memoize whether we have already submitted. let submitted = false; - this.form.on('submit', (event) => { + this.form.on('submit', () => { submitted = true; }); // Capture mousedown on button because firefox breaks blur on click - this.submit.on('mousedown', (event) => { + this.submit.on('mousedown', () => { setTimeout(() => { if (!submitted) { this.form.trigger('submit'); @@ -111,7 +116,7 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen this.groupsDrake = this .dragula .createGroup('groups', { - moves: (el, source, handle:HTMLElement) => handle.classList.contains('group-handle') + moves: (el, source, handle:HTMLElement) => handle.classList.contains('group-handle'), }) .drake; @@ -119,7 +124,7 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen this.attributeDrake = this .dragula .createGroup('attributes', { - moves: (el, source, handle:HTMLElement) => handle.classList.contains('attribute-handle') + moves: (el, source, handle:HTMLElement) => handle.classList.contains('attribute-handle'), }) .drake; @@ -133,24 +138,25 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen const that = this; this.autoscroll = new DomAutoscrollService( [ - document.getElementById('content-wrapper')! + document.getElementById('content-wrapper')!, ], { margin: 25, maxSpeed: 10, scrollWhenOutside: true, - autoScroll: function (this:any) { + autoScroll(this:any) { const groups = that.groupsDrake && that.groupsDrake.dragging; const attributes = that.attributeDrake && that.attributeDrake.dragging; return groups || attributes; - } - }); + }, + }, + ); } ngAfterViewInit() { const menu = jQuery(this.elementRef.nativeElement).find('.toolbar-items'); - install_menu_logic(menu); + installMenuLogic(menu); } public deactivateAttribute(attribute:TypeFormAttribute) { @@ -166,13 +172,13 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen // Disable display mode and timeline for now since we don't want users to enable it const disabledTabs = { 'display-settings': I18n.t('js.work_packages.table_configuration.embedded_tab_disabled'), - 'timelines': I18n.t('js.work_packages.table_configuration.embedded_tab_disabled') + timelines: I18n.t('js.work_packages.table_configuration.embedded_tab_disabled'), }; this.externalRelationQuery.show({ currentQuery: JSON.parse(group.query), - callback: (queryProps:any) => group.query = JSON.stringify(queryProps), - disabledTabs + callback: (queryProps:any) => (group.query = JSON.stringify(queryProps)), + disabledTabs, }); } @@ -181,15 +187,15 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen this.updateInactives(this.inactives.concat(group.attributes)); } - this.groups = this.groups.filter(el => el !== group); + this.groups = this.groups.filter((el) => el !== group); return group; } public createGroup(type:TypeGroupType, groupName = '') { const group:TypeGroup = { - type: type, - name: '', + type, + name: groupName, key: null, query: this.no_filter_query, attributes: [], @@ -205,8 +211,8 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen text: { title: this.I18n.t('js.types.attribute_groups.reset_title'), text: this.I18n.t('js.types.attribute_groups.confirm_reset'), - button_continue: this.I18n.t('js.label_reset') - } + button_continue: this.I18n.t('js.label_reset'), + }, }) .then(() => { this.form.find('input#type_attribute_groups').val(JSON.stringify([])); @@ -214,7 +220,8 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen // Disable our form handler that updates the attribute groups this.form.off('submit.typeformupdater'); this.form.trigger('submit'); - }); + }) + .catch(() => {}); $event.preventDefault(); return false; @@ -229,7 +236,9 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen // decides to remove all groups // This was necessary since the "default" is actually an empty array of groups private get emptyGroup():TypeGroup { - return { type: 'attribute', key: emptyTypeGroup, name: 'empty', attributes: [] }; + return { + type: 'attribute', key: emptyTypeGroup, name: 'empty', attributes: [], + }; } private updateHiddenFields() { @@ -243,4 +252,3 @@ export class TypeFormConfigurationComponent extends UntilDestroyedMixin implemen } } } - diff --git a/frontend/src/app/features/admin/types/type-form-configuration.html b/frontend/src/app/features/admin/types/type-form-configuration.html index 8c33d3e11f..ddf51da8e9 100644 --- a/frontend/src/app/features/admin/types/type-form-configuration.html +++ b/frontend/src/app/features/admin/types/type-form-configuration.html @@ -44,16 +44,16 @@
- - - + - +
diff --git a/frontend/src/app/features/api-docs/openproject-api-docs.module.ts b/frontend/src/app/features/api-docs/openproject-api-docs.module.ts index 9365791a1f..9e50ba220d 100644 --- a/frontend/src/app/features/api-docs/openproject-api-docs.module.ts +++ b/frontend/src/app/features/api-docs/openproject-api-docs.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,7 +27,7 @@ //++ import { NgModule } from '@angular/core'; -import { UIRouterModule } from "@uirouter/angular"; +import { UIRouterModule } from '@uirouter/angular'; import { SwaggerUIComponent } from './swagger-ui/swagger-ui.component'; import { API_DOCS_ROUTES } from './openproject-api-docs.routes'; @@ -35,13 +35,12 @@ import { API_DOCS_ROUTES } from './openproject-api-docs.routes'; imports: [ // Routes for /backlogs UIRouterModule.forChild({ - states: API_DOCS_ROUTES + states: API_DOCS_ROUTES, }), ], declarations: [ - SwaggerUIComponent - ] + SwaggerUIComponent, + ], }) export class OpenprojectApiDocsModule { } - diff --git a/frontend/src/app/features/api-docs/openproject-api-docs.routes.ts b/frontend/src/app/features/api-docs/openproject-api-docs.routes.ts index 805543661f..50d1cc8be9 100644 --- a/frontend/src/app/features/api-docs/openproject-api-docs.routes.ts +++ b/frontend/src/app/features/api-docs/openproject-api-docs.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration, UIRouter } from "@uirouter/angular"; -import { SwaggerUIComponent } from "./swagger-ui/swagger-ui.component"; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { SwaggerUIComponent } from './swagger-ui/swagger-ui.component'; export const API_DOCS_ROUTES:Ng2StateDeclaration[] = [ { name: 'api-docs', parent: 'root', url: '/api/docs', - component: SwaggerUIComponent - } + component: SwaggerUIComponent, + }, ]; diff --git a/frontend/src/app/features/api-docs/swagger-ui/swagger-ui.component.ts b/frontend/src/app/features/api-docs/swagger-ui/swagger-ui.component.ts index 6b0c0dd4fb..b89605a3cd 100644 --- a/frontend/src/app/features/api-docs/swagger-ui/swagger-ui.component.ts +++ b/frontend/src/app/features/api-docs/swagger-ui/swagger-ui.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,10 +34,10 @@ import * as SwaggerUI from 'swagger-ui'; selector: 'op-api-docs', styleUrls: ['./swagger-ui.component.sass'], templateUrl: './swagger-ui.component.html', - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, }) export class SwaggerUIComponent implements AfterViewInit { - constructor(private pathHelperService: PathHelperService) { + constructor(private pathHelperService:PathHelperService) { } ngAfterViewInit() { @@ -47,11 +47,11 @@ export class SwaggerUIComponent implements AfterViewInit { filter: true, requestInterceptor: (req) => { if (!req.loadSpec) { - // required to make session-based authentication work for POST requests with APIv3 - req.headers["X-Requested-With"] = "XMLHttpRequest"; + // required to make session-based authentication work for POST requests with APIv3 + req.headers['X-Requested-With'] = 'XMLHttpRequest'; } return req; - } + }, }); } } diff --git a/frontend/src/app/features/backlogs/backlogs-page/backlogs-page.component.ts b/frontend/src/app/features/backlogs/backlogs-page/backlogs-page.component.ts index 58b833698f..abd59ec3e7 100644 --- a/frontend/src/app/features/backlogs/backlogs-page/backlogs-page.component.ts +++ b/frontend/src/app/features/backlogs/backlogs-page/backlogs-page.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewEncapsulation } from "@angular/core"; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; export const backlogsPageComponentSelector = 'op-backlogs-page'; @@ -9,11 +9,11 @@ export const backlogsPageComponentSelector = 'op-backlogs-page'; encapsulation: ViewEncapsulation.None, template: '', styleUrls: [ - './styles/backlogs.sass' - ] + './styles/backlogs.sass', + ], }) export class BacklogsPageComponent implements OnInit { ngOnInit() { document.getElementById('projected-content')!.hidden = false; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/backlogs/openproject-backlogs.module.ts b/frontend/src/app/features/backlogs/openproject-backlogs.module.ts index 3e2bab6ab9..5fbc5e5f46 100644 --- a/frontend/src/app/features/backlogs/openproject-backlogs.module.ts +++ b/frontend/src/app/features/backlogs/openproject-backlogs.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,21 +27,20 @@ //++ import { NgModule } from '@angular/core'; -import { UIRouterModule } from "@uirouter/angular"; -import { BacklogsPageComponent } from "core-app/features/backlogs/backlogs-page/backlogs-page.component"; -import { BACKLOGS_ROUTES } from "core-app/features/backlogs/openproject-backlogs.routes"; +import { UIRouterModule } from '@uirouter/angular'; +import { BacklogsPageComponent } from 'core-app/features/backlogs/backlogs-page/backlogs-page.component'; +import { BACKLOGS_ROUTES } from 'core-app/features/backlogs/openproject-backlogs.routes'; @NgModule({ imports: [ // Routes for /backlogs UIRouterModule.forChild({ - states: BACKLOGS_ROUTES + states: BACKLOGS_ROUTES, }), ], declarations: [ - BacklogsPageComponent - ] + BacklogsPageComponent, + ], }) export class OpenprojectBacklogsModule { } - diff --git a/frontend/src/app/features/backlogs/openproject-backlogs.routes.ts b/frontend/src/app/features/backlogs/openproject-backlogs.routes.ts index abb3cc9bc0..4e0fc06e11 100644 --- a/frontend/src/app/features/backlogs/openproject-backlogs.routes.ts +++ b/frontend/src/app/features/backlogs/openproject-backlogs.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration, UIRouter } from "@uirouter/angular"; -import { BacklogsPageComponent } from "core-app/features/backlogs/backlogs-page/backlogs-page.component"; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { BacklogsPageComponent } from 'core-app/features/backlogs/backlogs-page/backlogs-page.component'; export const BACKLOGS_ROUTES:Ng2StateDeclaration[] = [ { name: 'backlogs', parent: 'root', url: '/backlogs', - component: BacklogsPageComponent + component: BacklogsPageComponent, }, { name: 'backlogs_sprint', parent: 'root', url: '/sprints/{sprintId:int}/taskboard', - component: BacklogsPageComponent + component: BacklogsPageComponent, }, { name: 'backlogs_burndown', parent: 'root', url: '/sprints/{sprintId:int}/burndown_chart', - component: BacklogsPageComponent + component: BacklogsPageComponent, }, ]; diff --git a/frontend/src/app/features/bim/bcf/api/bcf-api-request.service.ts b/frontend/src/app/features/bim/bcf/api/bcf-api-request.service.ts index 7bf8cebf27..81084f69fa 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-api-request.service.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-api-request.service.ts @@ -1,17 +1,17 @@ -import { HttpClient, HttpErrorResponse, HttpParams } from "@angular/common/http"; -import { Injector } from "@angular/core"; -import { TypedJSON } from "typedjson"; -import { Constructor } from "@angular/cdk/table"; -import { Observable, throwError } from "rxjs"; +import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; +import { Injector } from '@angular/core'; +import { TypedJSON } from 'typedjson'; +import { Constructor } from '@angular/cdk/table'; +import { Observable, throwError } from 'rxjs'; import { HTTPClientHeaders, HTTPClientOptions, HTTPClientParamMap, - HTTPSupportedMethods -} from "core-app/features/hal/http/http.interfaces"; -import { catchError, map } from "rxjs/operators"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { URLParamsEncoder } from "core-app/features/hal/services/url-params-encoder"; + HTTPSupportedMethods, +} from 'core-app/features/hal/http/http.interfaces'; +import { catchError, map } from 'rxjs/operators'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { URLParamsEncoder } from 'core-app/features/hal/services/url-params-encoder'; export class BcfApiRequestService { @InjectField() http:HttpClient; @@ -24,7 +24,7 @@ export class BcfApiRequestService { * @param resourceClass Optional mapped resource class with TypedJson annotations */ constructor(readonly injector:Injector, - readonly resourceClass?:Constructor) { + readonly resourceClass?:Constructor) { } /** @@ -36,10 +36,10 @@ export class BcfApiRequestService { */ get(path:string, params:HTTPClientParamMap, headers:HTTPClientHeaders = {}):Observable { const config:HTTPClientOptions = { - headers: headers, + headers, params: new HttpParams({ encoder: new URLParamsEncoder(), fromObject: params }), withCredentials: true, - responseType: 'json' + responseType: 'json', }; return this._request('get', path, config); @@ -54,7 +54,6 @@ export class BcfApiRequestService { * @param data Request payload (URL params for get, JSON payload otherwise) */ public request(method:HTTPSupportedMethods, path:string, data:HTTPClientParamMap = {}, headers:HTTPClientHeaders = {}):Observable { - // HttpClient requires us to create HttpParams instead of passing data for get // so forward to that method instead. if (method === 'get') { @@ -63,9 +62,9 @@ export class BcfApiRequestService { const config:HTTPClientOptions = { body: data || {}, - headers: headers, + headers, withCredentials: true, - responseType: 'json' + responseType: 'json', }; return this._request(method, path, config); @@ -89,7 +88,7 @@ export class BcfApiRequestService { catchError((error:HttpErrorResponse) => { console.error(`Failed to ${method} ${path}: ${error.name}`); return throwError(error); - }) + }), ); } @@ -101,8 +100,7 @@ export class BcfApiRequestService { if (this.resourceClass) { const serializer = new TypedJSON(this.resourceClass); return serializer.parse(data)!; - } else { - return data; } + return data; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts b/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts index c310e5427e..d804917e19 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TestBed, waitForAsync } from "@angular/core/testing"; -import { BcfApiService } from "core-app/features/bim/bcf/api/bcf-api.service"; -import { BcfResourceCollectionPath, BcfResourcePath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfTopicPaths } from "core-app/features/bim/bcf/api/topics/bcf-topic.paths"; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; +import { BcfResourceCollectionPath, BcfResourcePath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfTopicPaths } from 'core-app/features/bim/bcf/api/topics/bcf-topic.paths'; -describe('BcfApiService', function () { +describe('BcfApiService', () => { let service:BcfApiService; beforeEach(waitForAsync(() => { @@ -39,7 +39,7 @@ describe('BcfApiService', function () { TestBed.configureTestingModule({ providers: [ BcfApiService, - ] + ], }) .compileComponents() .then(() => { @@ -129,6 +129,5 @@ describe('BcfApiService', function () { expect(subject.id).toEqual('dfca6c25-832f-6a94-53ca-48d510b6bad9'); expect(subject.toPath()).toEqual(href); }); - }); }); diff --git a/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts b/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts index 168a7ef531..48b9972ceb 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-api.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable, Injector } from "@angular/core"; -import { BcfResourceCollectionPath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfProjectPaths } from "core-app/features/bim/bcf/api/projects/bcf-project.paths"; - +import { Injectable, Injector } from '@angular/core'; +import { BcfResourceCollectionPath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfProjectPaths } from 'core-app/features/bim/bcf/api/projects/bcf-project.paths'; @Injectable({ providedIn: 'root' }) export class BcfApiService { - public readonly bcfApiVersion = '2.1'; + public readonly appBasePath = window.appBasePath || ''; + public readonly bcfApiBase = `${this.appBasePath}/api/bcf/${this.bcfApiVersion}`; // /api/bcf/:version/projects @@ -55,7 +55,7 @@ export class BcfApiService { } const parts = href - .replace(this.bcfApiBase + '/', '') + .replace(`${this.bcfApiBase}/`, '') .split('/'); // Try to find a target collection or resource diff --git a/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts b/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts index e694345416..caf46e2fd5 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-authorization.service.ts @@ -1,15 +1,14 @@ -import { multiInput } from "reactivestates"; -import { BcfExtensionResource } from "core-app/features/bim/bcf/api/extensions/bcf-extension.resource"; -import { BcfApiService } from "core-app/features/bim/bcf/api/bcf-api.service"; -import { Observable } from "rxjs"; -import { map, take } from "rxjs/operators"; -import { Injectable } from "@angular/core"; +import { multiInput } from 'reactivestates'; +import { BcfExtensionResource } from 'core-app/features/bim/bcf/api/extensions/bcf-extension.resource'; +import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; +import { Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; export type AllowedExtensionKey = keyof BcfExtensionResource; @Injectable({ providedIn: 'root' }) export class BcfAuthorizationService { - // Poor mans caching to avoid repeatedly fetching from the backend. protected authorizationMap = multiInput(); @@ -29,20 +28,18 @@ export class BcfAuthorizationService { public authorized$(projectIdentifier:string, extension:AllowedExtensionKey, action:string):Observable { const state = this.authorizationMap.get(projectIdentifier); - state.putFromPromiseIfPristine(() => - this.bcfApi - .projects.id(projectIdentifier) - .extensions - .get() - .toPromise() - ); + state.putFromPromiseIfPristine(() => this.bcfApi + .projects.id(projectIdentifier) + .extensions + .get() + .toPromise()); return state .values$() .pipe( map( - resource => resource[extension] && resource[extension].includes(action) - ) + (resource) => resource[extension] && resource[extension].includes(action), + ), ); } @@ -57,10 +54,9 @@ export class BcfAuthorizationService { return this .authorized$(projectIdentifier, extension, action) .pipe( - take(1) + take(1), ) .toPromise() .catch(() => false); } } - diff --git a/frontend/src/app/features/bim/bcf/api/bcf-path-resources.ts b/frontend/src/app/features/bim/bcf/api/bcf-path-resources.ts index f9f7e764e6..ea009f26e3 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-path-resources.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-path-resources.ts @@ -1,25 +1,24 @@ -import { Injector } from "@angular/core"; -import { Constructor } from "@angular/cdk/table"; -import { SimpleResource, SimpleResourceCollection } from "core-app/core/apiv3/paths/path-resources"; +import { Injector } from '@angular/core'; +import { Constructor } from '@angular/cdk/table'; +import { SimpleResource, SimpleResourceCollection } from 'core-app/core/apiv3/paths/path-resources'; export class BcfResourcePath extends SimpleResource { constructor(readonly injector:Injector, basePath:string, - readonly id:string|number) { + readonly id:string|number) { super(basePath, id); } } export class BcfResourceCollectionPath extends SimpleResourceCollection { constructor(readonly injector:Injector, - protected basePath:string, - segment:string, - protected resource?:Constructor) { + protected basePath:string, + segment:string, + protected resource?:Constructor) { super(basePath, segment, resource); } public id(id:string|number):T { return new (this.resource || BcfResourcePath)(this.injector, this.path, id) as T; } - -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.paths.ts b/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.paths.ts index 7f1c7b3b0d..f13f664b73 100644 --- a/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.paths.ts +++ b/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.paths.ts @@ -1,7 +1,7 @@ -import { BcfResourcePath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfApiRequestService } from "core-app/features/bim/bcf/api/bcf-api-request.service"; -import { HTTPClientHeaders, HTTPClientParamMap } from "core-app/features/hal/http/http.interfaces"; -import { BcfExtensionResource } from "core-app/features/bim/bcf/api/extensions/bcf-extension.resource"; +import { BcfResourcePath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfApiRequestService } from 'core-app/features/bim/bcf/api/bcf-api-request.service'; +import { HTTPClientHeaders, HTTPClientParamMap } from 'core-app/features/hal/http/http.interfaces'; +import { BcfExtensionResource } from 'core-app/features/bim/bcf/api/extensions/bcf-extension.resource'; export class BcfExtensionPaths extends BcfResourcePath { readonly bcfExtensionService = new BcfApiRequestService(this.injector, BcfExtensionResource); diff --git a/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.resource.ts b/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.resource.ts index c22a4b0289..adde2f8e06 100644 --- a/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.resource.ts +++ b/frontend/src/app/features/bim/bcf/api/extensions/bcf-extension.resource.ts @@ -1,8 +1,7 @@ -import { jsonArrayMember, jsonObject } from "typedjson"; +import { jsonArrayMember, jsonObject } from 'typedjson'; @jsonObject export class BcfExtensionResource { - @jsonArrayMember(String) topic_actions:string[]; diff --git a/frontend/src/app/features/bim/bcf/api/projects/bcf-project.paths.ts b/frontend/src/app/features/bim/bcf/api/projects/bcf-project.paths.ts index 397ee20f46..c051a6736a 100644 --- a/frontend/src/app/features/bim/bcf/api/projects/bcf-project.paths.ts +++ b/frontend/src/app/features/bim/bcf/api/projects/bcf-project.paths.ts @@ -1,9 +1,9 @@ -import { BcfResourcePath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfApiRequestService } from "core-app/features/bim/bcf/api/bcf-api-request.service"; -import { BcfProjectResource } from "core-app/features/bim/bcf/api/projects/bcf-project.resource"; -import { HTTPClientHeaders, HTTPClientParamMap } from "core-app/features/hal/http/http.interfaces"; -import { BcfTopicCollectionPath } from "core-app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths"; -import { BcfExtensionPaths } from "core-app/features/bim/bcf/api/extensions/bcf-extension.paths"; +import { BcfResourcePath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfApiRequestService } from 'core-app/features/bim/bcf/api/bcf-api-request.service'; +import { BcfProjectResource } from 'core-app/features/bim/bcf/api/projects/bcf-project.resource'; +import { HTTPClientHeaders, HTTPClientParamMap } from 'core-app/features/hal/http/http.interfaces'; +import { BcfTopicCollectionPath } from 'core-app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths'; +import { BcfExtensionPaths } from 'core-app/features/bim/bcf/api/extensions/bcf-extension.paths'; export class BcfProjectPaths extends BcfResourcePath { readonly bcfProjectService = new BcfApiRequestService(this.injector, BcfProjectResource); @@ -16,4 +16,4 @@ export class BcfProjectPaths extends BcfResourcePath { get(params:HTTPClientParamMap = {}, headers:HTTPClientHeaders = {}) { return this.bcfProjectService.get(this.toPath(), params, headers); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/api/projects/bcf-project.resource.ts b/frontend/src/app/features/bim/bcf/api/projects/bcf-project.resource.ts index 9805eb4c68..0863d49e65 100644 --- a/frontend/src/app/features/bim/bcf/api/projects/bcf-project.resource.ts +++ b/frontend/src/app/features/bim/bcf/api/projects/bcf-project.resource.ts @@ -1,8 +1,7 @@ -import { jsonMember, jsonObject } from "typedjson"; +import { jsonMember, jsonObject } from 'typedjson'; @jsonObject export class BcfProjectResource { - @jsonMember project_id:number; diff --git a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.paths.ts b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.paths.ts index c096c90472..2cc1b01ce6 100644 --- a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.paths.ts +++ b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.paths.ts @@ -1,9 +1,9 @@ -import { BcfResourceCollectionPath, BcfResourcePath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfTopicResource } from "core-app/features/bim/bcf/api/topics/bcf-topic.resource"; -import { BcfApiRequestService } from "core-app/features/bim/bcf/api/bcf-api-request.service"; -import { BcfViewpointPaths } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.paths"; -import { BcfViewpointCollectionPath } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-collection.paths"; -import { HTTPClientHeaders, HTTPClientParamMap } from "core-app/features/hal/http/http.interfaces"; +import { BcfResourceCollectionPath, BcfResourcePath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfTopicResource } from 'core-app/features/bim/bcf/api/topics/bcf-topic.resource'; +import { BcfApiRequestService } from 'core-app/features/bim/bcf/api/bcf-api-request.service'; +import { BcfViewpointPaths } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.paths'; +import { BcfViewpointCollectionPath } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-collection.paths'; +import { HTTPClientHeaders, HTTPClientParamMap } from 'core-app/features/hal/http/http.interfaces'; export class BcfTopicPaths extends BcfResourcePath { readonly bcfTopicService = new BcfApiRequestService(this.injector, BcfTopicResource); @@ -17,4 +17,4 @@ export class BcfTopicPaths extends BcfResourcePath { get(params:HTTPClientParamMap = {}, headers:HTTPClientHeaders = {}) { return this.bcfTopicService.get(this.toPath(), params, headers); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.spec.ts b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.spec.ts index 8331313e88..fcdba37f04 100644 --- a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.spec.ts +++ b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.spec.ts @@ -1,28 +1,28 @@ -import { TypedJSON } from "typedjson"; -import { BcfTopicResource } from "core-app/features/bim/bcf/api/topics/bcf-topic.resource"; +import { TypedJSON } from 'typedjson'; +import { BcfTopicResource } from 'core-app/features/bim/bcf/api/topics/bcf-topic.resource'; import * as moment from 'moment'; export const topic_object = { - "guid": "00efc0da-b4d5-4933-bcb6-e01513ee2bcc", - "topic_type": "Clash", - "topic_status": "New", - "priority": "Normal", - "reference_links": ["/api/v3/work_packages/52"], - "title": "Clash between wall and facade", - "index": null, - "labels": [], - "creation_date": "2020-02-25T15:09:15.000Z", - "creation_author": "admin@example.net", - "modified_date": "2020-02-25T15:09:15.000Z", - "modified_author": null, - "assigned_to": "", - "stage": null, - "description": "Clash between wall and facade", - "due_date": "2020-05-16", - "authorization": { - "topic_actions": ["update", "updateRelatedTopics", "updateFiles", "createViewpoint"], - "topic_status": ["New", "In progress", "Resolved", "Closed"] - } + guid: '00efc0da-b4d5-4933-bcb6-e01513ee2bcc', + topic_type: 'Clash', + topic_status: 'New', + priority: 'Normal', + reference_links: ['/api/v3/work_packages/52'], + title: 'Clash between wall and facade', + index: null, + labels: [], + creation_date: '2020-02-25T15:09:15.000Z', + creation_author: 'admin@example.net', + modified_date: '2020-02-25T15:09:15.000Z', + modified_author: null, + assigned_to: '', + stage: null, + description: 'Clash between wall and facade', + due_date: '2020-05-16', + authorization: { + topic_actions: ['update', 'updateRelatedTopics', 'updateFiles', 'createViewpoint'], + topic_status: ['New', 'In progress', 'Resolved', 'Closed'], + }, }; describe('BcfTopicResource', () => { @@ -43,4 +43,3 @@ describe('BcfTopicResource', () => { expect(serializer.toPlainJson(subject)).toEqual(topic_object); }); }); - diff --git a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.ts b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.ts index fe0b179e6c..5234ddf2d8 100644 --- a/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.ts +++ b/frontend/src/app/features/bim/bcf/api/topics/bcf-topic.resource.ts @@ -1,6 +1,6 @@ -import { jsonArrayMember, jsonMember, jsonObject } from "typedjson"; -import * as moment from "moment"; -import { Moment } from "moment"; +import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; +import * as moment from 'moment'; +import { Moment } from 'moment'; @jsonObject export class BcfTopicAuthorizationMap { @@ -13,7 +13,6 @@ export class BcfTopicAuthorizationMap { @jsonObject export class BcfTopicResource { - @jsonMember guid:string; @@ -38,13 +37,13 @@ export class BcfTopicResource { @jsonArrayMember(String) labels:string[]; - @jsonMember({ deserializer: value => moment(value), serializer: (timestamp:Moment) => timestamp.toISOString() }) + @jsonMember({ deserializer: (value) => moment(value), serializer: (timestamp:Moment) => timestamp.toISOString() }) creation_date:Moment; @jsonMember creation_author:string; - @jsonMember({ deserializer: value => moment(value), serializer: (timestamp:Moment) => timestamp.toISOString() }) + @jsonMember({ deserializer: (value) => moment(value), serializer: (timestamp:Moment) => timestamp.toISOString() }) modified_date:Moment; @jsonMember({ preserveNull: true }) @@ -60,8 +59,8 @@ export class BcfTopicResource { description:string; @jsonMember({ - deserializer: value => moment(value), - serializer: (timestamp:Moment) => timestamp.format('YYYY-MM-DD') + deserializer: (value) => moment(value), + serializer: (timestamp:Moment) => timestamp.format('YYYY-MM-DD'), }) due_date:Moment; diff --git a/frontend/src/app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths.ts b/frontend/src/app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths.ts index cb003d196a..38a0ff5caa 100644 --- a/frontend/src/app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths.ts +++ b/frontend/src/app/features/bim/bcf/api/topics/bcf-viewpoint-collection.paths.ts @@ -1,22 +1,22 @@ -import { BcfResourceCollectionPath } from "core-app/features/bim/bcf/api/bcf-path-resources"; -import { BcfApiRequestService } from "core-app/features/bim/bcf/api/bcf-api-request.service"; -import { HTTPClientHeaders, HTTPClientParamMap } from "core-app/features/hal/http/http.interfaces"; -import { Observable } from "rxjs"; -import { BcfTopicPaths } from "core-app/features/bim/bcf/api/topics/bcf-topic.paths"; -import { Injector } from "@angular/core"; -import { BcfTopicResource } from "core-app/features/bim/bcf/api/topics/bcf-topic.resource"; +import { BcfResourceCollectionPath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; +import { BcfApiRequestService } from 'core-app/features/bim/bcf/api/bcf-api-request.service'; +import { HTTPClientHeaders, HTTPClientParamMap } from 'core-app/features/hal/http/http.interfaces'; +import { Observable } from 'rxjs'; +import { BcfTopicPaths } from 'core-app/features/bim/bcf/api/topics/bcf-topic.paths'; +import { Injector } from '@angular/core'; +import { BcfTopicResource } from 'core-app/features/bim/bcf/api/topics/bcf-topic.resource'; export class BcfTopicCollectionPath extends BcfResourceCollectionPath { readonly bcfTopicService = new BcfApiRequestService(this.injector, BcfTopicResource); constructor(readonly injector:Injector, - protected basePath:string, - segment:string) { + protected basePath:string, + segment:string) { super(injector, basePath, segment, BcfTopicPaths); } get(params:HTTPClientParamMap = {}, headers:HTTPClientHeaders = {}) { - throw new Error("Not implemented"); + throw new Error('Not implemented'); } /** @@ -28,7 +28,7 @@ export class BcfTopicCollectionPath extends BcfResourceCollectionPath { readonly bcfTopicService = new BcfApiRequestService(this.injector); get(params:HTTPClientParamMap = {}, headers:HTTPClientHeaders = {}) { - throw new Error("Not implemented"); + throw new Error('Not implemented'); } post(viewpoint:BcfViewpointInterface):Observable { @@ -18,7 +18,7 @@ export class BcfViewpointCollectionPath extends BcfResourceCollectionPath(this.injector); @@ -13,4 +13,4 @@ export class BcfViewpointPaths extends BcfResourcePath { delete(headers:HTTPClientHeaders = {}) { return this.bcfTopicService.request('delete', this.toPath(), {}, headers); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/bcf-constants.const.ts b/frontend/src/app/features/bim/bcf/bcf-constants.const.ts index cf0a0c8f46..53a68bc756 100644 --- a/frontend/src/app/features/bim/bcf/bcf-constants.const.ts +++ b/frontend/src/app/features/bim/bcf/bcf-constants.const.ts @@ -1 +1 @@ -export const BcfRestApi = "https://github.com/opf/openproject/blob/dev/docs/api/bcf/bcf-rest-api.md"; +export const BcfRestApi = 'https://github.com/opf/openproject/blob/dev/docs/api/bcf/bcf-rest-api.md'; diff --git a/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts b/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts index d96edbdde8..4e802cf4ee 100644 --- a/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts +++ b/frontend/src/app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service.ts @@ -1,10 +1,9 @@ -import { Injector, Injectable } from '@angular/core'; -import { BcfViewpointInterface } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface"; -import { Observable } from "rxjs"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { StateService } from "@uirouter/core"; - +import { Injectable, Injector } from '@angular/core'; +import { BcfViewpointInterface } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface'; +import { Observable } from 'rxjs'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { StateService } from '@uirouter/core'; @Injectable() export abstract class ViewerBridgeService { @@ -25,6 +24,7 @@ export abstract class ViewerBridgeService { } constructor(readonly injector:Injector) {} + /** * Get a viewpoint from the viewer */ diff --git a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component.ts b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component.ts index 336f98d92a..c6302ece10 100644 --- a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component.ts +++ b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component.ts @@ -1,11 +1,10 @@ -import { ChangeDetectionStrategy, Component } from "@angular/core"; -import { BcfWpAttributeGroupComponent } from "core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component"; -import { take, switchMap } from "rxjs/operators"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { forkJoin } from "rxjs"; -import { BcfViewpointInterface } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface"; -import { BcfViewpointItem } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-item.interface"; - +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { BcfWpAttributeGroupComponent } from 'core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component'; +import { switchMap, take } from 'rxjs/operators'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { forkJoin } from 'rxjs'; +import { BcfViewpointInterface } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface'; +import { BcfViewpointItem } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-item.interface'; @Component({ templateUrl: './bcf-wp-attribute-group.component.html', @@ -40,11 +39,11 @@ export class BcfNewWpAttributeGroupComponent extends BcfWpAttributeGroupComponen switchMap((wp:WorkPackageResource) => { this.workPackage = wp; const observables = this.galleryViewpoints - .filter(viewPointItem => !viewPointItem.href && viewPointItem.viewpoint) - .map(viewPointItem => this.viewpointsService.saveViewpoint$(this.workPackage, viewPointItem.viewpoint)); + .filter((viewPointItem) => !viewPointItem.href && viewPointItem.viewpoint) + .map((viewPointItem) => this.viewpointsService.saveViewpoint$(this.workPackage, viewPointItem.viewpoint)); return forkJoin(observables); - }) + }), ) .subscribe((viewpoints:BcfViewpointInterface[]) => { this.showIndex = this.galleryViewpoints.length - 1; @@ -53,29 +52,27 @@ export class BcfNewWpAttributeGroupComponent extends BcfWpAttributeGroupComponen // Disable show viewpoint functionality showViewpoint(workPackage:WorkPackageResource, index:number) { - return; + } deleteViewpoint(workPackage:WorkPackageResource, index:number) { this.galleryViewpoints = this.galleryViewpoints.filter((_, i) => i !== index); this.setViewpointsOnGallery(this.galleryViewpoints); - - return; } saveViewpoint() { this.viewerBridge .getViewpoint$() - .subscribe(viewpoint => { + .subscribe((viewpoint) => { const newViewpoint = { snapshotURL: viewpoint.snapshot.snapshot_data, - viewpoint: viewpoint + viewpoint, }; this.galleryViewpoints = [ ...this.galleryViewpoints, - newViewpoint + newViewpoint, ]; this.setViewpointsOnGallery(this.galleryViewpoints); @@ -89,10 +86,11 @@ export class BcfNewWpAttributeGroupComponent extends BcfWpAttributeGroupComponen shouldShowGroup() { return this.createAllowed && this.viewerVisible; } + protected actions() { // Show only delete button return super .actions() - .filter(el => el.icon === 'icon-delete'); + .filter((el) => el.icon === 'icon-delete'); } } diff --git a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts index 8d628629fd..d3258a9e98 100644 --- a/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts +++ b/frontend/src/app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component.ts @@ -6,31 +6,31 @@ import { Input, OnDestroy, OnInit, - ViewChild -} from "@angular/core"; -import { StateService } from "@uirouter/core"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; + ViewChild, +} from '@angular/core'; +import { StateService } from '@uirouter/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { NgxGalleryComponent, NgxGalleryOptions } from '@kolkov/ngx-gallery'; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { BcfAuthorizationService } from "core-app/features/bim/bcf/api/bcf-authorization.service"; -import { ViewpointsService } from "core-app/features/bim/bcf/helper/viewpoints.service"; -import { BcfViewpointItem } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-item.interface"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; - +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { BcfAuthorizationService } from 'core-app/features/bim/bcf/api/bcf-authorization.service'; +import { ViewpointsService } from 'core-app/features/bim/bcf/helper/viewpoints.service'; +import { BcfViewpointItem } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint-item.interface'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './bcf-wp-attribute-group.component.html', styleUrls: ['./bcf-wp-attribute-group.component.sass'], changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ViewpointsService] + providers: [ViewpointsService], }) export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements AfterViewInit, OnDestroy, OnInit { @Input() workPackage:WorkPackageResource; + @ViewChild(NgxGalleryComponent) gallery:NgxGalleryComponent; text = { @@ -86,7 +86,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements imagePercent: 80, thumbnailsPercent: 20, thumbnailsMargin: 5, - thumbnailMargin: 5 + thumbnailMargin: 5, }, // max-width 680 { @@ -95,7 +95,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements thumbnailsColumns: 3, thumbnailsMargin: 5, thumbnailMargin: 5, - } + }, ]; viewpoints:BcfViewpointItem[] = []; @@ -104,21 +104,24 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements // Store whether viewing is allowed viewAllowed = false; + // Store whether viewpoint creation is allowed createAllowed = false; + // Currently, this is static. Need observable if this changes over time viewerVisible = false; + projectId:string; constructor(readonly state:StateService, - readonly bcfAuthorization:BcfAuthorizationService, - readonly viewerBridge:ViewerBridgeService, - readonly apiV3Service:APIV3Service, - readonly wpCreate:WorkPackageCreateService, - readonly notifications:NotificationsService, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly viewpointsService:ViewpointsService) { + readonly bcfAuthorization:BcfAuthorizationService, + readonly viewerBridge:ViewerBridgeService, + readonly apiV3Service:APIV3Service, + readonly wpCreate:WorkPackageCreateService, + readonly notifications:NotificationsService, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly viewpointsService:ViewpointsService) { super(); } @@ -145,7 +148,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements .id(this.workPackage) .requireAndStream() .pipe(this.untilDestroyed()) - .subscribe(async wp => { + .subscribe(async (wp) => { this.workPackage = wp; if (!this.projectId) { @@ -184,7 +187,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements this.viewpointsService .deleteViewPoint$(workPackage, index) - .subscribe(data => { + .subscribe((data) => { this.notifications.addSuccess(this.text.notice_successful_delete); this.gallery.preview.close(); }); @@ -193,7 +196,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements public saveViewpoint(workPackage:WorkPackageResource) { this.viewpointsService .saveViewpoint$(workPackage) - .subscribe(viewpoint => { + .subscribe((viewpoint) => { this.notifications.addSuccess(this.text.notice_successful_create); this.showIndex = this.viewpoints.length; }); @@ -210,9 +213,9 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements } public shouldShowGroup() { - return this.viewAllowed && - (this.viewpoints.length > 0 || - (this.createAllowed && this.viewerVisible)); + return this.viewAllowed + && (this.viewpoints.length > 0 + || (this.createAllowed && this.viewerVisible)); } // Gallery functionality @@ -224,13 +227,13 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements this.showViewpoint(this.workPackage, index); this.gallery.preview.close(); }, - titleText: this.text.show_viewpoint + titleText: this.text.show_viewpoint, }, { icon: 'icon-delete', onClick: (evt:any, index:number) => this.deleteViewpoint(this.workPackage, index), - titleText: this.text.delete_viewpoint - } + titleText: this.text.delete_viewpoint, + }, ]; } @@ -261,7 +264,7 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements } protected setViewpointsOnGallery(viewpoints:BcfViewpointItem[]) { - const length = viewpoints.length; + const { length } = viewpoints; this.setThumbnailProperties(length); @@ -271,13 +274,11 @@ export class BcfWpAttributeGroupComponent extends UntilDestroyedMixin implements this.showIndex = length - 1; } - this.galleryImages = viewpoints.map(viewpoint => { - return { - small: viewpoint.snapshotURL, - medium: viewpoint.snapshotURL, - big: viewpoint.snapshotURL - }; - }); + this.galleryImages = viewpoints.map((viewpoint) => ({ + small: viewpoint.snapshotURL, + medium: viewpoint.snapshotURL, + big: viewpoint.snapshotURL, + })); this.cdRef.detectChanges(); } diff --git a/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts b/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts index e0736a1083..a98a64a66f 100644 --- a/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts +++ b/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { BcfPathHelperService } from "core-app/features/bim/bcf/helper/bcf-path-helper.service"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; export class BcfThumbnailDisplayField extends DisplayField { @InjectField() bcfPathHelper:BcfPathHelperService; diff --git a/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts b/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts index 6da9e849a1..e8705e766b 100644 --- a/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/bcf-detector.service.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@angular/core'; -import { DOCUMENT } from "@angular/common"; +import { DOCUMENT } from '@angular/common'; @Injectable() export class BcfDetectorService { - constructor (@Inject(DOCUMENT) private documentElement:Document) { + constructor(@Inject(DOCUMENT) private documentElement:Document) { } /** diff --git a/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts b/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts index ae7243c197..5cc501e3b0 100644 --- a/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/bcf-path-helper.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,8 @@ //++ import { Injectable } from '@angular/core'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; @Injectable() export class BcfPathHelperService { @@ -36,18 +36,17 @@ export class BcfPathHelperService { } public projectImportIssuePath(projectIdentifier:string) { - return this.pathHelper.projectPath(projectIdentifier) + '/issues/upload'; + return `${this.pathHelper.projectPath(projectIdentifier)}/issues/upload`; } public projectExportIssuesPath(projectIdentifier:string, filters:string|null) { if (filters) { - return this.pathHelper.projectPath(projectIdentifier) + '/work_packages.bcf?filters=' + filters; - } else { - return this.pathHelper.projectPath(projectIdentifier) + '/work_packages.bcf'; + return `${this.pathHelper.projectPath(projectIdentifier)}/work_packages.bcf?filters=${filters}`; } + return `${this.pathHelper.projectPath(projectIdentifier)}/work_packages.bcf`; } public snapshotPath(viewpoint:HalLink) { - return viewpoint.href + '/snapshot'; + return `${viewpoint.href}/snapshot`; } } diff --git a/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts b/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts index b0b1475777..2a6a44e73c 100644 --- a/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts +++ b/frontend/src/app/features/bim/bcf/helper/viewpoints.service.ts @@ -1,22 +1,23 @@ import { Injectable, Injector } from '@angular/core'; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { BcfApiService } from "core-app/features/bim/bcf/api/bcf-api.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { BcfViewpointPaths } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.paths"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { switchMap, map, tap } from 'rxjs/operators'; -import { of, forkJoin, Observable } from 'rxjs'; -import { BcfViewpointInterface } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface"; -import { BcfTopicResource } from "core-app/features/bim/bcf/api/topics/bcf-topic.resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; - +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { BcfViewpointPaths } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.paths'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { map, switchMap, tap } from 'rxjs/operators'; +import { forkJoin, Observable, of } from 'rxjs'; +import { BcfViewpointInterface } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface'; +import { BcfTopicResource } from 'core-app/features/bim/bcf/api/topics/bcf-topic.resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable() export class ViewpointsService { topicUUID:string|number; @InjectField() bcfApi:BcfApiService; + @InjectField() viewerBridge:ViewerBridgeService; + @InjectField() apiV3Service:APIV3Service; constructor(readonly injector:Injector) {} @@ -40,7 +41,7 @@ export class ViewpointsService { .delete() .pipe( // Update the work package to reload the viewpoints - tap(() => this.apiV3Service.work_packages.id(workPackage).requireAndStream(true)) + tap(() => this.apiV3Service.work_packages.id(workPackage).requireAndStream(true)), ); } @@ -48,40 +49,35 @@ export class ViewpointsService { const wpProjectId = workPackage.project.idFromLink; const topicUUID$ = this.setBcfTopic$(workPackage); // Default to the current viewer's viewpoint - const viewpoint$ = viewpoint ? - of(viewpoint) : - this.viewerBridge!.getViewpoint$(); + const viewpoint$ = viewpoint + ? of(viewpoint) + : this.viewerBridge.getViewpoint$(); return forkJoin({ topicUUID: topicUUID$, viewpoint: viewpoint$, }) .pipe( - switchMap(results => { - return this.bcfApi - .projects.id(wpProjectId) - .topics.id(results.topicUUID as (string | number)) - .viewpoints - .post(results.viewpoint); - } - ), + switchMap((results) => this.bcfApi + .projects.id(wpProjectId) + .topics.id(results.topicUUID) + .viewpoints + .post(results.viewpoint)), // Update the work package to reload the viewpoints - tap((results) => - this.apiV3Service.work_packages.id(workPackage).requireAndStream(true)) + tap((results) => this.apiV3Service.work_packages.id(workPackage).requireAndStream(true)), ); } public setBcfTopic$(workPackage:WorkPackageResource) { if (this.topicUUID) { return of(this.topicUUID); - } else { - const topicHref = workPackage.bcfTopic?.href; - const topicUUID$ = topicHref ? - of(this.bcfApi.parse(topicHref)!.id) : - this.createBcfTopic$(workPackage); - - return topicUUID$.pipe(map(topicUUID => this.topicUUID = topicUUID)); } + const topicHref = workPackage.bcfTopic?.href; + const topicUUID$ = topicHref + ? of(this.bcfApi.parse(topicHref)!.id) + : this.createBcfTopic$(workPackage); + + return topicUUID$.pipe(map((topicUUID) => this.topicUUID = topicUUID)); } private createBcfTopic$(workPackage:WorkPackageResource):Observable { @@ -96,7 +92,7 @@ export class ViewpointsService { map((resource:BcfTopicResource) => { this.topicUUID = resource.guid; return this.topicUUID; - }) + }), ); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts b/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts index 211b01515c..c685e896aa 100644 --- a/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts +++ b/frontend/src/app/features/bim/bcf/openproject-bcf.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,24 +27,24 @@ //++ import { Injector, NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { NgxGalleryModule } from "@kolkov/ngx-gallery"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { BcfThumbnailDisplayField } from "core-app/features/bim/bcf/fields/display/bcf-thumbnail-field.module"; -import { HTTP_INTERCEPTORS } from "@angular/common/http"; -import { BcfDetectorService } from "core-app/features/bim/bcf/helper/bcf-detector.service"; -import { BcfPathHelperService } from "core-app/features/bim/bcf/helper/bcf-path-helper.service"; -import { ViewpointsService } from "core-app/features/bim/bcf/helper/viewpoints.service"; -import { BcfImportButtonComponent } from "core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component"; -import { BcfExportButtonComponent } from "core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component"; -import { IFCViewerService } from "core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { HookService } from "core-app/features/plugins/hook-service"; -import { BcfWpAttributeGroupComponent } from "core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component"; -import { BcfNewWpAttributeGroupComponent } from "core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component"; -import { RevitBridgeService } from "core-app/features/bim/revit_add_in/revit-bridge.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { OpenProjectHeaderInterceptor } from "core-app/features/hal/http/openproject-header-interceptor"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { NgxGalleryModule } from '@kolkov/ngx-gallery'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { BcfThumbnailDisplayField } from 'core-app/features/bim/bcf/fields/display/bcf-thumbnail-field.module'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { BcfDetectorService } from 'core-app/features/bim/bcf/helper/bcf-detector.service'; +import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; +import { ViewpointsService } from 'core-app/features/bim/bcf/helper/viewpoints.service'; +import { BcfImportButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component'; +import { BcfExportButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component'; +import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { BcfWpAttributeGroupComponent } from 'core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-wp-attribute-group.component'; +import { BcfNewWpAttributeGroupComponent } from 'core-app/features/bim/bcf/bcf-wp-attribute-group/bcf-new-wp-attribute-group.component'; +import { RevitBridgeService } from 'core-app/features/bim/revit_add_in/revit-bridge.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { OpenProjectHeaderInterceptor } from 'core-app/features/hal/http/openproject-header-interceptor'; /** * Determines based on the current user agent whether @@ -55,9 +55,8 @@ import { OpenProjectHeaderInterceptor } from "core-app/features/hal/http/openpro export const viewerBridgeServiceFactory = (injector:Injector) => { if (window.navigator.userAgent.search('Revit') > -1) { return new RevitBridgeService(injector); - } else { - return injector.get(IFCViewerService, new IFCViewerService(injector)); } + return injector.get(IFCViewerService, new IFCViewerService(injector)); }; @NgModule({ @@ -70,7 +69,7 @@ export const viewerBridgeServiceFactory = (injector:Injector) => { { provide: ViewerBridgeService, useFactory: viewerBridgeServiceFactory, - deps: [Injector] + deps: [Injector], }, BcfDetectorService, BcfPathHelperService, @@ -85,7 +84,7 @@ export const viewerBridgeServiceFactory = (injector:Injector) => { exports: [ BcfImportButtonComponent, BcfExportButtonComponent, - ] + ], }) export class OpenprojectBcfModule { static bootstrapCalled = false; @@ -107,10 +106,9 @@ export class OpenprojectBcfModule { const displayFieldService = injector.get(DisplayFieldService); displayFieldService .addFieldType(BcfThumbnailDisplayField, 'bcfThumbnail', [ - 'BCF Thumbnail' + 'BCF Thumbnail', ]); - const hookService = injector.get(HookService); hookService.register('prependedAttributeGroups', (workPackage:WorkPackageResource) => { if (!window.OpenProject.isBimEdition) { @@ -119,10 +117,8 @@ export class OpenprojectBcfModule { if (workPackage.isNew) { return BcfNewWpAttributeGroupComponent; - } else { - return BcfWpAttributeGroupComponent; } + return BcfWpAttributeGroupComponent; }); } } - diff --git a/frontend/src/app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component.ts b/frontend/src/app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component.ts index e2a55dd194..f28c949bdc 100644 --- a/frontend/src/app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component.ts +++ b/frontend/src/app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component.ts @@ -1,20 +1,26 @@ -import { ChangeDetectionStrategy, Component, OnInit, NgZone } from "@angular/core"; -import { WorkPackageListViewComponent } from "core-app/features/work-packages/routing/wp-list-view/wp-list-view.component"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { bimSplitViewCardsIdentifier, bimSplitViewListIdentifier, bimListViewIdentifier, BimViewService } from "core-app/features/bim/ifc_models/pages/viewer/bim-view.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service"; +import { + ChangeDetectionStrategy, Component, NgZone, OnInit, +} from '@angular/core'; +import { WorkPackageListViewComponent } from 'core-app/features/work-packages/routing/wp-list-view/wp-list-view.component'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { + bimSplitViewCardsIdentifier, + bimSplitViewListIdentifier, + BimViewService, +} from 'core-app/features/bim/ifc_models/pages/viewer/bim-view.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { UIRouterGlobals } from '@uirouter/core'; -import { pluck, distinctUntilChanged } from "rxjs/operators"; -import { States } from "core-app/core/states/states.service"; -import { BcfApiService } from "core-app/features/bim/bcf/api/bcf-api.service"; -import { splitViewRoute } from "core-app/features/work-packages/routing/split-view-routes.helper"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { distinctUntilChanged, pluck } from 'rxjs/operators'; +import { States } from 'core-app/core/states/states.service'; +import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; +import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @Component({ templateUrl: './bcf-list-container.component.html', @@ -22,23 +28,29 @@ import { QueryResource } from "core-app/features/hal/resources/query-resource"; providers: [ { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService }, DragAndDropService, - CausedUpdatesService + CausedUpdatesService, ], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class BcfListContainerComponent extends WorkPackageListViewComponent implements OnInit { @InjectField() bimView:BimViewService; + @InjectField() ifcModelsService:IfcModelsDataService; + @InjectField() wpTableColumns:WorkPackageViewColumnsService; + @InjectField() uIRouterGlobals:UIRouterGlobals; + @InjectField() viewer:ViewerBridgeService; + @InjectField() states:States; + @InjectField() bcfApi:BcfApiService; - @InjectField() zone:NgZone; + @InjectField() zone:NgZone; public wpTableConfiguration = { - dragAndDropEnabled: false + dragAndDropEnabled: false, }; public showViewPointInFlight:boolean; @@ -78,10 +90,9 @@ export class BcfListContainerComponent extends WorkPackageListViewComponent impl public showResizerInCardView():boolean { if (this.noResults && this.ifcModelsService.models.length === 0) { return false; - } else { - return this.bimView.currentViewerState() === bimSplitViewCardsIdentifier || - this.bimView.currentViewerState() === bimSplitViewListIdentifier; } + return this.bimView.currentViewerState() === bimSplitViewCardsIdentifier + || this.bimView.currentViewerState() === bimSplitViewListIdentifier; } handleWorkPackageClicked(event:{ workPackageId:string; double:boolean }) { @@ -113,9 +124,9 @@ export class BcfListContainerComponent extends WorkPackageListViewComponent impl goToWpDetailState(workPackageId:string, cards:boolean, focus?:boolean) { // Show the split view when there is a viewer (browser) // Show only wp details when there is no viewer, plugin environment (ie: Revit) - const stateToGo = this.viewer.shouldShowViewer ? - splitViewRoute(this.$state) : - 'bim.partitioned.show'; + const stateToGo = this.viewer.shouldShowViewer + ? splitViewRoute(this.$state) + : 'bim.partitioned.show'; // Passing the card param to the new state because the router doesn't keep // it when going to 'bim.partitioned.show' const params = { workPackageId, cards, focus }; diff --git a/frontend/src/app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component.ts b/frontend/src/app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component.ts index 5780867515..8f77355af4 100644 --- a/frontend/src/app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component.ts +++ b/frontend/src/app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,13 +28,13 @@ import { WorkPackageCreateComponent } from 'core-app/features/work-packages/components/wp-new/wp-create.component'; import { Component } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { IFCViewerService } from "core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; @Component({ selector: 'bcf-new-split', - templateUrl: './bcf-new-split.component.html' + templateUrl: './bcf-new-split.component.html', }) export class BCFNewSplitComponent extends WorkPackageCreateComponent { public cancelState = '^'; diff --git a/frontend/src/app/features/bim/ifc_models/empty/empty-component.ts b/frontend/src/app/features/bim/ifc_models/empty/empty-component.ts index a89a650123..b687c04660 100644 --- a/frontend/src/app/features/bim/ifc_models/empty/empty-component.ts +++ b/frontend/src/app/features/bim/ifc_models/empty/empty-component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - template: '
' + template: '
', }) export class EmptyComponent { } diff --git a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts index 2515712232..e75f530b5b 100644 --- a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts +++ b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -33,42 +33,45 @@ import { HostListener, OnDestroy, OnInit, - ViewChild + ViewChild, } from '@angular/core'; -import { IFCViewerService } from "core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service"; -import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; +import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @Component({ selector: 'ifc-viewer', templateUrl: './ifc-viewer.component.html', styleUrls: ['./ifc-viewer.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class IFCViewerComponent implements OnInit, OnDestroy { private viewerUI:any; + modelCount:number; + canManage = this.ifcData.allowed('manage_ifc_models'); text = { empty_warning: this.I18n.t('js.ifc_models.empty_warning'), use_this_link_to_manage: this.I18n.t('js.ifc_models.use_this_link_to_manage'), - keyboard_input_disabled: this.I18n.t('js.ifc_models.keyboard_input_disabled') + keyboard_input_disabled: this.I18n.t('js.ifc_models.keyboard_input_disabled'), }; keyboardEnabled = false; @ViewChild('outerContainer') outerContainer:ElementRef; + @ViewChild('modelCanvas') modelCanvas:ElementRef; constructor(private I18n:I18nService, - private elementRef:ElementRef, - public ifcData:IfcModelsDataService, - private ifcViewer:IFCViewerService, - private currentUserService:CurrentUserService, - private currentProjectService:CurrentProjectService) { + private elementRef:ElementRef, + public ifcData:IfcModelsDataService, + private ifcViewer:IFCViewerService, + private currentUserService:CurrentUserService, + private currentProjectService:CurrentProjectService) { } ngOnInit():void { @@ -85,23 +88,22 @@ export class IFCViewerComponent implements OnInit, OnDestroy { [ 'ifc_models/create', 'ifc_models/update', - 'ifc_models/destroy' + 'ifc_models/destroy', ], - this.currentProjectService.id as string + this.currentProjectService.id as string, ).subscribe((manageIfcModelsAllowed) => { this.ifcViewer.newViewer( { - canvasElement: element.find(".ifc-model-viewer--model-canvas")[0], // WebGL canvas - explorerElement: jQuery(".ifc-model-viewer--tree-panel")[0], // Left panel - toolbarElement: element.find(".ifc-model-viewer--toolbar-container")[0], // Toolbar - navCubeCanvasElement: element.find(".ifc-model-viewer--nav-cube-canvas")[0], - busyModelBackdropElement: element.find(".xeokit-busy-modal-backdrop")[0], - enableEditModels: manageIfcModelsAllowed + canvasElement: element.find('.ifc-model-viewer--model-canvas')[0], // WebGL canvas + explorerElement: jQuery('.ifc-model-viewer--tree-panel')[0], // Left panel + toolbarElement: element.find('.ifc-model-viewer--toolbar-container')[0], // Toolbar + navCubeCanvasElement: element.find('.ifc-model-viewer--nav-cube-canvas')[0], + busyModelBackdropElement: element.find('.xeokit-busy-modal-backdrop')[0], + enableEditModels: manageIfcModelsAllowed, }, - this.ifcData.projects - ) + this.ifcData.projects, + ); }); - } ngOnDestroy():void { @@ -118,7 +120,7 @@ export class IFCViewerComponent implements OnInit, OnDestroy { @HostListener('window:mousedown', ['$event.target']) disableKeyboard(target:Element) { - if (this.modelCount && !this.outerContainer.nativeElement!.contains(target)) { + if (this.modelCount && !this.outerContainer.nativeElement.contains(target)) { this.keyboardEnabled = false; this.ifcViewer.setKeyboardEnabled(false); } diff --git a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts index 026689d14a..c8cb6effe6 100644 --- a/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts +++ b/frontend/src/app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service.ts @@ -1,16 +1,15 @@ -import { Injectable, Inject, Injector } from '@angular/core'; -import { XeokitServer } from "core-app/features/bim/ifc_models/xeokit/xeokit-server"; -import { BcfViewpointInterface } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { BehaviorSubject, Observable, Subject , of } from "rxjs"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { BcfApiService } from "core-app/features/bim/bcf/api/bcf-api.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { ViewpointsService } from "core-app/features/bim/bcf/helper/viewpoints.service"; -import { CurrentProjectService} from "core-app/core/current-project/current-project.service"; -import { HttpClient } from "@angular/common/http"; - +import { Injectable, Injector } from '@angular/core'; +import { XeokitServer } from 'core-app/features/bim/ifc_models/xeokit/xeokit-server'; +import { BcfViewpointInterface } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { ViewpointsService } from 'core-app/features/bim/bcf/helper/viewpoints.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { HttpClient } from '@angular/common/http'; export interface XeokitElements { canvasElement:HTMLElement; @@ -36,13 +35,19 @@ export interface BCFLoadOptions { @Injectable() export class IFCViewerService extends ViewerBridgeService { public shouldShowViewer = true; + public viewerVisible$ = new BehaviorSubject(false); + private _viewer:any; @InjectField() pathHelper:PathHelperService; + @InjectField() bcfApi:BcfApiService; + @InjectField() viewpointsService:ViewpointsService; + @InjectField() currentProjectService:CurrentProjectService; + @InjectField() httpClient:HttpClient; constructor(readonly injector:Injector) { @@ -54,46 +59,47 @@ export class IFCViewerService extends ViewerBridgeService { const server = new XeokitServer(this.pathHelper); const viewerUI = new XeokitViewerModule.BIMViewer(server, elements); - viewerUI.on("queryPicked", (event:any) => { + viewerUI.on('queryPicked', (event:any) => { alert(`IFC Name = "${event.objectName}"\nIFC class = "${event.objectType}"\nIFC GUID = ${event.objectId}`); }); - viewerUI.on("modelLoaded", () => this.viewerVisible$.next(true)); + viewerUI.on('modelLoaded', () => this.viewerVisible$.next(true)); - viewerUI.loadProject(projects[0]["id"]); + viewerUI.loadProject(projects[0].id); - viewerUI.on("addModel", (event:Event) => { // "Add" selected in Models tab's context menu + viewerUI.on('addModel', (event:Event) => { // "Add" selected in Models tab's context menu window.location.href = this.pathHelper.ifcModelsNewPath(this.currentProjectService.identifier as string); }); - viewerUI.on("editModel", (event:{ modelId:number|string }) => { // "Edit" selected in Models tab's context menu + viewerUI.on('editModel', (event:{ modelId:number|string }) => { // "Edit" selected in Models tab's context menu window.location.href = this.pathHelper.ifcModelsEditPath(this.currentProjectService.identifier as string, event.modelId); }); - viewerUI.on("deleteModel", (event:{ modelId:number|string }) => { // "Delete" selected in Models tab's context menu + viewerUI.on('deleteModel', (event:{ modelId:number|string }) => { // "Delete" selected in Models tab's context menu // We don't have an API for IFC models yet. We need to use the normal Rails form posts for deletion. const formData = new FormData(); formData.append( 'authenticity_token', - jQuery('meta[name=csrf-token]').attr('content') as string + jQuery('meta[name=csrf-token]').attr('content') as string, ); formData.append( '_method', - 'delete' + 'delete', ); this.httpClient.post( this.pathHelper.ifcModelsDeletePath( - this.currentProjectService.identifier as string, event.modelId), - formData - ) + this.currentProjectService.identifier as string, event.modelId, + ), + formData, + ) .subscribe() .add(() => { // Ensure we reload after every request. // We need to reload to get a fresh CSRF token for a successive // model deletion placed as a META element into the HTML HEAD. - window.location.reload() - }) + window.location.reload(); + }); }); this.viewer = viewerUI; @@ -137,10 +143,10 @@ export class IFCViewerService extends ViewerBridgeService { // ('bim.partitioned.split') if (this.routeWithViewer) { if (this.viewer) { - let viewpointOptions = { updateCompositeObjects: true }; + const viewpointOptions = { updateCompositeObjects: true }; this.viewpointsService .getViewPoint$(workPackage, index) - .subscribe(viewpoint => this.viewer.loadBCFViewpoint(viewpoint, viewpointOptions)); + .subscribe((viewpoint) => this.viewer.loadBCFViewpoint(viewpoint, viewpointOptions)); } } else { // Reload the whole app to get the correct menus and GON data @@ -149,7 +155,7 @@ export class IFCViewerService extends ViewerBridgeService { window.location.href = this.pathHelper.bimDetailsPath( workPackage.project.idFromLink, workPackage.id!, - index + index, ); } } @@ -157,4 +163,4 @@ export class IFCViewerService extends ViewerBridgeService { public viewerVisible():boolean { return !!this.viewer; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.module.ts b/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.module.ts index 33e74798da..80d2b91016 100644 --- a/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.module.ts +++ b/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,25 +25,25 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { NgModule } from "@angular/core"; -import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/openproject-work-packages.module"; +import { NgModule } from '@angular/core'; +import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; import { UIRouterModule } from '@uirouter/angular'; -import { OPSharedModule } from "core-app/shared/shared.module"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { IFC_ROUTES } from 'core-app/features/bim/ifc_models/openproject-ifc-models.routes'; +import { IFCViewerPageComponent } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component'; +import { EmptyComponent } from 'core-app/features/bim/ifc_models/empty/empty-component'; +import { BimViewToggleButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component'; +import { BimViewToggleDropdownDirective } from 'core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive'; +import { BimManageIfcModelsButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component'; +import { IFCViewerService } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service'; +import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openproject-fields.module'; +import { BCFNewSplitComponent } from 'core-app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component'; +import { BcfListContainerComponent } from 'core-app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component'; +import { BimViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bim-view.service'; +import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; +import { OpenprojectBcfModule } from 'core-app/features/bim/bcf/openproject-bcf.module'; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; import { IFCViewerComponent } from './ifc-viewer/ifc-viewer.component'; -import { IFC_ROUTES } from "core-app/features/bim/ifc_models/openproject-ifc-models.routes"; -import { IFCViewerPageComponent } from "core-app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component"; -import { EmptyComponent } from "core-app/features/bim/ifc_models/empty/empty-component"; -import { BimViewToggleButtonComponent } from "core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component"; -import { BimViewToggleDropdownDirective } from "core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive"; -import { BimManageIfcModelsButtonComponent } from "core-app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component"; -import { IFCViewerService } from "core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.service"; -import { OpenprojectFieldsModule } from "core-app/shared/components/fields/openproject-fields.module"; -import { BCFNewSplitComponent } from "core-app/features/bim/ifc_models/bcf/new-split/bcf-new-split.component"; -import { BcfListContainerComponent } from "core-app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component"; -import { BimViewService } from "core-app/features/bim/ifc_models/pages/viewer/bim-view.service"; -import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service"; -import { OpenprojectBcfModule } from "core-app/features/bim/bcf/openproject-bcf.module"; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; @NgModule({ imports: [ @@ -53,13 +53,13 @@ import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.modu OpenprojectBcfModule, OpenprojectWorkPackagesModule, UIRouterModule.forChild({ - states: IFC_ROUTES - }) + states: IFC_ROUTES, + }), ], providers: [ IFCViewerService, BimViewService, - IfcModelsDataService + IfcModelsDataService, ], declarations: [ // Pages @@ -75,9 +75,8 @@ import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.modu BimViewToggleDropdownDirective, BCFNewSplitComponent, - IFCViewerComponent - ] + IFCViewerComponent, + ], }) export class OpenprojectIFCModelsModule { } - diff --git a/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.routes.ts b/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.routes.ts index 9658e0ddaf..2e44af2408 100644 --- a/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.routes.ts +++ b/frontend/src/app/features/bim/ifc_models/openproject-ifc-models.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ import { Ng2StateDeclaration } from '@uirouter/angular'; -import { IFCViewerPageComponent } from "core-app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component"; -import { IFCViewerComponent } from "core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component"; -import { WorkPackagesBaseComponent } from "core-app/features/work-packages/routing/wp-base/wp--base.component"; -import { EmptyComponent } from "core-app/features/bim/ifc_models/empty/empty-component"; -import { makeSplitViewRoutes } from "core-app/features/work-packages/routing/split-view-routes.template"; -import { BcfListContainerComponent } from "core-app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component"; -import { WorkPackageSplitViewComponent } from "core-app/features/work-packages/routing/wp-split-view/wp-split-view.component"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { WorkPackageNewFullViewComponent } from "core-app/features/work-packages/components/wp-new/wp-new-full-view.component"; +import { IFCViewerPageComponent } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component'; +import { IFCViewerComponent } from 'core-app/features/bim/ifc_models/ifc-viewer/ifc-viewer.component'; +import { WorkPackagesBaseComponent } from 'core-app/features/work-packages/routing/wp-base/wp--base.component'; +import { EmptyComponent } from 'core-app/features/bim/ifc_models/empty/empty-component'; +import { makeSplitViewRoutes } from 'core-app/features/work-packages/routing/split-view-routes.template'; +import { BcfListContainerComponent } from 'core-app/features/bim/ifc_models/bcf/list-container/bcf-list-container.component'; +import { WorkPackageSplitViewComponent } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view.component'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { WorkPackageNewFullViewComponent } from 'core-app/features/work-packages/components/wp-new/wp-new-full-view.component'; export const IFC_ROUTES:Ng2StateDeclaration[] = [ { @@ -48,8 +48,8 @@ export const IFC_ROUTES:Ng2StateDeclaration[] = [ // Use custom encoder/decoder that ensures validity of URL string query_props: { type: 'opQueryString', dynamic: true }, models: { type: 'opQueryString', dynamic: true }, - viewpoint: { type: 'int', dynamic: true } - } + viewpoint: { type: 'int', dynamic: true }, + }, }, { name: 'bim.partitioned', @@ -58,44 +58,44 @@ export const IFC_ROUTES:Ng2StateDeclaration[] = [ redirectTo: (transition) => { const viewerBridgeService = transition.injector().get(ViewerBridgeService); - return viewerBridgeService.shouldShowViewer ? - 'bim.partitioned.split' : - 'bim.partitioned.list'; + return viewerBridgeService.shouldShowViewer + ? 'bim.partitioned.split' + : 'bim.partitioned.list'; }, }, { name: 'bim.partitioned.list', url: '/list?{cards:bool}', params: { - cards: true + cards: true, }, data: { baseRoute: 'bim.partitioned.list', newRoute: 'bim.partitioned.list.new', - partition: '-left-only' + partition: '-left-only', }, reloadOnSearch: false, views: { - 'content-left': { component: BcfListContainerComponent } - } + 'content-left': { component: BcfListContainerComponent }, + }, }, { name: 'bim.partitioned.split', url: '/split?{cards:bool}', params: { - cards: true + cards: true, }, data: { baseRoute: 'bim.partitioned.split', partition: '-split', newRoute: 'bim.partitioned.split.new', - bodyClasses: 'router--work-packages-partitioned-split-view' + bodyClasses: 'router--work-packages-partitioned-split-view', }, reloadOnSearch: false, views: { 'content-left': { component: IFCViewerComponent }, - 'content-right': { component: BcfListContainerComponent } - } + 'content-right': { component: BcfListContainerComponent }, + }, }, { name: 'bim.partitioned.model', @@ -109,8 +109,8 @@ export const IFC_ROUTES:Ng2StateDeclaration[] = [ // Retarget and by that override the grandparent views // https://ui-router.github.io/guide/views#relative-parent-state{ 'content-right': { component: EmptyComponent }, - 'content-left': { component: IFCViewerComponent } - } + 'content-left': { component: IFCViewerComponent }, + }, }, { name: 'bim.partitioned.new', @@ -121,14 +121,14 @@ export const IFC_ROUTES:Ng2StateDeclaration[] = [ allowMovingInEditMode: true, partition: '-left-only', }, - views: { 'content-left': { component:WorkPackageNewFullViewComponent } } + views: { 'content-left': { component: WorkPackageNewFullViewComponent } }, }, { name: 'bim.partitioned.show', url: '/show/{workPackageId:[0-9]+}?{cards:bool}', data: { baseRoute: 'bim.partitioned.list', - partition: '-left-only' + partition: '-left-only', }, reloadOnSearch: false, redirectTo: 'bim.partitioned.show.details', @@ -146,19 +146,18 @@ export const IFC_ROUTES:Ng2StateDeclaration[] = [ ...makeSplitViewRoutes( 'bim.partitioned.list', undefined, - WorkPackageSplitViewComponent + WorkPackageSplitViewComponent, ), // BCF single view for split ...makeSplitViewRoutes( 'bim.partitioned.split', undefined, - WorkPackageSplitViewComponent + WorkPackageSplitViewComponent, ), // BCF single view for model-only ...makeSplitViewRoutes( 'bim.partitioned.model', undefined, - WorkPackageSplitViewComponent + WorkPackageSplitViewComponent, ), ]; - diff --git a/frontend/src/app/features/bim/ifc_models/pages/viewer/bim-view.service.ts b/frontend/src/app/features/bim/ifc_models/pages/viewer/bim-view.service.ts index d34c267c54..f238123733 100644 --- a/frontend/src/app/features/bim/ifc_models/pages/viewer/bim-view.service.ts +++ b/frontend/src/app/features/bim/ifc_models/pages/viewer/bim-view.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,11 +28,10 @@ import { Injectable, OnDestroy } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Observable } from "rxjs"; -import { StateService, TransitionService } from "@uirouter/core"; -import { input } from "reactivestates"; -import { takeUntil } from "rxjs/operators"; - +import { Observable } from 'rxjs'; +import { StateService, TransitionService } from '@uirouter/core'; +import { input } from 'reactivestates'; +import { takeUntil } from 'rxjs/operators'; export const bimListViewIdentifier = 'list'; export const bimTableViewIdentifier = 'table'; @@ -65,9 +64,8 @@ export class BimViewService implements OnDestroy { private transitionFn:Function; constructor(readonly I18n:I18nService, - readonly transitions:TransitionService, - readonly state:StateService) { - + readonly transitions:TransitionService, + readonly state:StateService) { this.detectView(); this.transitionFn = this.transitions.onSuccess({}, (transition) => { @@ -89,20 +87,19 @@ export class BimViewService implements OnDestroy { public currentViewerState():BimViewState { if (this.state.includes('bim.partitioned.list')) { - return this.state.params?.cards ? - bimListViewIdentifier : - bimTableViewIdentifier; - } else if (this.state.includes('bim.**.model')) { + return this.state.params?.cards + ? bimListViewIdentifier + : bimTableViewIdentifier; + } if (this.state.includes('bim.**.model')) { return bimViewerViewIdentifier; - } else if (this.state.includes('bim.partitioned.show')) { - return this.state.params?.cards || this.state.params?.cards == null ? - bimListViewIdentifier : - bimTableViewIdentifier; - } else { - return this.state.params?.cards || this.state.params?.cards == null ? - bimSplitViewCardsIdentifier : - bimSplitViewListIdentifier; + } if (this.state.includes('bim.partitioned.show')) { + return this.state.params?.cards || this.state.params?.cards == null + ? bimListViewIdentifier + : bimTableViewIdentifier; } + return this.state.params?.cards || this.state.params?.cards == null + ? bimSplitViewCardsIdentifier + : bimSplitViewListIdentifier; } private detectView() { diff --git a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-models-data.service.ts b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-models-data.service.ts index 3f64b775bc..e7435aea97 100644 --- a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-models-data.service.ts +++ b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-models-data.service.ts @@ -1,7 +1,7 @@ -import { Injectable } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { GonService } from "core-app/core/gon/gon.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { Injectable } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { GonService } from 'core-app/core/gon/gon.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; export interface IFCPermissionMap { manage_ifc_models:boolean; @@ -29,10 +29,9 @@ export interface IfcModelDefinition { @Injectable() export class IfcModelsDataService { - constructor(readonly paths:PathHelperService, - readonly currentProjectService:CurrentProjectService, - readonly gon:GonService) { + readonly currentProjectService:CurrentProjectService, + readonly gon:GonService) { } public get models():IfcModelDefinition[] { @@ -54,7 +53,7 @@ export class IfcModelsDataService { public isDefaults():boolean { return !this .models - .find(item => item.default && this.shownModels.indexOf(item.id) === -1); + .find((item) => item.default && this.shownModels.indexOf(item.id) === -1); } public get manageIFCPath() { @@ -68,4 +67,4 @@ export class IfcModelsDataService { private get gonIFC():IFCGonDefinition { return (this.gon.get('ifc_models') as IFCGonDefinition); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts index ab9c46aba5..b4ca118514 100644 --- a/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts +++ b/frontend/src/app/features/bim/ifc_models/pages/viewer/ifc-viewer-page.component.ts @@ -1,51 +1,53 @@ -import { ChangeDetectionStrategy, Component, Injector, ViewEncapsulation } from "@angular/core"; -import { GonService } from "core-app/core/gon/gon.service"; +import { + ChangeDetectionStrategy, Component, Injector, ViewEncapsulation, +} from '@angular/core'; +import { GonService } from 'core-app/core/gon/gon.service'; import { PartitionedQuerySpacePageComponent, - ToolbarButtonComponentDefinition -} from "core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component"; -import { WorkPackageFilterButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component"; -import { ZenModeButtonComponent } from "core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component"; + ToolbarButtonComponentDefinition, +} from 'core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component'; +import { WorkPackageFilterButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component'; +import { ZenModeButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; import { bimListViewIdentifier, bimViewerViewIdentifier, - BimViewService -} from "core-app/features/bim/ifc_models/pages/viewer/bim-view.service"; -import { BimViewToggleButtonComponent } from "core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component"; -import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service"; -import { QueryParamListenerService } from "core-app/features/work-packages/components/wp-query/query-param-listener.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { BimManageIfcModelsButtonComponent } from "core-app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component"; -import { WorkPackageCreateButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component"; -import { StateService, TransitionService } from "@uirouter/core"; -import { BehaviorSubject } from "rxjs"; -import { BcfImportButtonComponent } from "core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component"; -import { BcfExportButtonComponent } from "core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; + BimViewService, +} from 'core-app/features/bim/ifc_models/pages/viewer/bim-view.service'; +import { BimViewToggleButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component'; +import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; +import { QueryParamListenerService } from 'core-app/features/work-packages/components/wp-query/query-param-listener.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { BimManageIfcModelsButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component'; +import { WorkPackageCreateButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component'; +import { StateService, TransitionService } from '@uirouter/core'; +import { BehaviorSubject } from 'rxjs'; +import { BcfImportButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component'; +import { BcfExportButtonComponent } from 'core-app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; @Component({ templateUrl: '../../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html', styleUrls: [ '../../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.sass', - './styles/generic.sass' + './styles/generic.sass', ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [ QueryParamListenerService, - ] + ], }) export class IFCViewerPageComponent extends PartitionedQuerySpacePageComponent { - text = { title: this.I18n.t('js.bcf.management'), delete: this.I18n.t('js.button_delete'), edit: this.I18n.t('js.button_edit'), - areYouSure: this.I18n.t('js.text_are_you_sure') + areYouSure: this.I18n.t('js.text_are_you_sure'), }; newRoute$ = new BehaviorSubject(undefined); + transitionUnsubscribeFn:Function; toolbarButtonComponents:ToolbarButtonComponentDefinition[] = [ @@ -53,56 +55,56 @@ export class IFCViewerPageComponent extends PartitionedQuerySpacePageComponent { component: WorkPackageCreateButtonComponent, inputs: { stateName$: this.newRoute$, - allowed: ['work_packages.createWorkPackage', 'work_package.copy'] - } + allowed: ['work_packages.createWorkPackage', 'work_package.copy'], + }, }, { component: BcfImportButtonComponent, show: () => this.ifcData.allowed('manage_bcf'), - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: BcfExportButtonComponent, show: () => this.ifcData.allowed('manage_bcf'), - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: WorkPackageFilterButtonComponent, - show: () => this.bimView.currentViewerState() !== bimViewerViewIdentifier + show: () => this.bimView.currentViewerState() !== bimViewerViewIdentifier, }, { component: BimViewToggleButtonComponent, - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: ZenModeButtonComponent, - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: BimManageIfcModelsButtonComponent, - show: () => { + show: () => // Hide 'Manage models' toolbar button on plugin environment (ie: Revit) - return this.viewerBridgeService.shouldShowViewer && - this.ifcData.allowed('manage_ifc_models'); - } - } + this.viewerBridgeService.shouldShowViewer + && this.ifcData.allowed('manage_ifc_models'), + + }, ]; - get newRoute () { + get newRoute() { // Open new work packages in full view when there // is no viewer (ie: Revit) - return this.viewerBridgeService.shouldShowViewer ? - this.state.current.data.newRoute : - 'bim.partitioned.new'; + return this.viewerBridgeService.shouldShowViewer + ? this.state.current.data.newRoute + : 'bim.partitioned.new'; } constructor(readonly ifcData:IfcModelsDataService, - readonly state:StateService, - readonly bimView:BimViewService, - readonly transition:TransitionService, - readonly gon:GonService, - readonly injector:Injector, - readonly viewerBridgeService:ViewerBridgeService) { + readonly state:StateService, + readonly bimView:BimViewService, + readonly transition:TransitionService, + readonly gon:GonService, + readonly injector:Injector, + readonly viewerBridgeService:ViewerBridgeService) { super(injector); } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts index fa43bd9951..78edb08d1a 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, OnDestroy, OnInit, Injector } from '@angular/core'; +import { + Component, Injector, OnDestroy, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { BcfPathHelperService } from "core-app/features/bim/bcf/helper/bcf-path-helper.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { StateService } from "@uirouter/core"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { WpTableExportModal } from "core-app/shared/components/modals/export-modal/wp-table-export.modal"; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { StateService } from '@uirouter/core'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { WpTableExportModalComponent } from 'core-app/shared/components/modals/export-modal/wp-table-export.modal'; @Component({ template: ` @@ -53,19 +55,21 @@ import { WpTableExportModal } from "core-app/shared/components/modals/export-mod export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { public text = { export: this.I18n.t('js.bcf.export'), - export_hover: this.I18n.t('js.bcf.export_bcf_xml_file') + export_hover: this.I18n.t('js.bcf.export_bcf_xml_file'), }; + public query:QueryResource; + public exportLink:string; constructor(readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly bcfPathHelper:BcfPathHelperService, - readonly querySpace:IsolatedQuerySpace, - readonly queryUrlParamsHelper:UrlParamsHelperService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly state:StateService) { + readonly currentProject:CurrentProjectService, + readonly bcfPathHelper:BcfPathHelperService, + readonly querySpace:IsolatedQuerySpace, + readonly queryUrlParamsHelper:UrlParamsHelperService, + readonly opModalService:OpModalService, + readonly injector:Injector, + readonly state:StateService) { super(); } @@ -73,7 +77,7 @@ export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnI this.querySpace.query .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((query) => { this.query = query; @@ -82,13 +86,13 @@ export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnI const filters = this.queryUrlParamsHelper.buildV3GetFilters(this.query.filters); this.exportLink = this.bcfPathHelper.projectExportIssuesPath( projectIdentifier!, - JSON.stringify(filters) + JSON.stringify(filters), ); }); } public showDelayedExport(event:any) { - this.opModalService.show(WpTableExportModal, this.injector, { link: this.exportLink }); + this.opModalService.show(WpTableExportModalComponent, this.injector, { link: this.exportLink }); event.preventDefault(); } } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts index e538430be4..22f3635efa 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,8 +28,8 @@ import { Component } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { BcfPathHelperService } from "core-app/features/bim/bcf/helper/bcf-path-helper.service"; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; @Component({ template: ` @@ -45,18 +45,18 @@ import { BcfPathHelperService } from "core-app/features/bim/bcf/helper/bcf-path- export class BcfImportButtonComponent { public text = { import: this.I18n.t('js.bcf.import'), - import_hover: this.I18n.t('js.bcf.import_bcf_xml_file') + import_hover: this.I18n.t('js.bcf.import_bcf_xml_file'), }; constructor(readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly bcfPathHelper:BcfPathHelperService) { + readonly currentProject:CurrentProjectService, + readonly bcfPathHelper:BcfPathHelperService) { } public handleClick() { - var projectIdentifier = this.currentProject.identifier; + const projectIdentifier = this.currentProject.identifier; if (projectIdentifier) { - var url = this.bcfPathHelper.projectImportIssuePath(projectIdentifier); + const url = this.bcfPathHelper.projectImportIssuePath(projectIdentifier); window.location.href = url; } } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts index 8e3dc1bd8e..31da7a93d5 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/manage-ifc-models-button/bim-manage-ifc-models-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,8 +28,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service"; - +import { IfcModelsDataService } from 'core-app/features/bim/ifc_models/pages/viewer/ifc-models-data.service'; @Component({ template: ` @@ -44,18 +43,18 @@ import { IfcModelsDataService } from "core-app/features/bim/ifc_models/pages/vie `, changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'bim-view-toggle-button' + selector: 'bim-view-toggle-button', }) export class BimManageIfcModelsButtonComponent { - text = { manage: this.I18n.t('js.ifc_models.models.ifc_models'), }; manageAllowed = this.ifcData.allowed('manage_ifc_models'); + manageIFCPath = this.ifcData.manageIFCPath; constructor(readonly I18n:I18nService, - readonly ifcData:IfcModelsDataService) { + readonly ifcData:IfcModelsDataService) { } } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component.ts index 7e84bf0f93..94abd06627 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,8 +28,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { BimViewService } from "core-app/features/bim/ifc_models/pages/viewer/bim-view.service"; - +import { BimViewService } from 'core-app/features/bim/ifc_models/pages/viewer/bim-view.service'; @Component({ template: ` @@ -47,13 +46,12 @@ import { BimViewService } from "core-app/features/bim/ifc_models/pages/viewer/bi `, changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'bim-view-toggle-button' + selector: 'bim-view-toggle-button', }) export class BimViewToggleButtonComponent { - view$ = this.bimView.view$; constructor(readonly I18n:I18nService, - readonly bimView:BimViewService) { + readonly bimView:BimViewService) { } } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive.ts b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive.ts index 501c3638dc..d0dcafeb5b 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/view-toggle/bim-view-toggle-dropdown.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,36 +26,35 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; -import { Directive, ElementRef } from "@angular/core"; -import { OpContextMenuTrigger } from "core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { StateService } from "@uirouter/core"; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { Directive, ElementRef } from '@angular/core'; +import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { StateService } from '@uirouter/core'; import { - bimListViewIdentifier, bimSplitViewListIdentifier, bimSplitViewCardsIdentifier, bimTableViewIdentifier, + bimListViewIdentifier, + bimSplitViewCardsIdentifier, + bimSplitViewListIdentifier, + bimTableViewIdentifier, bimViewerViewIdentifier, - BimViewService -} from "core-app/features/bim/ifc_models/pages/viewer/bim-view.service"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { - WorkPackageViewDisplayRepresentationService, -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; + BimViewService, +} from 'core-app/features/bim/ifc_models/pages/viewer/bim-view.service'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; @Directive({ - selector: '[bimViewDropdown]' + selector: '[bimViewDropdown]', }) export class BimViewToggleDropdownDirective extends OpContextMenuTrigger { - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly bimView:BimViewService, - readonly I18n:I18nService, - readonly state:StateService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly viewerBridgeService:ViewerBridgeService, - readonly wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService) { - + readonly opContextMenu:OPContextMenuService, + readonly bimView:BimViewService, + readonly I18n:I18nService, + readonly state:StateService, + readonly wpFiltersService:WorkPackageFiltersService, + readonly viewerBridgeService:ViewerBridgeService, + readonly wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService) { super(elementRef, opContextMenu); } @@ -67,40 +66,39 @@ export class BimViewToggleDropdownDirective extends OpContextMenuTrigger { public get locals() { return { items: this.items, - contextMenuId: 'bim-view-context-menu' + contextMenuId: 'bim-view-context-menu', }; } private buildItems() { - const current = this.bimView.current; - const items = this.viewerBridgeService.shouldShowViewer ? - [bimViewerViewIdentifier, bimListViewIdentifier, bimSplitViewCardsIdentifier, bimSplitViewListIdentifier, bimTableViewIdentifier] : - [bimListViewIdentifier, bimTableViewIdentifier]; + const { current } = this.bimView; + const items = this.viewerBridgeService.shouldShowViewer + ? [bimViewerViewIdentifier, bimListViewIdentifier, bimSplitViewCardsIdentifier, bimSplitViewListIdentifier, bimTableViewIdentifier] + : [bimListViewIdentifier, bimTableViewIdentifier]; this.items = items - .map(key => { - return { - hidden: key === current, - linkText: this.bimView.text[key], - icon: this.bimView.icon[key], - onClick: () => { - // Close filter section - if (this.wpFiltersService.visible) { - this.wpFiltersService.toggleVisibility(); - } + .map((key) => ({ + hidden: key === current, + linkText: this.bimView.text[key], + icon: this.bimView.icon[key], + onClick: () => { + // Close filter section + if (this.wpFiltersService.visible) { + this.wpFiltersService.toggleVisibility(); + } - switch (key) { - // This project controls the view representation of the data through - // the wpDisplayRepresentation service that modifies the QuerySpace - // to inform the rest of the app about which display mode is currently - // active (this.querySpace.query.live$). - // Under the hood it is done by modifying the params of actual route. - // Because of that, it is not possible to call this.state.go and - // this.wpDisplayRepresentation.setDisplayRepresentation at the same - // time, it raises a route error (The transition has been superseded by - // a different transition...). To avoid this error, we are passing - // a cards params to inform the view about the display representation mode - // it has to show (cards or list). + switch (key) { + // This project controls the view representation of the data through + // the wpDisplayRepresentation service that modifies the QuerySpace + // to inform the rest of the app about which display mode is currently + // active (this.querySpace.query.live$). + // Under the hood it is done by modifying the params of actual route. + // Because of that, it is not possible to call this.state.go and + // this.wpDisplayRepresentation.setDisplayRepresentation at the same + // time, it raises a route error (The transition has been superseded by + // a different transition...). To avoid this error, we are passing + // a cards params to inform the view about the display representation mode + // it has to show (cards or list). case bimListViewIdentifier: this.state.go('bim.partitioned.list', { cards: true }); break; @@ -116,12 +114,10 @@ export class BimViewToggleDropdownDirective extends OpContextMenuTrigger { case bimSplitViewListIdentifier: this.state.go('bim.partitioned.split', { cards: false }); break; - } - - return true; } - }; - }); + + return true; + }, + })); } } - diff --git a/frontend/src/app/features/bim/ifc_models/xeokit/xeokit-server.ts b/frontend/src/app/features/bim/ifc_models/xeokit/xeokit-server.ts index b8021eb073..afe38e641c 100644 --- a/frontend/src/app/features/bim/ifc_models/xeokit/xeokit-server.ts +++ b/frontend/src/app/features/bim/ifc_models/xeokit/xeokit-server.ts @@ -1,13 +1,14 @@ // @ts-ignore -import { utils } from "@xeokit/xeokit-sdk/src/viewer/scene/utils"; -import { IFCGonDefinition } from "../pages/viewer/ifc-models-data.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { utils } from '@xeokit/xeokit-sdk/src/viewer/scene/utils'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { IFCGonDefinition } from '../pages/viewer/ifc-models-data.service'; /** * Default server client which loads content via HTTP from the file system. */ export class XeokitServer { private ifcModels:IFCGonDefinition; + /** * * @param config @@ -33,14 +34,14 @@ export class XeokitServer { * @param error */ getProject(projectData:any, done:Function, _error:Function) { - var manifestData = { + const manifestData = { id: projectData[0].id, name: projectData[0].name, models: this.ifcModels.models, viewerContent: { - modelsLoaded: this.ifcModels.shown_models + modelsLoaded: this.ifcModels.shown_models, }, - viewerConfigs: {} + viewerConfigs: {}, }; done(manifestData); diff --git a/frontend/src/app/features/bim/ifc_models/xeokit/xeokit.d.ts b/frontend/src/app/features/bim/ifc_models/xeokit/xeokit.d.ts index 05683e5553..c3b280a503 100644 --- a/frontend/src/app/features/bim/ifc_models/xeokit/xeokit.d.ts +++ b/frontend/src/app/features/bim/ifc_models/xeokit/xeokit.d.ts @@ -1,2 +1,2 @@ declare module '@xeokit/xeokit-bim-viewer/node_modules/@xeokit/xeokit-sdk/src/viewer/scene/utils'; -declare module '@xeokit/xeokit-bim-viewer/dist/xeokit-bim-viewer.es'; \ No newline at end of file +declare module '@xeokit/xeokit-bim-viewer/dist/xeokit-bim-viewer.es'; diff --git a/frontend/src/app/features/bim/openproject-bim.module.ts b/frontend/src/app/features/bim/openproject-bim.module.ts index 6d2e9f5a18..3f2bbb4494 100644 --- a/frontend/src/app/features/bim/openproject-bim.module.ts +++ b/frontend/src/app/features/bim/openproject-bim.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,15 +27,14 @@ //++ import { NgModule } from '@angular/core'; -import { OpenprojectBcfModule } from "core-app/features/bim/bcf/openproject-bcf.module"; -import { OpenprojectIFCModelsModule } from "core-app/features/bim/ifc_models/openproject-ifc-models.module"; +import { OpenprojectBcfModule } from 'core-app/features/bim/bcf/openproject-bcf.module'; +import { OpenprojectIFCModelsModule } from 'core-app/features/bim/ifc_models/openproject-ifc-models.module'; @NgModule({ imports: [ OpenprojectBcfModule, OpenprojectIFCModelsModule, - ] + ], }) export class OpenprojectBimModule { } - diff --git a/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts b/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts index 4b511dcfa1..12f353c361 100644 --- a/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts +++ b/frontend/src/app/features/bim/revit_add_in/revit-add-in-settings-button.service.ts @@ -1,5 +1,5 @@ import { Injectable, Injector } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; /* * This service conditionally creates two settings buttons (on the user menu and on @@ -8,10 +8,11 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; @Injectable() export class RevitAddInSettingsButtonService { private readonly labelText:string; + private readonly groupLabelText:string; constructor(readonly injector:Injector, - readonly i18n:I18nService) { + readonly i18n:I18nService) { const onRevitAddInEnvironment = window.navigator.userAgent.search('Revit') > -1; if (onRevitAddInEnvironment) { diff --git a/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts b/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts index 49a32bc40a..196d6c7a2c 100644 --- a/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts +++ b/frontend/src/app/features/bim/revit_add_in/revit-bridge.service.ts @@ -1,12 +1,13 @@ import { Injectable, Injector } from '@angular/core'; -import { Observable, Subject, BehaviorSubject } from "rxjs"; -import { distinctUntilChanged, filter, first, map, mapTo } from "rxjs/operators"; -import { BcfViewpointInterface } from "core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface"; -import { ViewerBridgeService } from "core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { ViewpointsService } from "core-app/features/bim/bcf/helper/viewpoints.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; - +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { + distinctUntilChanged, filter, first, map, +} from 'rxjs/operators'; +import { BcfViewpointInterface } from 'core-app/features/bim/bcf/api/viewpoints/bcf-viewpoint.interface'; +import { ViewerBridgeService } from 'core-app/features/bim/bcf/bcf-viewer-bridge/viewer-bridge.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { ViewpointsService } from 'core-app/features/bim/bcf/helper/viewpoints.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; declare global { interface Window { @@ -17,8 +18,11 @@ declare global { @Injectable() export class RevitBridgeService extends ViewerBridgeService { public shouldShowViewer = false; + public viewerVisible$ = new BehaviorSubject(false); + private revitMessageReceivedSource = new Subject<{ messageType:string, trackingId:string, messagePayload:any }>(); + private trackingIdNumber = 0; @InjectField() viewpointsService:ViewpointsService; @@ -49,8 +53,8 @@ export class RevitBridgeService extends ViewerBridgeService { return this.revitMessageReceived$ .pipe( distinctUntilChanged(), - filter(message => message.messageType === 'ViewpointData' && message.trackingId === trackingId), - first() + filter((message) => message.messageType === 'ViewpointData' && message.trackingId === trackingId), + first(), ) .pipe( map((message) => { @@ -62,14 +66,14 @@ export class RevitBridgeService extends ViewerBridgeService { }; return viewpointJson; - }) + }), ); } public showViewpoint(workPackage:WorkPackageResource, index:number) { this.viewpointsService .getViewPoint$(workPackage, index) - .subscribe((viewpoint:BcfViewpointInterface) => this.sendMessageToRevit('ShowViewpoint', this.newTrackingId(), JSON.stringify(viewpoint))); + .subscribe((viewpoint:BcfViewpointInterface) => this.sendMessageToRevit('ShowViewpoint', this.newTrackingId(), JSON.stringify(viewpoint))); } sendMessageToRevit(messageType:string, trackingId:string, messagePayload?:any) { @@ -84,21 +88,21 @@ export class RevitBridgeService extends ViewerBridgeService { private hookUpRevitListener() { window.RevitBridge.sendMessageToOpenProject = (messageString:string) => { const message = JSON.parse(messageString); - const messageType = message.messageType; - const trackingId = message.trackingId; + const { messageType } = message; + const { trackingId } = message; const messagePayload = JSON.parse(message.messagePayload); this.revitMessageReceivedSource.next({ - messageType: messageType, - trackingId: trackingId, - messagePayload: messagePayload + messageType, + trackingId, + messagePayload, }); }; this.viewerVisible$.next(true); } newTrackingId():string { - this.trackingIdNumber = this.trackingIdNumber + 1; + this.trackingIdNumber += 1; return String(this.trackingIdNumber); } } diff --git a/frontend/src/app/features/boards/add-list-modal/add-list-modal.component.ts b/frontend/src/app/features/boards/add-list-modal/add-list-modal.component.ts index 8594c08858..c726a08f96 100644 --- a/frontend/src/app/features/boards/add-list-modal/add-list-modal.component.ts +++ b/frontend/src/app/features/boards/add-list-modal/add-list-modal.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,35 +26,37 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit } from "@angular/core"; -import { StateService } from "@uirouter/core"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; +import { + ChangeDetectorRef, Component, ElementRef, Inject, OnInit, +} from '@angular/core'; +import { StateService } from '@uirouter/core'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { DebouncedRequestSwitchmap, errorNotificationHandler, -} from "core-app/shared/helpers/rxjs/debounced-input-switchmap"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { Board } from "core-app/features/boards/board/board"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; -import { ValueOption } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +} from 'core-app/shared/helpers/rxjs/debounced-input-switchmap'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { Board } from 'core-app/features/boards/board/board'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; +import { ValueOption } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; @Component({ - templateUrl: './add-list-modal.html' + templateUrl: './add-list-modal.html', }) export class AddListModalComponent extends OpModalComponent implements OnInit { /** Keep a switchmap for search term and loading state */ public requests = new DebouncedRequestSwitchmap( (searchTerm:string) => this.actionService.loadAvailable(this.board, this.active, searchTerm), errorNotificationHandler(this.halNotification), - true + true, ); public showClose:boolean; @@ -102,7 +104,7 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { onCreate: (value:HalResource) => this.onNewActionCreated(value), onOpen: () => this.requests.input$.next(''), onChange: (value:HalResource) => this.onModelChange(value), - onAfterViewInit: (component:CreateAutocompleterComponent) => component.focusInputField() + onAfterViewInit: (component:CreateAutocompleterComponent) => component.focusInputField(), }; /** The loaded available values */ @@ -112,14 +114,13 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { showWarning = false; constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly boardActions:BoardActionsRegistryService, - readonly halNotification:HalResourceNotificationService, - readonly state:StateService, - readonly boardService:BoardService, - readonly I18n:I18nService) { - + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly boardActions:BoardActionsRegistryService, + readonly halNotification:HalResourceNotificationService, + readonly state:StateService, + readonly boardService:BoardService, + readonly I18n:I18nService) { super(locals, cdRef, elementRef); } @@ -130,12 +131,11 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { this.active = new Set(this.locals.active as string[]); this.actionService = this.boardActions.get(this.board.actionAttribute!); - this .requests .output$ .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((values:unknown[]) => { let hasMember = false; @@ -152,7 +152,8 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { .warningTextWhenNoOptionsAvailable(hasMember) .then((text) => { this.warningText = text; - }); + }) + .catch(() => {}); this.availableValues = values; this.showWarning = this.requests.lastRequestedValue !== undefined && (values.length === 0); this.cdRef.detectChanges(); @@ -170,13 +171,13 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { this.inFlight = true; this.actionService .addColumnWithActionAttribute(this.board, this.selectedAttribute!) - .then(board => this.boardService.save(board).toPromise()) + .then((board) => this.boardService.save(board).toPromise()) .then((board) => { this.inFlight = false; this.closeMe(); this.state.go('boards.partitioned.show', { board_id: board.id, isNew: true }); }) - .catch(() => this.inFlight = false); + .catch(() => (this.inFlight = false)); } onNewActionCreated(newValue:HalResource) { @@ -188,4 +189,3 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { return this.actionService.autocompleterComponent(); } } - diff --git a/frontend/src/app/features/boards/board-constants.const.ts b/frontend/src/app/features/boards/board-constants.const.ts index 388d8ea5dc..e3024a9471 100644 --- a/frontend/src/app/features/boards/board-constants.const.ts +++ b/frontend/src/app/features/boards/board-constants.const.ts @@ -1 +1 @@ -export const boardTeaserVideoURL = "https://player.vimeo.com/video/337280106"; +export const boardTeaserVideoURL = 'https://player.vimeo.com/video/337280106'; diff --git a/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts b/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts index 46c7be540e..5b2fab28a1 100644 --- a/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts +++ b/frontend/src/app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,32 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Directive, ElementRef, Injector } from '@angular/core'; +import { + ChangeDetectorRef, Directive, ElementRef, Injector, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { BoardListComponent } from "core-app/features/boards/board/board-list/board-list.component"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { BoardListComponent } from 'core-app/features/boards/board/board-list/board-list.component'; @Directive({ - selector: '[addCardDropdown]' + selector: '[op-addCardDropdown]', }) export class AddCardDropdownMenuDirective extends OpContextMenuTrigger { - - private focusAfterClose = true; - constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly authorisationService:AuthorisationService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly boardList:BoardListComponent, - readonly injector:Injector, - readonly querySpace:IsolatedQuerySpace, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { - + readonly opContextMenu:OPContextMenuService, + readonly opModalService:OpModalService, + readonly authorisationService:AuthorisationService, + readonly wpInlineCreate:WorkPackageInlineCreateService, + readonly boardList:BoardListComponent, + readonly injector:Injector, + readonly querySpace:IsolatedQuerySpace, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService) { super(elementRef, opContextMenu); } @@ -70,7 +68,7 @@ export class AddCardDropdownMenuDirective extends OpContextMenuTrigger { public positionArgs(evt:JQuery.TriggeredEvent) { const additionalPositionArgs = { my: 'left top', - at: 'left bottom' + at: 'left bottom', }; const position = super.positionArgs(evt); @@ -87,7 +85,7 @@ export class AddCardDropdownMenuDirective extends OpContextMenuTrigger { onClick: () => { this.boardList.addNewCard(); return true; - } + }, }, { disabled: !this.wpInlineCreate.canReference, @@ -95,8 +93,8 @@ export class AddCardDropdownMenuDirective extends OpContextMenuTrigger { onClick: () => { this.boardList.addReferenceCard(); return true; - } - } + }, + }, ]; } } diff --git a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts index e115a20030..4112830fcf 100644 --- a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts +++ b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,42 +26,43 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from "@angular/core"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Board } from "core-app/features/boards/board/board"; -import { StateService } from "@uirouter/core"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { tap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { Observable } from "rxjs"; -import { OpAutocompleterComponent } from "core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { + ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild, +} from '@angular/core'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { StateService } from '@uirouter/core'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { tap } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { Observable } from 'rxjs'; +import { OpAutocompleterComponent } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ - templateUrl: './add-list-modal.html' + templateUrl: './add-list-modal.html', }) export class AddListModalComponent extends OpModalComponent implements OnInit { - @ViewChild(OpAutocompleterComponent, { static: true }) public ngSelectComponent:OpAutocompleterComponent; getAutocompleterData = (searchTerm:string):Observable => { - // Remove prefix # from search searchTerm = searchTerm.replace(/^#/, ''); - return this.actionService.loadAvailable(this.board, this.active, searchTerm).pipe(tap((values) => this.warnIfNoOptions(values))); + return this.actionService.loadAvailable(this.board, this.active, searchTerm) + .pipe(tap((values) => (this.warnIfNoOptions(values)))); }; public autocompleterOptions = { - resource:"", - getOptionsFn: this.getAutocompleterData + resource: '', + getOptionsFn: this.getAutocompleterData, }; public showClose:boolean; @@ -110,16 +111,15 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { showWarning = false; constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly boardActions:BoardActionsRegistryService, - readonly halNotification:HalResourceNotificationService, - readonly state:StateService, - readonly boardService:BoardService, - readonly I18n:I18nService, - readonly apiV3Service:APIV3Service, - readonly currentProject:CurrentProjectService) { - + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly boardActions:BoardActionsRegistryService, + readonly halNotification:HalResourceNotificationService, + readonly state:StateService, + readonly boardService:BoardService, + readonly I18n:I18nService, + readonly apiV3Service:APIV3Service, + readonly currentProject:CurrentProjectService) { super(locals, cdRef, elementRef); } @@ -128,7 +128,7 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { this.board = this.locals.board; this.active = new Set(this.locals.active as string[]); this.actionService = this.boardActions.get(this.board.actionAttribute!); - this.autocompleterOptions.resource=this.actionService.localizedName.toLowerCase(); + this.autocompleterOptions.resource = this.actionService.localizedName.toLowerCase(); } onModelChange(element:HalResource) { @@ -139,13 +139,13 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { this.inFlight = true; this.actionService .addColumnWithActionAttribute(this.board, this.selectedAttribute!) - .then(board => this.boardService.save(board).toPromise()) + .then((board) => this.boardService.save(board).toPromise()) .then((board) => { this.inFlight = false; this.closeMe(); this.state.go('boards.partitioned.show', { board_id: board.id, isNew: true }); }) - .catch(() => this.inFlight = false); + .catch(() => (this.inFlight = false)); } onNewActionCreated() { @@ -154,21 +154,24 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { .versions .post(this.getVersionPayload(this.ngSelectComponent.ngSelectInstance.searchTerm)) .subscribe( - version => { this.selectedAttribute = version; - this.create();}, - error => { + (version) => { + this.selectedAttribute = version; + this.create(); + }, + (error) => { this.ngSelectComponent.closeSelect(); this.halNotification.handleRawError(error); - }); - + }, + ); } + private getVersionPayload(name:string) { const payload:any = {}; - payload['name'] = name; - payload['_links'] = { + payload.name = name; + payload._links = { definingProject: { - href: this.apiV3Service.projects.id(this.currentProject.id!).path - } + href: this.apiV3Service.projects.id(this.currentProject.id!).path, + }, }; return payload; @@ -189,9 +192,9 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { .warningTextWhenNoOptionsAvailable(hasMember) .then((text) => { this.warningText = text; - }); + }) + .catch(() => {}); this.showWarning = this.ngSelectComponent.ngSelectInstance.searchTerm !== undefined && (values.length === 0); this.cdRef.detectChanges(); } } - diff --git a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-action.service.ts b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-action.service.ts index 4b84800ac8..28745670e9 100644 --- a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-action.service.ts @@ -1,14 +1,14 @@ -import { Injectable } from "@angular/core"; -import { AssigneeBoardHeaderComponent } from "core-app/features/boards/board/board-actions/assignee/assignee-board-header.component"; -import { CachedBoardActionService } from "core-app/features/boards/board/board-actions/cached-board-action.service"; -import { Board } from "core-app/features/boards/board/board"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { Injectable } from '@angular/core'; +import { AssigneeBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/assignee/assignee-board-header.component'; +import { CachedBoardActionService } from 'core-app/features/boards/board/board-actions/cached-board-action.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; @Injectable() export class BoardAssigneeActionService extends CachedBoardActionService { @@ -22,12 +22,12 @@ export class BoardAssigneeActionService extends CachedBoardActionService { icon = 'icon-user'; - image = ImageHelpers.imagePath('board_creation_modal/assignees.svg'); + image = imagePath('board_creation_modal/assignees.svg'); readonly unassignedUser:any = { id: null, href: null, - name: this.I18n.t('js.filter.noneElement') + name: this.I18n.t('js.filter.noneElement'), }; /** @@ -44,15 +44,15 @@ export class BoardAssigneeActionService extends CachedBoardActionService { filter = { assignee: { operator: '!*', - values: [] - } + values: [], + }, }; } else { filter = { assignee: { operator: '=', - values: [value.idFromLink] - } + values: [value.idFromLink], + }, }; } @@ -82,9 +82,9 @@ export class BoardAssigneeActionService extends CachedBoardActionService { } public warningTextWhenNoOptionsAvailable(hasMember?:boolean) { - let text = hasMember ? - this.I18n.t('js.boards.add_list_modal.warning.assignee'): - this.I18n.t('js.boards.add_list_modal.warning.no_member'); + let text = hasMember + ? this.I18n.t('js.boards.add_list_modal.warning.assignee') + : this.I18n.t('js.boards.add_list_modal.warning.no_member'); return this .apiV3Service @@ -96,8 +96,8 @@ export class BoardAssigneeActionService extends CachedBoardActionService { if (project.memberships) { text = text.concat( this.I18n.t('js.boards.add_list_modal.warning.add_members', { - link: this.pathHelper.projectMembershipsPath(this.currentProject.identifier!) - }) + link: this.pathHelper.projectMembershipsPath(this.currentProject.identifier!), + }), ); } @@ -114,7 +114,7 @@ export class BoardAssigneeActionService extends CachedBoardActionService { .get() .toPromise() .then( - (collection:CollectionResource) => [this.unassignedUser].concat(collection.elements) + (collection:CollectionResource) => [this.unassignedUser].concat(collection.elements), ); } } diff --git a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts index d59d8f785a..74ff343079 100644 --- a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,25 +25,24 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; - +import { Component, Input } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; @Component({ templateUrl: './assignee-board-header.html', styleUrls: ['./assignee-board-header.sass'], - host: { 'class': 'title-container -small' } + host: { class: 'title-container -small' }, }) export class AssigneeBoardHeaderComponent { @Input('resource') public user:UserResource; text = { - assignee: this.I18n.t('js.work_packages.properties.assignee') + assignee: this.I18n.t('js.work_packages.properties.assignee'), }; constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { } } diff --git a/frontend/src/app/features/boards/board/board-actions/board-action.service.ts b/frontend/src/app/features/boards/board/board-actions/board-action.service.ts index 9588066f3c..4d5b2eab33 100644 --- a/frontend/src/app/features/boards/board/board-actions/board-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/board-action.service.ts @@ -1,38 +1,37 @@ -import { Board } from "core-app/features/boards/board/board"; -import { ComponentType } from "@angular/cdk/portal"; -import { OpContextMenuItem } from "core-app/shared/components/op-context-menu/op-context-menu.types"; -import { DisabledButtonPlaceholder } from "core-app/features/boards/board/board-list/board-list.component"; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; -import { BoardListsService } from "core-app/features/boards/board/board-list/board-lists.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { Injectable, Injector } from "@angular/core"; -import { map } from "rxjs/operators"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageFilterValues } from "core-app/features/work-packages/components/wp-edit-form/work-package-filter-values"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { Observable } from "rxjs"; -import { FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Board } from 'core-app/features/boards/board/board'; +import { ComponentType } from '@angular/cdk/portal'; +import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; +import { DisabledButtonPlaceholder } from 'core-app/features/boards/board/board-list/board-list.component'; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; +import { BoardListsService } from 'core-app/features/boards/board/board-list/board-lists.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { Injectable, Injector } from '@angular/core'; +import { map } from 'rxjs/operators'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageFilterValues } from 'core-app/features/work-packages/components/wp-edit-form/work-package-filter-values'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { Observable } from 'rxjs'; +import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export abstract class BoardActionService { - constructor(readonly injector:Injector, - protected boardListsService:BoardListsService, - protected I18n:I18nService, - protected halResourceService:HalResourceService, - protected pathHelper:PathHelperService, - protected currentProject:CurrentProjectService, - protected apiV3Service:APIV3Service, - protected schemaCache:SchemaCacheService) { + protected boardListsService:BoardListsService, + protected I18n:I18nService, + protected halResourceService:HalResourceService, + protected pathHelper:PathHelperService, + protected currentProject:CurrentProjectService, + protected apiV3Service:APIV3Service, + protected schemaCache:SchemaCacheService) { } /** @@ -75,7 +74,7 @@ export abstract class BoardActionService { * @param query */ getActionFilter(query:QueryResource, getHref = false):QueryFilterInstanceResource|undefined { - return query.filters.find(filter => filter.id === this.filterName); + return query.filters.find((filter) => filter.id === this.filterName); } /** @@ -89,7 +88,7 @@ export abstract class BoardActionService { return; } - const value = filter.values[0] as string|HalResource; + const value = filter.values[0]; if (value instanceof HalResource) { return getHref ? value.href! : value.id!; @@ -98,7 +97,6 @@ export abstract class BoardActionService { return value; } - /** * Returns the current filter value if any * @param query @@ -134,8 +132,8 @@ export abstract class BoardActionService { const filter = { [this.filterName]: { operator: '=' as FilterOperator, - values: [value.idFromLink] - } + values: [value.idFromLink], + }, }; return this.boardListsService.addQuery(board, params, [filter]); @@ -152,7 +150,7 @@ export abstract class BoardActionService { return this .loadValues(matching) .pipe( - map(items => items.filter(item => !active.has(item.id!))) + map((items) => items.filter((item) => !active.has(item.id!))), ); } @@ -231,7 +229,7 @@ export abstract class BoardActionService { if (!changeset.isWritable(this.filterName)) { throw new Error(this.I18n.t( 'js.boards.error_attribute_not_writable', - { attribute: changeset.humanName(this.filterName) } + { attribute: changeset.humanName(this.filterName) }, )); } @@ -255,4 +253,3 @@ export abstract class BoardActionService { */ protected abstract loadValues(matching?:string):Observable; } - diff --git a/frontend/src/app/features/boards/board/board-actions/board-actions-registry.service.ts b/frontend/src/app/features/boards/board/board-actions/board-actions-registry.service.ts index a998d9acf2..095fa61588 100644 --- a/frontend/src/app/features/boards/board/board-actions/board-actions-registry.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/board-actions-registry.service.ts @@ -1,9 +1,8 @@ -import { Injectable } from "@angular/core"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; +import { Injectable } from '@angular/core'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; @Injectable({ providedIn: 'root' }) export class BoardActionsRegistryService { - private mapping:{ [attribute:string]:BoardActionService } = {}; public add(attribute:string, service:BoardActionService) { @@ -11,9 +10,9 @@ export class BoardActionsRegistryService { } public available() { - return _.map(this.mapping, (service:BoardActionService, attribute:string) => { - return { attribute: attribute, text: service.localizedName, icon:'', description:'', image:'' }; - }); + return _.map(this.mapping, (service:BoardActionService, attribute:string) => ({ + attribute, text: service.localizedName, icon: '', description: '', image: '', + })); } public get(attribute:string):BoardActionService { @@ -21,6 +20,6 @@ export class BoardActionsRegistryService { return this.mapping[attribute]; } - throw(`No action service exists for ${attribute}`); + throw new Error(`No action service exists for ${attribute}`); } } diff --git a/frontend/src/app/features/boards/board/board-actions/cached-board-action.service.ts b/frontend/src/app/features/boards/board/board-actions/cached-board-action.service.ts index fd8260f024..b717d511b0 100644 --- a/frontend/src/app/features/boards/board/board-actions/cached-board-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/cached-board-action.service.ts @@ -1,10 +1,10 @@ -import { Injectable } from "@angular/core"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { input } from "reactivestates"; -import { Observable } from "rxjs"; -import { map, take } from "rxjs/operators"; -import { Board } from "core-app/features/boards/board/board"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; +import { input } from 'reactivestates'; +import { Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { Board } from 'core-app/features/boards/board/board'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export abstract class CachedBoardActionService extends BoardActionService { @@ -19,16 +19,13 @@ export abstract class CachedBoardActionService extends BoardActionService { .cache .values$() .pipe( - map(results => { + map((results) => { if (matching) { - return results.filter(resource => - new RegExp(matching, 'i').test(resource.name) - ); - } else { - return results; + return results.filter((resource) => new RegExp(matching, 'i').test(resource.name)); } + return results; }), - take(1) + take(1), ); } @@ -51,14 +48,11 @@ export abstract class CachedBoardActionService extends BoardActionService { .cache .values$() .pipe( - take(1) + take(1), ) .toPromise() - .then(results => { - return results.find(resource => resource.id === id)!; - }); + .then((results) => results.find((resource) => resource.id === id)!); } protected abstract loadUncached():Promise; } - diff --git a/frontend/src/app/features/boards/board/board-actions/status/status-action.service.ts b/frontend/src/app/features/boards/board/board-actions/status/status-action.service.ts index af29f59407..bd1888df7b 100644 --- a/frontend/src/app/features/boards/board/board-actions/status/status-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/status/status-action.service.ts @@ -1,16 +1,15 @@ -import { Injectable } from "@angular/core"; -import { Board } from "core-app/features/boards/board/board"; -import { StatusResource } from "core-app/features/hal/resources/status-resource"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { CachedBoardActionService } from "core-app/features/boards/board/board-actions/cached-board-action.service"; -import { StatusBoardHeaderComponent } from "core-app/features/boards/board/board-actions/status/status-board-header.component"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; +import { Injectable } from '@angular/core'; +import { Board } from 'core-app/features/boards/board/board'; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; +import { CachedBoardActionService } from 'core-app/features/boards/board/board-actions/cached-board-action.service'; +import { StatusBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/status/status-board-header.component'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; @Injectable() export class BoardStatusActionService extends CachedBoardActionService { filterName = 'status'; - text = this.I18n.t('js.boards.board_type.board_type_title.status'); + text = this.I18n.t('js.boards.board_type.board_type_title.status'); description = this.I18n.t('js.boards.board_type.action_text_status'); @@ -18,7 +17,7 @@ export class BoardStatusActionService extends CachedBoardActionService { icon = 'icon-workflow'; - image = ImageHelpers.imagePath('board_creation_modal/status.svg'); + image = imagePath('board_creation_modal/status.svg'); localizedName = this.I18n.t('js.work_packages.properties.status'); @@ -30,19 +29,16 @@ export class BoardStatusActionService extends CachedBoardActionService { return this .loadValues() .toPromise() - .then((results) => - Promise.all( - results.map((status:StatusResource) => { - - if (status.isDefault) { - return this.addColumnWithActionAttribute(board, status); - } - - return Promise.resolve(board); - }) - ) - .then(() => board) - ); + .then((results) => Promise.all( + results.map((status:StatusResource) => { + if (status.isDefault) { + return this.addColumnWithActionAttribute(board, status); + } + + return Promise.resolve(board); + }), + ) + .then(() => board)); } public warningTextWhenNoOptionsAvailable() { @@ -55,6 +51,6 @@ export class BoardStatusActionService extends CachedBoardActionService { .statuses .get() .toPromise() - .then(collection => collection.elements); + .then((collection) => collection.elements); } } diff --git a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts index 95d049dd2a..f61e57cbbf 100644 --- a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,21 +25,20 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { StatusResource } from "core-app/features/hal/resources/status-resource"; - +import { Component, Input } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; @Component({ templateUrl: './status-board-header.html', styleUrls: ['./status-board-header.sass'], - host: { 'class': 'title-container -small' } + host: { class: 'title-container -small' }, }) export class StatusBoardHeaderComponent { @Input('resource') public status:StatusResource; text = { - status: this.I18n.t('js.work_packages.properties.status') + status: this.I18n.t('js.work_packages.properties.status'), }; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-action.service.ts b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-action.service.ts index 3544eef950..8a9baecc11 100644 --- a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-action.service.ts @@ -1,20 +1,20 @@ -import { Injectable } from "@angular/core"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { SubprojectBoardHeaderComponent } from "core-app/features/boards/board/board-actions/subproject/subproject-board-header.component"; -import { CachedBoardActionService } from "core-app/features/boards/board/board-actions/cached-board-action.service"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { Injectable } from '@angular/core'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { SubprojectBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/subproject/subproject-board-header.component'; +import { CachedBoardActionService } from 'core-app/features/boards/board/board-actions/cached-board-action.service'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; @Injectable() export class BoardSubprojectActionService extends CachedBoardActionService { filterName = 'onlySubproject'; - text = this.I18n.t('js.boards.board_type.board_type_title.subproject'); + text = this.I18n.t('js.boards.board_type.board_type_title.subproject'); description = this.I18n.t('js.boards.board_type.action_text_subprojects'); @@ -22,7 +22,7 @@ export class BoardSubprojectActionService extends CachedBoardActionService { icon = 'icon-projects'; - image = ImageHelpers.imagePath('board_creation_modal/subproject.svg'); + image = imagePath('board_creation_modal/subproject.svg'); localizedName = this.I18n.t('js.work_packages.properties.subproject'); @@ -38,7 +38,7 @@ export class BoardSubprojectActionService extends CachedBoardActionService { assignToWorkPackage(changeset:WorkPackageChangeset, query:QueryResource) { const href = this.getActionValueId(query, true); - changeset.setValue('project', { href: href }); + changeset.setValue('project', { href }); } protected loadUncached():Promise { @@ -49,7 +49,7 @@ export class BoardSubprojectActionService extends CachedBoardActionService { .filtered( new ApiV3FilterBuilder() .add('ancestor', '=', [currentProjectId]) - .add('active', '=', true) + .add('active', '=', true), ) .get() .toPromise() diff --git a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts index fa86c4e5c7..41f7808c6e 100644 --- a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,25 +25,24 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; - +import { Component, Input } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ templateUrl: './subproject-board-header.html', styleUrls: ['./subproject-board-header.sass'], - host: { 'class': 'title-container -small' } + host: { class: 'title-container -small' }, }) export class SubprojectBoardHeaderComponent { @Input() public resource:HalResource; text = { - project: this.I18n.t('js.time_entry.project') + project: this.I18n.t('js.time_entry.project'), }; constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { } } diff --git a/frontend/src/app/features/boards/board/board-actions/subtasks/board-subtasks-action.service.ts b/frontend/src/app/features/boards/board/board-actions/subtasks/board-subtasks-action.service.ts index a613060c78..a5b28f0cc1 100644 --- a/frontend/src/app/features/boards/board/board-actions/subtasks/board-subtasks-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/subtasks/board-subtasks-action.service.ts @@ -1,14 +1,14 @@ -import { Injectable } from "@angular/core"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Observable } from "rxjs"; -import { map } from "rxjs/operators"; -import { SubtasksBoardHeaderComponent } from "core-app/features/boards/board/board-actions/subtasks/subtasks-board-header.component"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { SubtasksBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/subtasks/subtasks-board-header.component'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export class BoardSubtasksActionService extends BoardActionService { @@ -22,7 +22,7 @@ export class BoardSubtasksActionService extends BoardActionService { icon = 'icon-hierarchy'; - image = ImageHelpers.imagePath('board_creation_modal/parent-child.svg'); + image = imagePath('board_creation_modal/parent-child.svg'); localizedName = this.I18n.t('js.boards.board_type.action_type.subtasks'); @@ -48,7 +48,7 @@ export class BoardSubtasksActionService extends BoardActionService { protected loadValues(matching?:string):Observable { const filters = new ApiV3FilterBuilder(); filters.add('is_milestone', '=', false); - filters.add('project', '=', [this.currentProject.id]); + filters.add('project', '=', [this.currentProject.id || '']); if (matching) { filters.add('subjectOrId', '**', [matching]); @@ -60,7 +60,7 @@ export class BoardSubtasksActionService extends BoardActionService { .filtered(filters) .get() .pipe( - map(collection => collection.elements) + map((collection) => collection.elements), ); } diff --git a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts index 6f562b9663..f3f37ee43b 100644 --- a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,29 +25,28 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input, OnInit } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; - +import { Component, Input, OnInit } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; @Component({ templateUrl: './subtasks-board-header.html', styleUrls: ['./subtasks-board-header.sass'], - host: { 'class': 'title-container -small' } + host: { class: 'title-container -small' }, }) export class SubtasksBoardHeaderComponent implements OnInit { @Input() public resource:WorkPackageResource; text = { - workPackage: this.I18n.t('js.label_work_package_parent') + workPackage: this.I18n.t('js.label_work_package_parent'), }; typeHighlightingClass:string; constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { } ngOnInit() { diff --git a/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts b/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts index 5cba3b6fee..91ca01f514 100644 --- a/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts @@ -1,22 +1,23 @@ -import { Injectable } from "@angular/core"; -import { Board } from "core-app/features/boards/board/board"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { OpContextMenuItem } from "core-app/shared/components/op-context-menu/op-context-menu.types"; -import { LinkHandling } from "core-app/shared/helpers/link-handling/link-handling"; -import { StateService } from "@uirouter/core"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { VersionBoardHeaderComponent } from "core-app/features/boards/board/board-actions/version/version-board-header.component"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { CachedBoardActionService } from "core-app/features/boards/board/board-actions/cached-board-action.service"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import { VersionAutocompleterComponent } from "core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Injectable } from '@angular/core'; +import { Board } from 'core-app/features/boards/board/board'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; +import { StateService } from '@uirouter/core'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { VersionBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/version/version-board-header.component'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { CachedBoardActionService } from 'core-app/features/boards/board/board-actions/cached-board-action.service'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; +import { VersionAutocompleterComponent } from 'core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export class BoardVersionActionService extends CachedBoardActionService { @InjectField() state:StateService; + @InjectField() halNotification:HalResourceNotificationService; filterName = 'version'; @@ -29,7 +30,7 @@ export class BoardVersionActionService extends CachedBoardActionService { icon = 'icon-getting-started'; - image = ImageHelpers.imagePath('board_creation_modal/version.svg'); + image = imagePath('board_creation_modal/version.svg'); private writable$:Promise; @@ -54,19 +55,17 @@ export class BoardVersionActionService extends CachedBoardActionService { return this .loadValues() .toPromise() - .then((results) => { - return Promise.all( - results.map((version:VersionResource) => { - const definingName = _.get(version, 'definingProject.name', null); - if (version.isOpen() && definingName && definingName === this.currentProject.name) { - return this.addColumnWithActionAttribute(board, version); - } - - return Promise.resolve(board); - }) - ) - .then(() => board); - }); + .then((results) => Promise.all( + results.map((version:VersionResource) => { + const definingName = _.get(version, 'definingProject.name', null); + if (version.isOpen() && definingName && definingName === this.currentProject.name) { + return this.addColumnWithActionAttribute(board, version); + } + + return Promise.resolve(board); + }), + ) + .then(() => board)); } /** @@ -80,9 +79,8 @@ export class BoardVersionActionService extends CachedBoardActionService { .then((version:VersionResource) => { if (version) { return this.buildItemsForVersion(version); - } else { - return []; } + return []; }); } @@ -97,11 +95,10 @@ export class BoardVersionActionService extends CachedBoardActionService { public disabledAddButtonPlaceholder(version:VersionResource) { if (version.isLocked()) { return { icon: 'locked', text: this.I18n.t('js.boards.version.locked') }; - } else if (version.isClosed()) { + } if (version.isClosed()) { return { icon: 'not-supported', text: this.I18n.t('js.boards.version.closed') }; - } else { - return undefined; } + return undefined; } public dragIntoAllowed(query:QueryResource, value:HalResource|undefined) { @@ -116,11 +113,11 @@ export class BoardVersionActionService extends CachedBoardActionService { return this .apiV3Service .projects - .id(this.currentProject.id!) + .id(this.currentProject.id) .versions .get() .toPromise() - .then(collection => collection.elements); + .then((collection) => collection.elements); } private patchVersionStatus(version:VersionResource, newStatus:'open'|'closed'|'locked') { @@ -129,10 +126,10 @@ export class BoardVersionActionService extends CachedBoardActionService { .id(version) .patch({ status: newStatus }) .subscribe( - version => { + (version) => { this.state.go('.', {}, { reload: true }); }, - error => this.halNotification.handleRawError(error) + (error) => this.halNotification.handleRawError(error), ); } @@ -146,7 +143,7 @@ export class BoardVersionActionService extends CachedBoardActionService { onClick: () => { this.patchVersionStatus(version, 'locked'); return true; - } + }, }, { // Unlock version @@ -155,7 +152,7 @@ export class BoardVersionActionService extends CachedBoardActionService { onClick: () => { this.patchVersionStatus(version, 'open'); return true; - } + }, }, { // Close version @@ -164,7 +161,7 @@ export class BoardVersionActionService extends CachedBoardActionService { onClick: () => { this.patchVersionStatus(version, 'closed'); return true; - } + }, }, { // Open version @@ -173,20 +170,20 @@ export class BoardVersionActionService extends CachedBoardActionService { onClick: () => { this.patchVersionStatus(version, 'open'); return true; - } + }, }, { // Show link linkText: this.I18n.t('js.boards.version.show_version'), href: this.pathHelper.versionShowPath(id), onClick: (evt:JQuery.TriggeredEvent) => { - if (!LinkHandling.isClickedWithModifier(evt)) { + if (!isClickedWithModifier(evt)) { window.open(this.pathHelper.versionShowPath(id), '_blank'); return true; } return false; - } + }, }, { // Edit link @@ -194,14 +191,14 @@ export class BoardVersionActionService extends CachedBoardActionService { linkText: this.I18n.t('js.boards.version.edit_version'), href: this.pathHelper.versionEditPath(id), onClick: (evt:JQuery.TriggeredEvent) => { - if (!LinkHandling.isClickedWithModifier(evt)) { + if (!isClickedWithModifier(evt)) { window.open(this.pathHelper.versionEditPath(id), '_blank'); return true; } return false; - } - } + }, + }, ]; } } diff --git a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts index 148ae81bb1..750ce8daf7 100644 --- a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,27 +25,26 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input } from "@angular/core"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; - +import { Component, Input } from '@angular/core'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Component({ templateUrl: './version-board-header.html', styleUrls: ['./version-board-header.sass'], - host: { 'class': 'title-container -small' } + host: { class: 'title-container -small' }, }) export class VersionBoardHeaderComponent { @Input('resource') public version:VersionResource; constructor(readonly I18n:I18nService, - readonly pathHelper:PathHelperService) { + readonly pathHelper:PathHelperService) { } public text = { isLocked: this.I18n.t('js.boards.version.is_locked'), isClosed: this.I18n.t('js.boards.version.is_closed'), - version: this.I18n.t('js.work_packages.properties.version') + version: this.I18n.t('js.work_packages.properties.version'), }; } diff --git a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts index 3f9d2589f4..c8f9116921 100644 --- a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts +++ b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts @@ -1,22 +1,22 @@ -import { AfterViewInit, Component, Input } from "@angular/core"; -import { Board } from "core-app/features/boards/board/board"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { WorkPackageStatesInitializationService } from "core-app/features/work-packages/components/wp-list/wp-states-initialization.service"; +import { AfterViewInit, Component, Input } from '@angular/core'; +import { Board } from 'core-app/features/boards/board/board'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { StateService } from "@uirouter/core"; -import { debounceTime, skip, take } from "rxjs/operators"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { Observable } from "rxjs"; -import { BoardFiltersService } from "core-app/features/boards/board/board-filter/board-filters.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { StateService } from '@uirouter/core'; +import { debounceTime, skip, take } from 'rxjs/operators'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { Observable } from 'rxjs'; +import { BoardFiltersService } from 'core-app/features/boards/board/board-filter/board-filters.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ selector: 'board-filter', - templateUrl: './board-filter.component.html' + templateUrl: './board-filter.component.html', }) export class BoardFilterComponent extends UntilDestroyedMixin implements AfterViewInit { /** Current active */ @@ -25,14 +25,14 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi initialized = false; constructor(private readonly currentProjectService:CurrentProjectService, - private readonly querySpace:IsolatedQuerySpace, - private readonly apiV3Service:APIV3Service, - private readonly halResourceService:HalResourceService, - private readonly wpStatesInitialization:WorkPackageStatesInitializationService, - private readonly wpTableFilters:WorkPackageViewFiltersService, - private readonly urlParamsHelper:UrlParamsHelperService, - private readonly boardFilters:BoardFiltersService, - private readonly $state:StateService) { + private readonly querySpace:IsolatedQuerySpace, + private readonly apiV3Service:APIV3Service, + private readonly halResourceService:HalResourceService, + private readonly wpStatesInitialization:WorkPackageStatesInitializationService, + private readonly wpTableFilters:WorkPackageViewFiltersService, + private readonly urlParamsHelper:UrlParamsHelperService, + private readonly boardFilters:BoardFiltersService, + private readonly $state:StateService) { super(); } @@ -43,7 +43,7 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi this.board$ .pipe(take(1)) - .subscribe(board => { + .subscribe((board) => { // Initially load the form once to be able to render filters this.loadQueryForm(); @@ -63,17 +63,16 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi .pipe( this.untilDestroyed(), skip(1), - debounceTime(250) + debounceTime(250), ) .subscribe(() => { - const filters:QueryFilterInstanceResource[] = this.wpTableFilters.current; const filterHash = this.urlParamsHelper.buildV3GetFilters(filters); const query_props = JSON.stringify(filterHash); this.boardFilters.filters.putValue(filterHash); - this.$state.go('.', { query_props: query_props }, { custom: { notify: false } }); + this.$state.go('.', { query_props }, { custom: { notify: false } }); }); } @@ -85,7 +84,7 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi .loadWithParams( { filters: JSON.stringify(this.boardFilters.current) }, undefined, - this.currentProjectService.id + this.currentProjectService.id, ) .subscribe(([form, query]) => { this.querySpace.query.putValue(query); diff --git a/frontend/src/app/features/boards/board/board-filter/board-filters.service.ts b/frontend/src/app/features/boards/board/board-filter/board-filters.service.ts index 18835611ff..8f2501ed9e 100644 --- a/frontend/src/app/features/boards/board/board-filter/board-filters.service.ts +++ b/frontend/src/app/features/boards/board/board-filter/board-filters.service.ts @@ -1,6 +1,6 @@ -import { input } from "reactivestates"; -import { Injectable } from "@angular/core"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { input } from 'reactivestates'; +import { Injectable } from '@angular/core'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; @Injectable() export class BoardFiltersService { @@ -15,4 +15,4 @@ export class BoardFiltersService { get current():ApiV3Filter[] { return this.filters.getValueOr([]); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts b/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts index 3857452ce0..6667e26d4a 100644 --- a/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts +++ b/frontend/src/app/features/boards/board/board-list/board-inline-create.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,24 +27,23 @@ //++ import { Injectable, Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { BoardInlineAddAutocompleterComponent } from "core-app/features/boards/board/inline-add/board-inline-add-autocompleter.component"; -import { GonService } from "core-app/core/gon/gon.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { BoardInlineAddAutocompleterComponent } from 'core-app/features/boards/board/inline-add/board-inline-add-autocompleter.component'; +import { GonService } from 'core-app/core/gon/gon.service'; @Injectable() export class BoardInlineCreateService extends WorkPackageInlineCreateService { - constructor(readonly injector:Injector, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly halResourceService:HalResourceService, - protected readonly pathHelperService:PathHelperService, - protected readonly Gon:GonService, - protected readonly wpRelationsHierarchyService:WorkPackageRelationsHierarchyService) { + protected readonly querySpace:IsolatedQuerySpace, + protected readonly halResourceService:HalResourceService, + protected readonly pathHelperService:PathHelperService, + protected readonly Gon:GonService, + protected readonly wpRelationsHierarchyService:WorkPackageRelationsHierarchyService) { super(injector); } @@ -71,6 +70,6 @@ export class BoardInlineCreateService extends WorkPackageInlineCreateService { */ public readonly buttonTexts = { reference: this.I18n.t('js.relation_buttons.add_existing_child'), - create: this.I18n.t('js.relation_buttons.add_new_child') + create: this.I18n.t('js.relation_buttons.add_new_child'), }; } diff --git a/frontend/src/app/features/boards/board/board-list/board-list-cross-selection.service.ts b/frontend/src/app/features/boards/board/board-list/board-list-cross-selection.service.ts index af94fb280c..b1eb708525 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list-cross-selection.service.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list-cross-selection.service.ts @@ -1,6 +1,6 @@ -import { Observable, Subject } from "rxjs"; -import { filter } from "rxjs/operators"; -import { Injectable } from "@angular/core"; +import { Observable, Subject } from 'rxjs'; +import { filter } from 'rxjs/operators'; +import { Injectable } from '@angular/core'; export interface BoardSelection { /** The query that the selection happened in */ @@ -13,14 +13,12 @@ export interface BoardSelection { allSelected:string[]; } - /** * Responsible for keeping selected items across all lists of a board, * selections in one list will propagate to other lists as well. */ @Injectable() export class BoardListCrossSelectionService { - private selections$ = new Subject(); /** @@ -44,11 +42,11 @@ export class BoardListCrossSelectionService { return this .selections$ .pipe( - filter(selection => selection.withinQuery !== id) + filter((selection) => selection.withinQuery !== id), ); } selections():Observable { return this.selections$; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts index 3980a8311e..374c058fe6 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Output, EventEmitter, Input } from '@angular/core'; +import { + Component, EventEmitter, Input, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { Board } from "core-app/features/boards/board/board"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { OpContextMenuItem } from "core-app/shared/components/op-context-menu/op-context-menu.types"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; +import { Board } from 'core-app/features/boards/board/board'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; @Component({ selector: 'board-list-menu', @@ -43,14 +45,15 @@ import { BoardActionService } from "core-app/features/boards/board/board-actions }) export class BoardListMenuComponent { @Input() board:Board; + @Output() onRemove = new EventEmitter(); constructor(readonly opModalService:OpModalService, - readonly authorisationService:AuthorisationService, - private readonly querySpace:IsolatedQuerySpace, - private readonly boardService:BoardService, - private readonly boardActionRegistry:BoardActionsRegistryService, - readonly I18n:I18nService) { + readonly authorisationService:AuthorisationService, + private readonly querySpace:IsolatedQuerySpace, + private readonly boardService:BoardService, + private readonly boardActionRegistry:BoardActionsRegistryService, + readonly I18n:I18nService) { } public get menuItems() { @@ -62,8 +65,8 @@ export class BoardListMenuComponent { onClick: () => { this.onRemove.emit(); return true; - } - } + }, + }, ]; // Add action specific menu entries diff --git a/frontend/src/app/features/boards/board/board-list/board-list.component.html b/frontend/src/app/features/boards/board/board-list/board-list.component.html index aaf159c84a..14355d3e1e 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list.component.html +++ b/frontend/src/app/features/boards/board/board-list/board-list.component.html @@ -36,7 +36,7 @@
diff --git a/frontend/src/app/features/boards/board/board-list/board-list.component.ts b/frontend/src/app/features/boards/board/board-list/board-list.component.ts index 0857b67176..65a427fa96 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list.component.ts @@ -9,50 +9,52 @@ import { OnDestroy, OnInit, Output, - ViewChild -} from "@angular/core"; + ViewChild, +} from '@angular/core'; import { LoadingIndicatorService, - withLoadingIndicator -} from "core-app/core/loading-indicator/loading-indicator.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { BoardInlineCreateService } from "core-app/features/boards/board/board-list/board-inline-create.service"; -import { AbstractWidgetComponent } from "core-app/shared/components/grids/widgets/abstract-widget.component"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { Board } from "core-app/features/boards/board/board"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageStatesInitializationService } from "core-app/features/work-packages/components/wp-list/wp-states-initialization.service"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { BoardActionService } from "core-app/features/boards/board/board-actions/board-action.service"; -import { ComponentType } from "@angular/cdk/portal"; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { BoardListMenuComponent } from "core-app/features/boards/board/board-list/board-list-menu.component"; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { WorkPackageCardDragAndDropService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { BoardFiltersService } from "core-app/features/boards/board/board-filter/board-filters.service"; -import { StateService, TransitionService } from "@uirouter/core"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { BoardListCrossSelectionService } from "core-app/features/boards/board/board-list/board-list-cross-selection.service"; -import { debounceTime, filter, map, retry } from "rxjs/operators"; -import { ChangeItem } from "core-app/shared/components/fields/changeset/changeset"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { HalEvent, HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; + withLoadingIndicator, +} from 'core-app/core/loading-indicator/loading-indicator.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { BoardInlineCreateService } from 'core-app/features/boards/board/board-list/board-inline-create.service'; +import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { Board } from 'core-app/features/boards/board/board'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; +import { ComponentType } from '@angular/cdk/portal'; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { BoardListMenuComponent } from 'core-app/features/boards/board/board-list/board-list-menu.component'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { WorkPackageCardDragAndDropService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { BoardFiltersService } from 'core-app/features/boards/board/board-filter/board-filters.service'; +import { StateService, TransitionService } from '@uirouter/core'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { BoardListCrossSelectionService } from 'core-app/features/boards/board/board-list/board-list-cross-selection.service'; +import { + debounceTime, filter, map, retry, +} from 'rxjs/operators'; +import { ChangeItem } from 'core-app/shared/components/fields/changeset/changeset'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { HalEvent, HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; export interface DisabledButtonPlaceholder { text:string; @@ -67,8 +69,8 @@ export interface DisabledButtonPlaceholder { providers: [ { provide: WorkPackageInlineCreateService, useClass: BoardInlineCreateService }, BoardListMenuComponent, - WorkPackageCardDragAndDropService - ] + WorkPackageCardDragAndDropService, + ], }) export class BoardListComponent extends AbstractWidgetComponent implements OnInit, OnDestroy { /** Output fired upon query removal */ @@ -91,7 +93,9 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni /** The action attribute resource if any */ public actionResource:HalResource|undefined; + public actionResourceClass = ''; + public headerComponent:ComponentType|undefined; /** Rename inFlight */ @@ -107,7 +111,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni updateSuccessful: this.I18n.t('js.notice_successful_update'), areYouSure: this.I18n.t('js.text_are_you_sure'), unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), - click_to_remove: this.I18n.t('js.boards.click_to_remove_list') + click_to_remove: this.I18n.t('js.boards.click_to_remove_list'), }; /** Are we allowed to remove and drag & drop elements ? */ @@ -121,36 +125,36 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni /** Move check to be passed into card component */ public canDragOutOf = false; + public canDragOutOfHandler = (workPackage:WorkPackageResource) => this.canMove(workPackage); public buttonPlaceholder:DisabledButtonPlaceholder|undefined; constructor(readonly apiv3Service:APIV3Service, - readonly I18n:I18nService, - readonly state:StateService, - readonly cdRef:ChangeDetectorRef, - readonly transitions:TransitionService, - readonly boardFilters:BoardFiltersService, - readonly notifications:NotificationsService, - readonly querySpace:IsolatedQuerySpace, - readonly halNotification:HalResourceNotificationService, - readonly halEvents:HalEventsService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpViewFocusService:WorkPackageViewFocusService, - readonly wpViewSelectionService:WorkPackageViewSelectionService, - readonly boardListCrossSelectionService:BoardListCrossSelectionService, - readonly authorisationService:AuthorisationService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly injector:Injector, - readonly halEditing:HalResourceEditingService, - readonly loadingIndicator:LoadingIndicatorService, - readonly schemaCache:SchemaCacheService, - readonly boardService:BoardService, - readonly boardActionRegistry:BoardActionsRegistryService, - readonly causedUpdates:CausedUpdatesService, - readonly keepTab:KeepTabService, - readonly $state:StateService, - ) { + readonly I18n:I18nService, + readonly state:StateService, + readonly cdRef:ChangeDetectorRef, + readonly transitions:TransitionService, + readonly boardFilters:BoardFiltersService, + readonly notifications:NotificationsService, + readonly querySpace:IsolatedQuerySpace, + readonly halNotification:HalResourceNotificationService, + readonly halEvents:HalEventsService, + readonly wpStatesInitialization:WorkPackageStatesInitializationService, + readonly wpViewFocusService:WorkPackageViewFocusService, + readonly wpViewSelectionService:WorkPackageViewSelectionService, + readonly boardListCrossSelectionService:BoardListCrossSelectionService, + readonly authorisationService:AuthorisationService, + readonly wpInlineCreate:WorkPackageInlineCreateService, + readonly injector:Injector, + readonly halEditing:HalResourceEditingService, + readonly loadingIndicator:LoadingIndicatorService, + readonly schemaCache:SchemaCacheService, + readonly boardService:BoardService, + readonly boardActionRegistry:BoardActionsRegistryService, + readonly causedUpdates:CausedUpdatesService, + readonly keepTab:KeepTabService, + readonly $state:StateService) { super(I18n, injector); } @@ -160,7 +164,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni this.resource.isNewWidget = false; // Set initial selection if split view open - if (this.state.includes(this.state.current.data.baseRoute + '.details')) { + if (this.state.includes(`${this.state.current.data.baseRoute}.details`)) { const wpId = this.state.params.workPackageId; this.wpViewSelectionService.initializeSelection([wpId]); } @@ -181,26 +185,26 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni .updates$() .pipe( debounceTime(100), - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((selectionState) => { - const selected = Object.keys(_.pickBy(selectionState.selected, (selected, _) => selected === true)); + const selected = Object.keys(_.pickBy(selectionState.selected, (selected, _) => selected === true)); - const focused = this.wpViewFocusService.focusedWorkPackage; + const focused = this.wpViewFocusService.focusedWorkPackage; - this.boardListCrossSelectionService.updateSelection({ - withinQuery: this.queryId, - focusedWorkPackage: focused, - allSelected: selected + this.boardListCrossSelectionService.updateSelection({ + withinQuery: this.queryId, + focusedWorkPackage: focused, + allSelected: selected, + }); }); - }); // Apply focus and selection when changed in cross service this.boardListCrossSelectionService .selectionsForQuery(this.queryId) .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(selection => { + .subscribe((selection) => { this.wpViewSelectionService.initializeSelection(selection.allSelected); }); @@ -209,7 +213,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni .filters .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => this.updateQuery(true)); @@ -219,7 +223,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni this.querySpace.query .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((query) => { this.query = query; @@ -250,9 +254,9 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni } public get canRename() { - return this.canManage && - !!this.query.updateImmediately && - this.board.isFree; + return this.canManage + && !!this.query.updateImmediately + && this.board.isFree; } public addReferenceCard() { @@ -264,7 +268,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni } public deleteList(query?:QueryResource) { - query = query ? query : this.query; + query = query || this.query; if (!window.confirm(this.text.areYouSure)) { return; @@ -298,10 +302,9 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni private boardListActionColorClass(value?:HalResource):string { const attribute = this.board.actionAttribute!; if (value && value.id) { - return Highlighting.backgroundClass(attribute, value.id!); - } else { - return ''; + return Highlighting.backgroundClass(attribute, value.id); } + return ''; } public get listName() { @@ -341,7 +344,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni } // Load the resource - actionService.getLoadedActionValue(query).then(async resource => { + actionService.getLoadedActionValue(query).then(async (resource) => { this.actionResource = resource; this.headerComponent = actionService.headerComponent(); this.buttonPlaceholder = actionService.disabledAddButtonPlaceholder(resource); @@ -372,7 +375,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni */ private addWorkPackage(workPackage:WorkPackageResource) { const query = this.querySpace.query.value!; - const changeset = this.halEditing.changeFor(workPackage) as WorkPackageChangeset; + const changeset:WorkPackageChangeset = this.halEditing.changeFor(workPackage); // Assign to the action attribute if this is an action board this.actionService?.assignToWorkPackage(changeset, query); @@ -380,10 +383,9 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni if (changeset.isEmpty()) { // Ensure work package and its schema is loaded return this.apiv3Service.work_packages.cache.updateWorkPackage(workPackage); - } else { - // Save changes to the work package, which reloads it as well - return this.halEditing.save(changeset); } + // Save changes to the work package, which reloads it as well + return this.halEditing.save(changeset); } private get queryId():string { @@ -396,7 +398,7 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni .queries .find(this.columnsQueryProps, this.queryId) .pipe( - retry(3) + retry(3), ); // Spread arguments on pipe does not work: @@ -407,11 +409,11 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni observable .subscribe( - query => this.wpStatesInitialization.updateQuerySpace(query, query.results), - error => { + (query) => this.wpStatesInitialization.updateQuerySpace(query, query.results), + (error) => { this.loadingError = this.halNotification.retrieveErrorMessage(error); this.cdRef.detectChanges(); - } + }, ); } @@ -425,16 +427,15 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni const newFilters = existingFilters.concat(filters); const newColumnsQueryProps:any = { 'columns[]': ['id', 'subject'], - 'showHierarchies': false, - 'pageSize': 500, - 'filters': JSON.stringify(newFilters), + showHierarchies: false, + pageSize: 500, + filters: JSON.stringify(newFilters), }; this.columnsQueryProps = newColumnsQueryProps; } private listenToActionAttributeChanges() { - // If we don't have an action attribute // nothing to do if (!this.board.actionAttribute) { @@ -445,14 +446,13 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni this.halEvents .events$ .pipe( - filter(event => event.resourceType === 'WorkPackage'), + filter((event) => event.resourceType === 'WorkPackage'), // Only allow updates, otherwise this causes an error reloading the list // before the work package can be added to the query order - filter(event => event.eventType === 'updated'), + filter((event) => event.eventType === 'updated'), map((event:HalEvent) => event.commit?.changes[this.actionService!.filterName]), - filter(value => !!value), + filter((value) => !!value), filter((value:ChangeItem) => { - // Compare the from and to values from the committed changes // with the current actionResource const current = this.actionResource?.href; @@ -460,17 +460,17 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni const from = (value.from as HalResource|undefined)?.href; return !!current && (current === to || current === from); - }) + }), ).subscribe((event) => { - this.updateQuery(true); - }); + this.updateQuery(true); + }); } openFullViewOnDoubleClick(event:{ workPackageId:string, double:boolean }) { if (event.double) { this.state.go( 'work-packages.show', - { workPackageId: event.workPackageId } + { workPackageId: event.workPackageId }, ); } } diff --git a/frontend/src/app/features/boards/board/board-list/board-lists.service.ts b/frontend/src/app/features/boards/board/board-list/board-lists.service.ts index b7148610b1..6d2ffdcc49 100644 --- a/frontend/src/app/features/boards/board/board-list/board-lists.service.ts +++ b/frontend/src/app/features/boards/board/board-list/board-lists.service.ts @@ -1,26 +1,25 @@ -import { Injectable } from "@angular/core"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { Board } from "core-app/features/boards/board/board"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; +import { Injectable } from '@angular/core'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { Board } from 'core-app/features/boards/board/board'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; @Injectable({ providedIn: 'root' }) export class BoardListsService { - private v3 = this.pathHelper.api.v3; constructor(private readonly CurrentProject:CurrentProjectService, - private readonly pathHelper:PathHelperService, - private readonly apiV3Service:APIV3Service, - private readonly halResourceService:HalResourceService, - private readonly notifications:NotificationsService, - private readonly I18n:I18nService) { + private readonly pathHelper:PathHelperService, + private readonly apiV3Service:APIV3Service, + private readonly halResourceService:HalResourceService, + private readonly notifications:NotificationsService, + private readonly I18n:I18nService) { } @@ -34,7 +33,7 @@ export class BoardListsService { .loadWithParams( { pageSize: 0, - filters: filterJson + filters: filterJson, }, undefined, this.CurrentProject.identifier, @@ -44,15 +43,14 @@ export class BoardListsService { .then(([form, query]) => { // When the permission to create public queries is missing, throw an error. // Otherwise private queries would be created. - if (form.schema['public'].writable) { + if (form.schema.public.writable) { return this .apiV3Service .queries .post(query, form) .toPromise(); - } else { - throw new Error(this.I18n.t('js.boards.error_permission_missing')); } + throw new Error(this.I18n.t('js.boards.error_permission_missing')); }); } @@ -83,8 +81,8 @@ export class BoardListsService { endColumn: count + 2, options: { queryId: query.id, - filters: filters, - } + filters, + }, }; const resource = this.halResourceService.createHalResourceOfClass(GridWidgetResource, source); @@ -100,13 +98,13 @@ export class BoardListsService { return { hidden: true, public: true, - "_links": { - "sortBy": [ - { "href": this.v3.apiV3Base + "/queries/sort_bys/manualSorting-asc" }, - { "href": this.v3.apiV3Base + "/queries/sort_bys/id-asc" }, - ] + _links: { + sortBy: [ + { href: `${this.v3.apiV3Base}/queries/sort_bys/manualSorting-asc` }, + { href: `${this.v3.apiV3Base}/queries/sort_bys/id-asc` }, + ], }, - ...params + ...params, }; } diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts index eb50c46699..581ce56097 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts @@ -1,38 +1,43 @@ -import { Component, ElementRef, Injector, OnInit, QueryList, ViewChild, ViewChildren } from "@angular/core"; -import { forkJoin, Observable, of, Subscription } from "rxjs"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { BoardListComponent } from "core-app/features/boards/board/board-list/board-list.component"; -import { StateService } from "@uirouter/core"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { BoardListsService } from "core-app/features/boards/board/board-list/board-lists.service"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { QueryUpdatedService } from "core-app/features/boards/board/query-updated/query-updated.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { Board, BoardWidgetOption } from "core-app/features/boards/board/board"; -import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { BoardPartitionedPageComponent } from "core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component"; -import { AddListModalComponent } from "core-app/features/boards/board/add-list-modal/add-list-modal.component"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardListCrossSelectionService } from "core-app/features/boards/board/board-list/board-list-cross-selection.service"; -import { catchError, filter, map, switchMap, tap } from "rxjs/operators"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { + Component, ElementRef, Injector, OnInit, QueryList, ViewChild, ViewChildren, +} from '@angular/core'; +import { + forkJoin, Observable, of, Subscription, +} from 'rxjs'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { BoardListComponent } from 'core-app/features/boards/board/board-list/board-list.component'; +import { StateService } from '@uirouter/core'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { BoardListsService } from 'core-app/features/boards/board/board-list/board-lists.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { QueryUpdatedService } from 'core-app/features/boards/board/query-updated/query-updated.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { Board, BoardWidgetOption } from 'core-app/features/boards/board/board'; +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { BoardPartitionedPageComponent } from 'core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component'; +import { AddListModalComponent } from 'core-app/features/boards/board/add-list-modal/add-list-modal.component'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardListCrossSelectionService } from 'core-app/features/boards/board/board-list/board-list-cross-selection.service'; +import { + catchError, filter, map, switchMap, tap, +} from 'rxjs/operators'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; @Component({ templateUrl: './board-list-container.component.html', styleUrls: ['./board-list-container.component.sass'], providers: [ - BoardListCrossSelectionService - ] + BoardListCrossSelectionService, + ], }) export class BoardListContainerComponent extends UntilDestroyedMixin implements OnInit { - text = { button_more: this.I18n.t('js.button_more'), delete: this.I18n.t('js.button_delete'), @@ -46,9 +51,9 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), }; - /** Container reference */ public _container:HTMLElement; + @ViewChild('container') set container(v:ElementRef|undefined) { // ViewChild reference may be undefined initially @@ -57,7 +62,7 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements if (this._container === undefined) { this.Drag.addScrollContainer(v.nativeElement); } - setTimeout(() => this._container = v.nativeElement); + setTimeout(() => (this._container = v.nativeElement)); } } @@ -67,27 +72,28 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements trackByQueryId = (index:number, widget:GridWidgetResource) => widget.options.queryId; board$:Observable; + boardWidgets:GridWidgetResource[] = []; private currentQueryUpdatedMonitoring:Subscription; constructor(readonly I18n:I18nService, - readonly state:StateService, - readonly notifications:NotificationsService, - readonly halNotification:HalResourceNotificationService, - readonly boardComponent:BoardPartitionedPageComponent, - readonly BoardList:BoardListsService, - readonly boardActionRegistry:BoardActionsRegistryService, - readonly opModalService:OpModalService, - readonly injector:Injector, - readonly apiV3Service:APIV3Service, - readonly Boards:BoardService, - readonly Banner:BannersService, - readonly boardListCrossSelectionService:BoardListCrossSelectionService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly Drag:DragAndDropService, - readonly apiv3Service:APIV3Service, - readonly QueryUpdated:QueryUpdatedService) { + readonly state:StateService, + readonly notifications:NotificationsService, + readonly halNotification:HalResourceNotificationService, + readonly boardComponent:BoardPartitionedPageComponent, + readonly BoardList:BoardListsService, + readonly boardActionRegistry:BoardActionsRegistryService, + readonly opModalService:OpModalService, + readonly injector:Injector, + readonly apiV3Service:APIV3Service, + readonly Boards:BoardService, + readonly Banner:BannersService, + readonly boardListCrossSelectionService:BoardListCrossSelectionService, + readonly wpStatesInitialization:WorkPackageStatesInitializationService, + readonly Drag:DragAndDropService, + readonly apiv3Service:APIV3Service, + readonly QueryUpdated:QueryUpdatedService) { super(); } @@ -100,7 +106,7 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements .requireAndStream() .pipe( this.setAllowedBoardWidgets, - tap(board => this.setupQueryUpdatedMonitoring(board)) + tap((board) => this.setupQueryUpdatedMonitoring(board)), ); this.Boards.currentBoard$.next(id); @@ -110,49 +116,47 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements .pipe( this.untilDestroyed(), filter((state) => state.focusedWorkPackage !== null), - filter(() => this.state.includes(this.state.current.data.baseRoute + '.details')) - ).subscribe(selection => { + filter(() => this.state.includes(`${this.state.current.data.baseRoute}.details`)), + ).subscribe((selection) => { // Update split screen - this.state.go(this.state.current.data.baseRoute + '.details', { workPackageId: selection.focusedWorkPackage }); + this.state.go(`${this.state.current.data.baseRoute}.details`, { workPackageId: selection.focusedWorkPackage }); }); } - setAllowedBoardWidgets = (boardObservable:Observable) => { + setAllowedBoardWidgets = (boardObservable:Observable) => // The grid config could have widgets that the user is not allowed to // see, so we filter out those that rise an access error. - return boardObservable + boardObservable .pipe( switchMap( - board => this.getAllowedBoardWidgets(board).pipe(map(allowedBoardWidgets => ({ board, allowedBoardWidgets }))), + (board) => this.getAllowedBoardWidgets(board).pipe(map((allowedBoardWidgets) => ({ board, allowedBoardWidgets }))), ), - map(result => { + map((result) => { this.boardWidgets = result.allowedBoardWidgets; return result.board; - }) - ); - }; + }), + ) + ; getAllowedBoardWidgets(board:Board) { if (board.queries?.length) { - const queryRequests$ = board.queries.map(query => this.apiv3Service.queries + const queryRequests$ = board.queries.map((query) => this.apiv3Service.queries .find({ filters: JSON.stringify(query.options.filters), pageSize: 0 }, query.options.queryId as string) .pipe( map(() => query), - catchError(error => { + catchError((error) => { const userIsNotAllowedToSeeSubprojectError = 'urn:openproject-org:api:v3:errors:InvalidQuery'; - const result = error.errorIdentifier === userIsNotAllowedToSeeSubprojectError ? null : query; + const result = error.errorIdentifier === userIsNotAllowedToSeeSubprojectError ? null : query; return of(result); - }) - ) - ); + }), + )); return forkJoin([...queryRequests$]) - .pipe(map(boardWidgets => boardWidgets.filter(boardWidget => !!boardWidget) as GridWidgetResource[])); - } else { - return of([]); + .pipe(map((boardWidgets) => boardWidgets.filter((boardWidget) => !!boardWidget) as GridWidgetResource[])); } + return of([]); } moveList(board:Board, event:CdkDragDrop) { @@ -169,16 +173,15 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements if (board.isFree) { return this.BoardList .addFreeQuery(board, { name: this.text.unnamed_list }) - .then(board => this.Boards.save(board).toPromise()) - .catch(error => this.showError(error)); - } else { - const active = this.getActionFiltersFromWidget(board); - this.opModalService.show( - AddListModalComponent, - this.injector, - { board: board, active: active } - ); + .then((board) => this.Boards.save(board).toPromise()) + .catch((error) => this.showError(error)); } + const active = this.getActionFiltersFromWidget(board); + this.opModalService.show( + AddListModalComponent, + this.injector, + { board, active }, + ); } showBoardListView() { @@ -202,7 +205,7 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements .QueryUpdated .monitor(board.queries.map((widget) => widget.options.queryId as string)) .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((collection) => this.requestRefreshOfUpdatedLists(collection.elements)); } @@ -234,9 +237,9 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements */ private getActionFiltersFromWidget(board:Board):(string|null)[] { return board.grid.widgets - .map(widget => { + .map((widget) => { const service = this.boardActionRegistry.get(board.actionAttribute!); - const filterName = service.filterName; + const { filterName } = service; const options:BoardWidgetOption = widget.options as any; const filter = _.find(options.filters, (filter) => !!filter[filterName]); @@ -244,7 +247,6 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements return (filter[filterName].values[0] || null) as any; } }) - .filter(value => value !== undefined); + .filter((value) => value !== undefined); } - } diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts index e7d70745e1..9e52d0c94a 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts @@ -1,29 +1,31 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector } from "@angular/core"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, +} from '@angular/core'; import { DynamicComponentDefinition, ToolbarButtonComponentDefinition, - ViewPartitionState -} from "core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component"; -import { StateService, TransitionService } from "@uirouter/core"; -import { BoardFilterComponent } from "core-app/features/boards/board/board-filter/board-filter.component"; -import { Board } from "core-app/features/boards/board/board"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { WorkPackageFilterButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component"; -import { ZenModeButtonComponent } from "core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component"; -import { BoardsMenuButtonComponent } from "core-app/features/boards/board/toolbar-menu/boards-menu-button.component"; -import { RequestSwitchmap } from "core-app/shared/helpers/rxjs/request-switchmap"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { finalize, take } from "rxjs/operators"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { Ng2StateDeclaration } from "@uirouter/angular"; -import { BoardFiltersService } from "core-app/features/boards/board/board-filter/board-filters.service"; -import { CardViewHandlerRegistry } from "core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; + ViewPartitionState, +} from 'core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component'; +import { StateService, TransitionService } from '@uirouter/core'; +import { BoardFilterComponent } from 'core-app/features/boards/board/board-filter/board-filter.component'; +import { Board } from 'core-app/features/boards/board/board'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { WorkPackageFilterButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component'; +import { ZenModeButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; +import { BoardsMenuButtonComponent } from 'core-app/features/boards/board/toolbar-menu/boards-menu-button.component'; +import { RequestSwitchmap } from 'core-app/shared/helpers/rxjs/request-switchmap'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { finalize, take } from 'rxjs/operators'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { BoardFiltersService } from 'core-app/features/boards/board/board-filter/board-filters.service'; +import { CardViewHandlerRegistry } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export function boardCardViewHandlerFactory(injector:Injector) { return new CardViewHandlerRegistry(injector); @@ -33,16 +35,15 @@ export function boardCardViewHandlerFactory(injector:Injector) { templateUrl: '../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html', styleUrls: [ '../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.sass', - './board-partitioned-page.component.sass' + './board-partitioned-page.component.sass', ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ DragAndDropService, BoardFiltersService, - ] + ], }) export class BoardPartitionedPageComponent extends UntilDestroyedMixin { - text = { button_more: this.I18n.t('js.button_more'), delete: this.I18n.t('js.button_delete'), @@ -75,6 +76,7 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { /** Current query title to render */ selectedTitle?:string; + currentQuery:QueryResource|undefined; /** Whether we're saving the board */ @@ -99,7 +101,7 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { filterContainerDefinition:DynamicComponentDefinition = { component: BoardFilterComponent, inputs: { - board$: this.board$ + board$: this.board$, }, }; @@ -110,40 +112,40 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { return this.Boards .save(board) .pipe( - finalize(() => this.toolbarDisabled = false) + finalize(() => (this.toolbarDisabled = false)), ); - } + }, ); toolbarButtonComponents:ToolbarButtonComponentDefinition[] = [ { component: WorkPackageFilterButtonComponent, - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: ZenModeButtonComponent, - containerClasses: 'hidden-for-mobile' + containerClasses: 'hidden-for-mobile', }, { component: BoardsMenuButtonComponent, containerClasses: 'hidden-for-mobile', show: () => this.editable, inputs: { - board$: this.board$ - } - } + board$: this.board$, + }, + }, ]; constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly $transitions:TransitionService, - readonly state:StateService, - readonly notifications:NotificationsService, - readonly halNotification:HalResourceNotificationService, - readonly injector:Injector, - readonly apiV3Service:APIV3Service, - readonly boardFilters:BoardFiltersService, - readonly Boards:BoardService) { + readonly cdRef:ChangeDetectorRef, + readonly $transitions:TransitionService, + readonly state:StateService, + readonly notifications:NotificationsService, + readonly halNotification:HalResourceNotificationService, + readonly injector:Injector, + readonly apiV3Service:APIV3Service, + readonly boardFilters:BoardFiltersService, + readonly Boards:BoardService) { super(); } @@ -154,10 +156,10 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { this.boardSaver .observe(componentDestroyed(this)) .subscribe( - (board:Board) => { + () => { this.notifications.addSuccess(this.text.updateSuccessful); }, - (error:unknown) => this.halNotification.handleRawError(error) + (error:unknown) => this.halNotification.handleRawError(error), ); this.removeTransitionSubscription = this.$transitions.onSuccess({}, (transition):any => { @@ -171,9 +173,9 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { this.board$ .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(board => { + .subscribe((board) => { const queryProps = this.state.params.query_props; this.editable = board.editable; this.selectedTitle = board.name; @@ -190,7 +192,7 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin { changeChangesFromTitle(newName:string) { this.board$ .pipe(take(1)) - .subscribe(board => { + .subscribe((board) => { board.name = newName; board.filters = this.boardFilters.current; diff --git a/frontend/src/app/features/boards/board/board-video-teaser-modal/board-video-teaser-modal.component.ts b/frontend/src/app/features/boards/board/board-video-teaser-modal/board-video-teaser-modal.component.ts index 02bb00d539..b8cb03f39b 100644 --- a/frontend/src/app/features/boards/board/board-video-teaser-modal/board-video-teaser-modal.component.ts +++ b/frontend/src/app/features/boards/board/board-video-teaser-modal/board-video-teaser-modal.component.ts @@ -1,11 +1,12 @@ -import { ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, +} from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { boardTeaserVideoURL } from "core-app/features/boards/board-constants.const"; -import { DomSanitizer } from "@angular/platform-browser"; - +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { boardTeaserVideoURL } from 'core-app/features/boards/board-constants.const'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ template: ` @@ -36,10 +37,9 @@ import { DomSanitizer } from "@angular/platform-browser";
- ` + `, }) export class BoardVideoTeaserModalComponent extends OpModalComponent implements OnInit, OnDestroy { - /* Close on escape? */ public closeOnEscape = false; @@ -47,17 +47,16 @@ export class BoardVideoTeaserModalComponent extends OpModalComponent implements public closeOnOutsideClick = false; public text:any = { - title: this.I18n.t('js.label_board_plural') + title: this.I18n.t('js.label_board_plural'), }; public teaserVideoUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(boardTeaserVideoURL); constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly domSanitizer:DomSanitizer) { - + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly domSanitizer:DomSanitizer) { super(locals, cdRef, elementRef); } } diff --git a/frontend/src/app/features/boards/board/board.service.ts b/frontend/src/app/features/boards/board/board.service.ts index fba11832f8..5d52078824 100644 --- a/frontend/src/app/features/boards/board/board.service.ts +++ b/frontend/src/app/features/boards/board/board.service.ts @@ -1,14 +1,14 @@ -import { Injectable } from "@angular/core"; -import { BoardListsService } from "core-app/features/boards/board/board-list/board-lists.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { Board, BoardType } from "core-app/features/boards/board/board"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { BehaviorSubject, Observable } from "rxjs"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { Injectable } from '@angular/core'; +import { BoardListsService } from 'core-app/features/boards/board/board-list/board-lists.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { Board, BoardType } from 'core-app/features/boards/board/board'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export interface CreateBoardParams { type:BoardType; @@ -18,7 +18,6 @@ export interface CreateBoardParams { @Injectable({ providedIn: 'root' }) export class BoardService { - public currentBoard$:BehaviorSubject = new BehaviorSubject(null); private loadAllPromise:Promise|undefined; @@ -26,17 +25,17 @@ export class BoardService { private text = { unnamed_board: this.I18n.t('js.boards.label_unnamed_board'), action_board: (attr:string) => this.I18n.t('js.boards.board_type.action_by_attribute', - { attribute: this.I18n.t('js.boards.board_type.action_type.' + attr) }), + { attribute: this.I18n.t(`js.boards.board_type.action_type.${attr}`) }), unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), }; constructor(protected apiV3Service:APIV3Service, - protected PathHelper:PathHelperService, - protected CurrentProject:CurrentProjectService, - protected halResourceService:HalResourceService, - protected boardActions:BoardActionsRegistryService, - protected I18n:I18nService, - protected boardsList:BoardListsService) { + protected PathHelper:PathHelperService, + protected CurrentProject:CurrentProjectService, + protected halResourceService:HalResourceService, + protected boardActions:BoardActionsRegistryService, + protected I18n:I18nService, + protected boardsList:BoardListsService) { } /** @@ -63,7 +62,6 @@ export class BoardService { return !!board.grid.$links.delete; } - /** * Save the changes to the board */ @@ -114,7 +112,7 @@ export class BoardService { return params.boardName; } - if (params.type === "action") { + if (params.type === 'action') { return this.text.action_board(params.attribute!); } diff --git a/frontend/src/app/features/boards/board/board.ts b/frontend/src/app/features/boards/board/board.ts index 3ba58878ca..14dd8e6b11 100644 --- a/frontend/src/app/features/boards/board/board.ts +++ b/frontend/src/app/features/boards/board/board.ts @@ -1,7 +1,7 @@ -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { CardHighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { CardHighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export type BoardType = 'free'|'action'; @@ -60,7 +60,7 @@ export class Board { } public removeQuery(widget:GridWidgetResource) { - this.grid.widgets = this.grid.widgets.filter(el => el.options.queryId !== widget.options.queryId); + this.grid.widgets = this.grid.widgets.filter((el) => el.options.queryId !== widget.options.queryId); } public get queries():GridWidgetResource[] { @@ -80,9 +80,7 @@ export class Board { } public sortWidgets() { - this.grid.widgets = this.grid.widgets.sort((a, b) => { - return a.startColumn - b.startColumn; - }); + this.grid.widgets = this.grid.widgets.sort((a, b) => a.startColumn - b.startColumn); } public showStatusButton() { diff --git a/frontend/src/app/features/boards/board/caused-updates/caused-updates.service.ts b/frontend/src/app/features/boards/board/caused-updates/caused-updates.service.ts index f98bf5cabd..a471886332 100644 --- a/frontend/src/app/features/boards/board/caused-updates/caused-updates.service.ts +++ b/frontend/src/app/features/boards/board/caused-updates/caused-updates.service.ts @@ -1,12 +1,11 @@ -import { Injectable } from "@angular/core"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { Injectable } from '@angular/core'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; /** * The service is intended to store all the updates caused to a query by a user. * It is e.g. used to not update the board list when the current user moved a card within a list/query. */ - @Injectable() export class CausedUpdatesService { /** contains all the updates to the query caused by modifications of the user */ diff --git a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts index a6a8e3215e..bcd4e4f9b8 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts @@ -8,28 +8,26 @@ import { Injector, OnDestroy, OnInit, - ViewChild + ViewChild, } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { ActiveTabInterface, TabComponent, TabInterface, - TabPortalOutlet + TabPortalOutlet, } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardConfigurationService } from "core-app/features/boards/board/configuration-modal/board-configuration.service"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { Board } from "core-app/features/boards/board/board"; - +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardConfigurationService } from 'core-app/features/boards/board/configuration-modal/board-configuration.service'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { Board } from 'core-app/features/boards/board/board'; @Component({ - templateUrl: './board-configuration.modal.html' + templateUrl: './board-configuration.modal.html', }) -export class BoardConfigurationModal extends OpModalComponent implements OnInit, OnDestroy { - +export class BoardConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy { /* Close on escape? */ public closeOnEscape = false; @@ -46,18 +44,19 @@ export class BoardConfigurationModal extends OpModalComponent implements OnInit, // Get the view child we'll use as the portal host @ViewChild('tabContentOutlet', { static: true }) tabContentOutlet:ElementRef; + // And a reference to the actual portal host interface public tabPortalHost:TabPortalOutlet; constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly boardService:BoardService, - readonly boardConfigurationService:BoardConfigurationService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly componentFactoryResolver:ComponentFactoryResolver, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef) { + readonly I18n:I18nService, + readonly boardService:BoardService, + readonly boardConfigurationService:BoardConfigurationService, + readonly injector:Injector, + readonly appRef:ApplicationRef, + readonly componentFactoryResolver:ComponentFactoryResolver, + readonly cdRef:ChangeDetectorRef, + readonly elementRef:ElementRef) { super(locals, cdRef, elementRef); } @@ -69,7 +68,7 @@ export class BoardConfigurationModal extends OpModalComponent implements OnInit, this.tabContentOutlet.nativeElement, this.componentFactoryResolver, this.appRef, - this.injector + this.injector, ); setTimeout(() => { @@ -102,10 +101,9 @@ export class BoardConfigurationModal extends OpModalComponent implements OnInit, const board = this.locals.board as Board; this.boardService .save(board) - .subscribe(board => { + .subscribe(() => { this.service.close(); }); - } /** @@ -121,4 +119,4 @@ export class BoardConfigurationModal extends OpModalComponent implements OnInit, protected get afterFocusOn():JQuery { return this.$element; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts index 76e2346247..6cbd5f3ef9 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.service.ts @@ -1,17 +1,16 @@ import { Injectable } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { TabInterface } from "core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet"; -import { BoardHighlightingTabComponent } from "core-app/features/boards/board/configuration-modal/tabs/highlighting-tab.component"; +import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; +import { BoardHighlightingTabComponent } from 'core-app/features/boards/board/configuration-modal/tabs/highlighting-tab.component'; @Injectable() export class BoardConfigurationService { - protected _tabs:TabInterface[] = [ { id: 'highlighting', name: this.I18n.t('js.work_packages.table_configuration.highlighting'), componentClass: BoardHighlightingTabComponent, - } + }, ]; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts index 619ffb8df1..8cfe856988 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts @@ -1,19 +1,20 @@ import { Component, Inject, Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { Board } from "core-app/features/boards/board/board"; -import { CardHighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { CardHighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; @Component({ - templateUrl: './highlighting-tab.component.html' + templateUrl: './highlighting-tab.component.html', }) export class BoardHighlightingTabComponent implements TabComponent { - // Highlighting mode public highlightingMode:CardHighlightingMode = 'none'; + public entireCardMode = false; + public lastEntireCardAttribute:CardHighlightingMode = 'type'; // Current board resource @@ -26,12 +27,12 @@ export class BoardHighlightingTabComponent implements TabComponent { type: this.I18n.t('js.work_packages.properties.type'), priority: this.I18n.t('js.work_packages.table_configuration.highlighting_mode.priority'), entire_card_by: this.I18n.t('js.card.highlighting.entire_card_by'), - } + }, }; constructor(readonly injector:Injector, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService) { + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly I18n:I18nService) { } public onSave() { @@ -58,6 +59,5 @@ export class BoardHighlightingTabComponent implements TabComponent { } else { this.entireCardMode = false; } - } } diff --git a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts index fe2c173d11..e3b30afdb8 100644 --- a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts +++ b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,23 +34,23 @@ import { Input, Output, ViewChild, - ViewEncapsulation + ViewEncapsulation, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Observable, of } from "rxjs"; -import { catchError, map, tap } from "rxjs/operators"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { WorkPackageCardDragAndDropService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { OpAutocompleterComponent } from "core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { Observable, of } from 'rxjs'; +import { catchError, map, tap } from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { WorkPackageCardDragAndDropService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { OpAutocompleterComponent } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; @Component({ selector: 'board-inline-add-autocompleter', @@ -58,20 +58,18 @@ import { HalResourceService } from "core-app/features/hal/services/hal-resource. // Allow styling the embedded ng-select encapsulation: ViewEncapsulation.None, - styleUrls: ['./board-inline-add-autocompleter.sass'] + styleUrls: ['./board-inline-add-autocompleter.sass'], }) export class BoardInlineAddAutocompleterComponent implements AfterViewInit { - readonly text = { - placeholder: this.I18n.t('js.relations_autocomplete.placeholder') + placeholder: this.I18n.t('js.relations_autocomplete.placeholder'), }; // Whether we're currently loading public isLoading = false; - getAutocompleterData = (searchString:string):Observable => - { + getAutocompleterData = (searchString:string):Observable => { // Return when the search string is empty if (searchString.length === 0) { this.isLoading = false; @@ -100,38 +98,39 @@ export class BoardInlineAddAutocompleterComponent implements AfterViewInit { .filtered(filters) .get() .pipe( - map(collection => collection.elements), + map((collection) => collection.elements), catchError((error:unknown) => { this.notificationService.handleRawError(error); return of([]); }), - tap(() => this.isLoading = false) + tap(() => this.isLoading = false), ); - } + }; public autocompleterOptions = { - resource:'work_packages', - getOptionsFn: this.getAutocompleterData - } + resource: 'work_packages', + getOptionsFn: this.getAutocompleterData, + }; @Input() appendToContainer = 'body'; + @ViewChild(OpAutocompleterComponent) public ngSelectComponent:OpAutocompleterComponent; @Output() onCancel = new EventEmitter(); - @Output() onReferenced = new EventEmitter(); + @Output() onReferenced = new EventEmitter(); constructor(private readonly querySpace:IsolatedQuerySpace, - private readonly pathHelper:PathHelperService, - private readonly apiV3Service:APIV3Service, - private readonly urlParamsHelper:UrlParamsHelperService, - private readonly notificationService:WorkPackageNotificationService, - private readonly CurrentProject:CurrentProjectService, - private readonly halResourceService:HalResourceService, - private readonly schemaCacheService:SchemaCacheService, - private readonly cdRef:ChangeDetectorRef, - private readonly I18n:I18nService, - private readonly wpCardDragDrop:WorkPackageCardDragAndDropService) { + private readonly pathHelper:PathHelperService, + private readonly apiV3Service:APIV3Service, + private readonly urlParamsHelper:UrlParamsHelperService, + private readonly notificationService:WorkPackageNotificationService, + private readonly CurrentProject:CurrentProjectService, + private readonly halResourceService:HalResourceService, + private readonly schemaCacheService:SchemaCacheService, + private readonly cdRef:ChangeDetectorRef, + private readonly I18n:I18nService, + private readonly wpCardDragDrop:WorkPackageCardDragAndDropService) { } ngAfterViewInit():void { @@ -148,7 +147,6 @@ export class BoardInlineAddAutocompleterComponent implements AfterViewInit { } public addWorkPackageToQuery(workPackage?:WorkPackageResource) { - if (workPackage) { this.schemaCacheService .ensureLoaded(workPackage) diff --git a/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts b/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts index 309f889f8b..500b59a03f 100644 --- a/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts +++ b/frontend/src/app/features/boards/board/query-updated/query-updated.service.ts @@ -1,16 +1,15 @@ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; import { interval } from 'rxjs'; -import { startWith, switchMap, filter } from 'rxjs/operators'; -import { ActiveWindowService } from "core-app/core/active-window/active-window.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { filter, startWith, switchMap } from 'rxjs/operators'; +import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; const POLLING_INTERVAL = 2000; @Injectable() export class QueryUpdatedService { - constructor(readonly activeWindow:ActiveWindowService, - readonly apiV3Service:APIV3Service) { + readonly apiV3Service:APIV3Service) { } public monitor(ids:string[]) { @@ -28,7 +27,7 @@ export class QueryUpdatedService { return result; }), - filter((collection) => collection.count > 0) + filter((collection) => collection.count > 0), ); } @@ -37,8 +36,8 @@ export class QueryUpdatedService { .apiV3Service .queries .list({ - filters: [["id", "=", ids], - ["updatedAt", "<>d", [updatedAfter.toISOString()]]] + filters: [['id', '=', ids], + ['updatedAt', '<>d', [updatedAfter.toISOString()]]], }); } } diff --git a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts index 0f7f82d3e8..0ad0271de3 100644 --- a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts +++ b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts @@ -1,7 +1,7 @@ -import { Component, Input } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Board } from "core-app/features/boards/board/board"; -import { Observable } from "rxjs"; +import { Component, Input } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { Observable } from 'rxjs'; @Component({ template: ` @@ -11,7 +11,7 @@ import { Observable } from "rxjs"; [boardsToolbarMenu-resource]="board$ | async"> - ` + `, }) export class BoardsMenuButtonComponent { @Input() board$:Observable; @@ -22,4 +22,4 @@ export class BoardsMenuButtonComponent { constructor(readonly I18n:I18nService) { } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts b/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts index 7334a128e1..ac7b6774ff 100644 --- a/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts +++ b/frontend/src/app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Directive, ElementRef, Injector, Input } from '@angular/core'; +import { + Directive, ElementRef, Injector, Input, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuTrigger } from 'core-app/shared/components/op-context-menu/handlers/op-context-menu-trigger.directive'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { Board } from "core-app/features/boards/board/board"; -import { BoardConfigurationModal } from "core-app/features/boards/board/configuration-modal/board-configuration.modal"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { StateService } from "@uirouter/core"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { triggerEditingEvent } from "core-app/shared/components/editable-toolbar-title/editable-toolbar-title.component"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { BoardConfigurationModalComponent } from 'core-app/features/boards/board/configuration-modal/board-configuration.modal'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { StateService } from '@uirouter/core'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { triggerEditingEvent } from 'core-app/shared/components/editable-toolbar-title/editable-toolbar-title.component'; @Directive({ - selector: '[boardsToolbarMenu]' + selector: '[boardsToolbarMenu]', }) export class BoardsToolbarMenuDirective extends OpContextMenuTrigger { @Input('boardsToolbarMenu-resource') public board:Board; @@ -49,21 +51,20 @@ export class BoardsToolbarMenuDirective extends OpContextMenuTrigger { }; constructor(readonly elementRef:ElementRef, - readonly opContextMenu:OPContextMenuService, - readonly opModalService:OpModalService, - readonly boardService:BoardService, - readonly Notifications:NotificationsService, - readonly State:StateService, - readonly injector:Injector, - readonly I18n:I18nService) { - + readonly opContextMenu:OPContextMenuService, + readonly opModalService:OpModalService, + readonly boardService:BoardService, + readonly Notifications:NotificationsService, + readonly State:StateService, + readonly injector:Injector, + readonly I18n:I18nService) { super(elementRef, opContextMenu); } public get locals() { return { contextMenuId: 'boardsToolbarMenu', - items: this.items + items: this.items, }; } @@ -78,32 +79,32 @@ export class BoardsToolbarMenuDirective extends OpContextMenuTrigger { // Configuration modal linkText: this.I18n.t('js.toolbar.settings.configure_view'), icon: 'icon-settings', - onClick: ($event:JQuery.TriggeredEvent) => { + onClick: () => { this.opContextMenu.close(); - this.opModalService.show(BoardConfigurationModal, this.injector, { board: this.board }); + this.opModalService.show(BoardConfigurationModalComponent, this.injector, { board: this.board }); return true; - } + }, }, { // Rename query shortcut linkText: this.I18n.t('js.toolbar.settings.page_settings'), icon: 'icon-edit', - onClick: ($event:JQuery.TriggeredEvent) => { + onClick: () => { if (this.board.grid.updateImmediately) { - jQuery(`.toolbar-container .editable-toolbar-title--input`).trigger(triggerEditingEvent); + jQuery('.toolbar-container .editable-toolbar-title--input').trigger(triggerEditingEvent); } return true; - } + }, }, { // Delete query linkText: this.I18n.t('js.toolbar.settings.delete'), icon: 'icon-delete', - onClick: ($event:JQuery.TriggeredEvent) => { - if (this.board.grid.delete && - window.confirm(this.I18n.t('js.text_query_destroy_confirmation'))) { + onClick: () => { + if (this.board.grid.delete + && window.confirm(this.I18n.t('js.text_query_destroy_confirmation'))) { this.boardService .delete(this.board) .then(() => { @@ -112,8 +113,8 @@ export class BoardsToolbarMenuDirective extends OpContextMenuTrigger { } return true; - } - } + }, + }, ]; } } diff --git a/frontend/src/app/features/boards/boards-root/boards-root.component.ts b/frontend/src/app/features/boards/boards-root/boards-root.component.ts index dbd04ff820..63b0330ffd 100644 --- a/frontend/src/app/features/boards/boards-root/boards-root.component.ts +++ b/frontend/src/app/features/boards/boards-root/boards-root.component.ts @@ -1,12 +1,12 @@ -import { Component, Injector } from "@angular/core"; -import { BoardConfigurationService } from "core-app/features/boards/board/configuration-modal/board-configuration.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { BoardStatusActionService } from "core-app/features/boards/board/board-actions/status/status-action.service"; -import { BoardVersionActionService } from "core-app/features/boards/board/board-actions/version/version-action.service"; -import { QueryUpdatedService } from "core-app/features/boards/board/query-updated/query-updated.service"; -import { BoardAssigneeActionService } from "core-app/features/boards/board/board-actions/assignee/assignee-action.service"; -import { BoardSubprojectActionService } from "core-app/features/boards/board/board-actions/subproject/subproject-action.service"; -import { BoardSubtasksActionService } from "core-app/features/boards/board/board-actions/subtasks/board-subtasks-action.service"; +import { Component, Injector } from '@angular/core'; +import { BoardConfigurationService } from 'core-app/features/boards/board/configuration-modal/board-configuration.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { BoardStatusActionService } from 'core-app/features/boards/board/board-actions/status/status-action.service'; +import { BoardVersionActionService } from 'core-app/features/boards/board/board-actions/version/version-action.service'; +import { QueryUpdatedService } from 'core-app/features/boards/board/query-updated/query-updated.service'; +import { BoardAssigneeActionService } from 'core-app/features/boards/board/board-actions/assignee/assignee-action.service'; +import { BoardSubprojectActionService } from 'core-app/features/boards/board/board-actions/subproject/subproject-action.service'; +import { BoardSubtasksActionService } from 'core-app/features/boards/board/board-actions/subtasks/board-subtasks-action.service'; @Component({ selector: 'boards-entry', @@ -19,12 +19,10 @@ import { BoardSubtasksActionService } from "core-app/features/boards/board/board BoardSubprojectActionService, BoardSubtasksActionService, QueryUpdatedService, - ] + ], }) export class BoardsRootComponent { - constructor(readonly injector:Injector) { - // Register action services const registry = injector.get(BoardActionsRegistryService); diff --git a/frontend/src/app/features/boards/boards-sidebar/boards-menu.component.ts b/frontend/src/app/features/boards/boards-sidebar/boards-menu.component.ts index 64417f3480..9e2ca3195b 100644 --- a/frontend/src/app/features/boards/boards-sidebar/boards-menu.component.ts +++ b/frontend/src/app/features/boards/boards-sidebar/boards-menu.component.ts @@ -1,19 +1,19 @@ -import { Component, OnInit } from "@angular/core"; -import { Observable } from "rxjs"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { Board } from "core-app/features/boards/board/board"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { map } from "rxjs/operators"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { MainMenuNavigationService } from "core-app/core/main-menu/main-menu-navigation.service"; +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { map } from 'rxjs/operators'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { MainMenuNavigationService } from 'core-app/core/main-menu/main-menu-navigation.service'; export const boardsMenuSelector = 'boards-menu'; @Component({ selector: boardsMenuSelector, - templateUrl: './boards-menu.component.html' + templateUrl: './boards-menu.component.html', }) export class BoardsMenuComponent extends UntilDestroyedMixin implements OnInit { @@ -28,23 +28,21 @@ export class BoardsMenuComponent extends UntilDestroyedMixin implements OnInit { .boards .observeAll() .pipe( - map((boards:Board[]) => { - return boards.sort(function (a, b) { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - return 0; - }); - }) + map((boards:Board[]) => boards.sort((a, b) => { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + })), ); constructor(private readonly boardService:BoardService, - private readonly apiV3Service:APIV3Service, - private readonly currentProject:CurrentProjectService, - private readonly mainMenuService:MainMenuNavigationService) { + private readonly apiV3Service:APIV3Service, + private readonly currentProject:CurrentProjectService, + private readonly mainMenuService:MainMenuNavigationService) { super(); } @@ -62,10 +60,10 @@ export class BoardsMenuComponent extends UntilDestroyedMixin implements OnInit { .boardService .currentBoard$ .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((id:string|null) => { - this.selectedBoardId = id ? id : ''; + this.selectedBoardId = id || ''; }); } diff --git a/frontend/src/app/features/boards/index-page/boards-index-page.component.ts b/frontend/src/app/features/boards/index-page/boards-index-page.component.ts index ec0cc18566..fe2a831c0f 100644 --- a/frontend/src/app/features/boards/index-page/boards-index-page.component.ts +++ b/frontend/src/app/features/boards/index-page/boards-index-page.component.ts @@ -1,27 +1,28 @@ -import { AfterViewInit, Component, Injector, OnInit } from "@angular/core"; -import { Observable } from "rxjs"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { Board } from "core-app/features/boards/board/board"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { NewBoardModalComponent } from "core-app/features/boards/new-board-modal/new-board-modal.component"; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { contactUrl } from "core-app/core/setup/globals/constants.const"; -import { DomSanitizer } from "@angular/platform-browser"; -import { boardTeaserVideoURL } from "core-app/features/boards/board-constants.const"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { + AfterViewInit, Component, Injector, OnInit, +} from '@angular/core'; +import { Observable } from 'rxjs'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { Board } from 'core-app/features/boards/board/board'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { NewBoardModalComponent } from 'core-app/features/boards/new-board-modal/new-board-modal.component'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { contactUrl } from 'core-app/core/setup/globals/constants.const'; +import { DomSanitizer } from '@angular/platform-browser'; +import { boardTeaserVideoURL } from 'core-app/features/boards/board-constants.const'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './boards-index-page.component.html', - styleUrls: ['./boards-index-page.component.sass'] + styleUrls: ['./boards-index-page.component.sass'], }) export class BoardsIndexPageComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { - public text = { name: this.I18n.t('js.modals.label_name'), board: this.I18n.t('js.label_board'), @@ -29,7 +30,7 @@ export class BoardsIndexPageComponent extends UntilDestroyedMixin implements OnI type: this.I18n.t('js.boards.label_board_type'), type_free: this.I18n.t('js.boards.board_type.free'), action_by_attribute: (attr:string) => this.I18n.t('js.boards.board_type.action_by_attribute', - { attribute: this.I18n.t('js.boards.board_type.action_type.' + attr ) }), + { attribute: this.I18n.t(`js.boards.board_type.action_type.${attr}`) }), createdAt: this.I18n.t('js.label_created_on'), delete: this.I18n.t('js.button_delete'), areYouSure: this.I18n.t('js.text_are_you_sure'), @@ -52,15 +53,15 @@ export class BoardsIndexPageComponent extends UntilDestroyedMixin implements OnI teaserVideoURL = this.domSanitizer.bypassSecurityTrustResourceUrl(boardTeaserVideoURL); constructor(private readonly boardService:BoardService, - private readonly apiV3Service:APIV3Service, - private readonly I18n:I18nService, - private readonly notifications:NotificationsService, - private readonly opModalService:OpModalService, - private readonly loadingIndicatorService:LoadingIndicatorService, - private readonly authorisationService:AuthorisationService, - private readonly injector:Injector, - private readonly bannerService:BannersService, - private readonly domSanitizer:DomSanitizer) { + private readonly apiV3Service:APIV3Service, + private readonly I18n:I18nService, + private readonly notifications:NotificationsService, + private readonly opModalService:OpModalService, + private readonly loadingIndicatorService:LoadingIndicatorService, + private readonly authorisationService:AuthorisationService, + private readonly injector:Injector, + private readonly bannerService:BannersService, + private readonly domSanitizer:DomSanitizer) { super(); } @@ -91,7 +92,7 @@ export class BoardsIndexPageComponent extends UntilDestroyedMixin implements OnI .then(() => { this.notifications.addSuccess(this.text.deleteSuccessful); }) - .catch((error) => this.notifications.addError("Deletion failed: " + error)); + .catch((error) => this.notifications.addError(`Deletion failed: ${error}`)); } public showBoardIndexView() { diff --git a/frontend/src/app/features/boards/new-board-modal/new-board-modal.component.ts b/frontend/src/app/features/boards/new-board-modal/new-board-modal.component.ts index 26ab7eb3a3..4dcf611a55 100644 --- a/frontend/src/app/features/boards/new-board-modal/new-board-modal.component.ts +++ b/frontend/src/app/features/boards/new-board-modal/new-board-modal.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from "@angular/core"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BoardType } from "core-app/features/boards/board/board"; -import { StateService } from "@uirouter/core"; -import { BoardService } from "core-app/features/boards/board/board.service"; -import { BoardActionsRegistryService } from "core-app/features/boards/board/board-actions/board-actions-registry.service"; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { + ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, +} from '@angular/core'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BoardType } from 'core-app/features/boards/board/board'; +import { StateService } from '@uirouter/core'; +import { BoardService } from 'core-app/features/boards/board/board.service'; +import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; import { ITileViewEntry } from '../tile-view/tile-view.component'; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import imagePath = ImageHelpers.imagePath; - @Component({ templateUrl: './new-board-modal.html', @@ -72,16 +72,15 @@ export class NewBoardModalComponent extends OpModalComponent { }; constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly state:StateService, - readonly boardService:BoardService, - readonly boardActions:BoardActionsRegistryService, - readonly halNotification:HalResourceNotificationService, - readonly loadingIndicatorService:LoadingIndicatorService, - readonly I18n:I18nService, - readonly boardActionRegistry:BoardActionsRegistryService) { - + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly state:StateService, + readonly boardService:BoardService, + readonly boardActions:BoardActionsRegistryService, + readonly halNotification:HalResourceNotificationService, + readonly loadingIndicatorService:LoadingIndicatorService, + readonly I18n:I18nService, + readonly boardActionRegistry:BoardActionsRegistryService) { super(locals, cdRef, elementRef); this.initiateTiles(); } @@ -100,7 +99,7 @@ export class NewBoardModalComponent extends OpModalComponent { text: this.text.free_board_title, icon: 'icon-boards', description: this.text.free_board_text, - image: imagePath('board_creation_modal/lists.svg') + image: imagePath('board_creation_modal/lists.svg'), }); this.addIcon(this.available); this.addDescription(this.available); @@ -113,7 +112,7 @@ export class NewBoardModalComponent extends OpModalComponent { } private createAction(attribute:string) { - this.create({ type: 'action', attribute: attribute! }); + this.create({ type: 'action', attribute }); } private create(params:{ type:BoardType, attribute?:string }) { @@ -133,36 +132,36 @@ export class NewBoardModalComponent extends OpModalComponent { } private addDescription(tiles:ITileViewEntry[]) { - tiles.forEach(element => { + tiles.forEach((element) => { if (element.attribute !== 'basic') { - const service = this.boardActionRegistry.get(element.attribute!); + const service = this.boardActionRegistry.get(element.attribute); element.description = service.description; } }); } private addIcon(tiles:ITileViewEntry[]) { - tiles.forEach(element => { + tiles.forEach((element) => { if (element.attribute !== 'basic') { - const service = this.boardActionRegistry.get(element.attribute!); + const service = this.boardActionRegistry.get(element.attribute); element.icon = service.icon; } }); } private addText(tiles:ITileViewEntry[]) { - tiles.forEach(element => { + tiles.forEach((element) => { if (element.attribute !== 'basic') { - const service = this.boardActionRegistry.get(element.attribute!); + const service = this.boardActionRegistry.get(element.attribute); element.text = service.text; } }); } private addImage(tiles:ITileViewEntry[]) { - tiles.forEach(element => { + tiles.forEach((element) => { if (element.attribute !== 'basic') { - const service = this.boardActionRegistry.get(element.attribute!); + const service = this.boardActionRegistry.get(element.attribute); element.image = service.image; } }); diff --git a/frontend/src/app/features/boards/openproject-boards.module.ts b/frontend/src/app/features/boards/openproject-boards.module.ts index dead247c6f..9a55dc1f97 100644 --- a/frontend/src/app/features/boards/openproject-boards.module.ts +++ b/frontend/src/app/features/boards/openproject-boards.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,36 +27,36 @@ //++ import { NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/openproject-work-packages.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { UIRouterModule } from "@uirouter/angular"; -import { BoardListComponent } from "core-app/features/boards/board/board-list/board-list.component"; -import { BoardsRootComponent } from "core-app/features/boards/boards-root/boards-root.component"; -import { BoardInlineAddAutocompleterComponent } from "core-app/features/boards/board/inline-add/board-inline-add-autocompleter.component"; -import { BoardsToolbarMenuDirective } from "core-app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive"; -import { BoardConfigurationModal } from "core-app/features/boards/board/configuration-modal/board-configuration.modal"; -import { BoardsIndexPageComponent } from "core-app/features/boards/index-page/boards-index-page.component"; -import { BoardsMenuComponent } from "core-app/features/boards/boards-sidebar/boards-menu.component"; -import { NewBoardModalComponent } from "core-app/features/boards/new-board-modal/new-board-modal.component"; -import { AddListModalComponent } from "core-app/features/boards/board/add-list-modal/add-list-modal.component"; -import { BoardHighlightingTabComponent } from "core-app/features/boards/board/configuration-modal/tabs/highlighting-tab.component"; -import { AddCardDropdownMenuDirective } from "core-app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive"; -import { BoardFilterComponent } from "core-app/features/boards/board/board-filter/board-filter.component"; -import { DragScrollModule } from "cdk-drag-scroll"; -import { BoardListMenuComponent } from "core-app/features/boards/board/board-list/board-list-menu.component"; -import { VersionBoardHeaderComponent } from "core-app/features/boards/board/board-actions/version/version-board-header.component"; -import { DynamicModule } from "ng-dynamic-component"; -import { BOARDS_ROUTES, uiRouterBoardsConfiguration } from "core-app/features/boards/openproject-boards.routes"; -import { BoardPartitionedPageComponent } from "core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component"; -import { BoardListContainerComponent } from "core-app/features/boards/board/board-partitioned-page/board-list-container.component"; -import { BoardsMenuButtonComponent } from "core-app/features/boards/board/toolbar-menu/boards-menu-button.component"; -import { AssigneeBoardHeaderComponent } from "core-app/features/boards/board/board-actions/assignee/assignee-board-header.component"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { UIRouterModule } from '@uirouter/angular'; +import { BoardListComponent } from 'core-app/features/boards/board/board-list/board-list.component'; +import { BoardsRootComponent } from 'core-app/features/boards/boards-root/boards-root.component'; +import { BoardInlineAddAutocompleterComponent } from 'core-app/features/boards/board/inline-add/board-inline-add-autocompleter.component'; +import { BoardsToolbarMenuDirective } from 'core-app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive'; +import { BoardConfigurationModalComponent } from 'core-app/features/boards/board/configuration-modal/board-configuration.modal'; +import { BoardsIndexPageComponent } from 'core-app/features/boards/index-page/boards-index-page.component'; +import { BoardsMenuComponent } from 'core-app/features/boards/boards-sidebar/boards-menu.component'; +import { NewBoardModalComponent } from 'core-app/features/boards/new-board-modal/new-board-modal.component'; +import { AddListModalComponent } from 'core-app/features/boards/board/add-list-modal/add-list-modal.component'; +import { BoardHighlightingTabComponent } from 'core-app/features/boards/board/configuration-modal/tabs/highlighting-tab.component'; +import { AddCardDropdownMenuDirective } from 'core-app/features/boards/board/add-card-dropdown/add-card-dropdown-menu.directive'; +import { BoardFilterComponent } from 'core-app/features/boards/board/board-filter/board-filter.component'; +import { DragScrollModule } from 'cdk-drag-scroll'; +import { BoardListMenuComponent } from 'core-app/features/boards/board/board-list/board-list-menu.component'; +import { VersionBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/version/version-board-header.component'; +import { DynamicModule } from 'ng-dynamic-component'; +import { BOARDS_ROUTES, uiRouterBoardsConfiguration } from 'core-app/features/boards/openproject-boards.routes'; +import { BoardPartitionedPageComponent } from 'core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component'; +import { BoardListContainerComponent } from 'core-app/features/boards/board/board-partitioned-page/board-list-container.component'; +import { BoardsMenuButtonComponent } from 'core-app/features/boards/board/toolbar-menu/boards-menu-button.component'; +import { AssigneeBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/assignee/assignee-board-header.component'; +import { SubprojectBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/subproject/subproject-board-header.component'; +import { SubtasksBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/subtasks/subtasks-board-header.component'; +import { StatusBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/status/status-board-header.component'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; import { TileViewComponent } from './tile-view/tile-view.component'; -import { SubprojectBoardHeaderComponent } from "core-app/features/boards/board/board-actions/subproject/subproject-board-header.component"; -import { SubtasksBoardHeaderComponent } from "core-app/features/boards/board/board-actions/subtasks/subtasks-board-header.component"; -import { StatusBoardHeaderComponent } from "core-app/features/boards/board/board-actions/status/status-board-header.component"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; @NgModule({ imports: [ @@ -72,7 +72,7 @@ import { OpenprojectAutocompleterModule } from "core-app/shared/components/autoc // Routes for /boards UIRouterModule.forChild({ states: BOARDS_ROUTES, - config: uiRouterBoardsConfiguration + config: uiRouterBoardsConfiguration, }), ], declarations: [ @@ -84,7 +84,7 @@ import { OpenprojectAutocompleterModule } from "core-app/shared/components/autoc BoardInlineAddAutocompleterComponent, BoardsMenuComponent, BoardHighlightingTabComponent, - BoardConfigurationModal, + BoardConfigurationModalComponent, BoardsToolbarMenuDirective, BoardsMenuButtonComponent, NewBoardModalComponent, @@ -98,8 +98,7 @@ import { OpenprojectAutocompleterModule } from "core-app/shared/components/autoc SubtasksBoardHeaderComponent, StatusBoardHeaderComponent, TileViewComponent, - ] + ], }) export class OpenprojectBoardsModule { } - diff --git a/frontend/src/app/features/boards/openproject-boards.routes.ts b/frontend/src/app/features/boards/openproject-boards.routes.ts index 30f6358ed2..53c4a03e03 100644 --- a/frontend/src/app/features/boards/openproject-boards.routes.ts +++ b/frontend/src/app/features/boards/openproject-boards.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration, UIRouter } from "@uirouter/angular"; -import { BoardsRootComponent } from "core-app/features/boards/boards-root/boards-root.component"; -import { BoardsIndexPageComponent } from "core-app/features/boards/index-page/boards-index-page.component"; -import { BoardPartitionedPageComponent } from "core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component"; -import { BoardListContainerComponent } from "core-app/features/boards/board/board-partitioned-page/board-list-container.component"; -import { makeSplitViewRoutes } from "core-app/features/work-packages/routing/split-view-routes.template"; -import { WorkPackageSplitViewComponent } from "core-app/features/work-packages/routing/wp-split-view/wp-split-view.component"; +import { Ng2StateDeclaration, UIRouter } from '@uirouter/angular'; +import { BoardsRootComponent } from 'core-app/features/boards/boards-root/boards-root.component'; +import { BoardsIndexPageComponent } from 'core-app/features/boards/index-page/boards-index-page.component'; +import { BoardPartitionedPageComponent } from 'core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component'; +import { BoardListContainerComponent } from 'core-app/features/boards/board/board-partitioned-page/board-list-container.component'; +import { makeSplitViewRoutes } from 'core-app/features/work-packages/routing/split-view-routes.template'; +import { WorkPackageSplitViewComponent } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view.component'; export const menuItemClass = 'board-view-menu-item'; @@ -45,14 +45,14 @@ export const BOARDS_ROUTES:Ng2StateDeclaration[] = [ url: '/boards/?query_props', data: { bodyClasses: 'router--boards-view-base', - menuItem: menuItemClass + menuItem: menuItemClass, }, params: { // Use custom encoder/decoder that ensures validity of URL string - query_props: { type: 'opQueryString', dynamic: true } + query_props: { type: 'opQueryString', dynamic: true }, }, redirectTo: 'boards.list', - component: BoardsRootComponent + component: BoardsRootComponent, }, { name: 'boards.list', @@ -60,20 +60,20 @@ export const BOARDS_ROUTES:Ng2StateDeclaration[] = [ data: { parent: 'boards', bodyClasses: 'router--boards-list-view', - menuItem: menuItemClass - } + menuItem: menuItemClass, + }, }, { name: 'boards.partitioned', url: '{board_id}', params: { board_id: { type: 'int' }, - isNew: { type: 'bool', inherit: false, dynamic: true } + isNew: { type: 'bool', inherit: false, dynamic: true }, }, data: { parent: 'boards', bodyClasses: 'router--boards-full-view', - menuItem: menuItemClass + menuItem: menuItemClass, }, reloadOnSearch: false, component: BoardPartitionedPageComponent, @@ -83,17 +83,17 @@ export const BOARDS_ROUTES:Ng2StateDeclaration[] = [ name: 'boards.partitioned.show', url: '', data: { - baseRoute: 'boards.partitioned.show' + baseRoute: 'boards.partitioned.show', }, views: { - 'content-left': { component: BoardListContainerComponent } - } + 'content-left': { component: BoardListContainerComponent }, + }, }, ...makeSplitViewRoutes( 'boards.partitioned.show', menuItemClass, - WorkPackageSplitViewComponent - ) + WorkPackageSplitViewComponent, + ), ]; export function uiRouterBoardsConfiguration(uiRouter:UIRouter) { @@ -101,7 +101,7 @@ export function uiRouterBoardsConfiguration(uiRouter:UIRouter) { // cf., https://community.openproject.com/wp/29754 uiRouter.urlService.rules .when( - new RegExp("^/projects/(.*)/boards$"), - match => `/projects/${match[1]}/boards/` + new RegExp('^/projects/(.*)/boards$'), + (match) => `/projects/${match[1]}/boards/`, ); } diff --git a/frontend/src/app/features/boards/tile-view/tile-view.component.spec.ts b/frontend/src/app/features/boards/tile-view/tile-view.component.spec.ts index 52b0a23c57..357e8e20b9 100644 --- a/frontend/src/app/features/boards/tile-view/tile-view.component.spec.ts +++ b/frontend/src/app/features/boards/tile-view/tile-view.component.spec.ts @@ -1,9 +1,8 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; import { TileViewComponent } from './tile-view.component'; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; -import imagePath = ImageHelpers.imagePath; describe('shows tiles', () => { let app:TileViewComponent; @@ -17,7 +16,7 @@ describe('shows tiles', () => { image: imagePath('board_creation_modal/lists.svg'), description: `Create a board in which you can freely create lists and order your work packages within. - Moving work packages between lists do not change the work package itself.` + Moving work packages between lists do not change the work package itself.`, }]; beforeEach(() => { @@ -43,13 +42,11 @@ describe('shows tiles', () => { fixture.detectChanges(); const tile:HTMLElement = element.query(By.css('.tile-block')).nativeElement; expect(tile.textContent).toContain('Basic'); - }); it('should show the image', () => { fixture.detectChanges(); const tile = document.querySelector('.tile-block-image'); expect(document.contains(tile)).toBeTruthy(); - }); -}); \ No newline at end of file +}); diff --git a/frontend/src/app/features/boards/tile-view/tile-view.component.ts b/frontend/src/app/features/boards/tile-view/tile-view.component.ts index 72df33a46e..1ab4927874 100644 --- a/frontend/src/app/features/boards/tile-view/tile-view.component.ts +++ b/frontend/src/app/features/boards/tile-view/tile-view.component.ts @@ -1,31 +1,33 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { + ChangeDetectionStrategy, Component, EventEmitter, Input, Output, +} from '@angular/core'; export interface ITileViewEntry { - text:string; - attribute:string; - icon:string; - description:string; - image:string; - } + text:string; + attribute:string; + icon:string; + description:string; + image:string; +} - @Component({ - selector: 'tile-view', - styleUrls: ['./tile-view.component.sass'], - templateUrl: './tile-view.component.html', - changeDetection: ChangeDetectionStrategy.OnPush - }) +@Component({ + selector: 'tile-view', + styleUrls: ['./tile-view.component.sass'], + templateUrl: './tile-view.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) export class TileViewComponent { - @Input() public tiles:ITileViewEntry[]; - @Input() public disable = false; + @Input() public tiles:ITileViewEntry[]; - @Output() public create = new EventEmitter(); + @Input() public disable = false; - public disabled() { - return this.disable; - } + @Output() public create = new EventEmitter(); - public created(attribute:string) { - this.create.emit(attribute); - } + public disabled() { + return this.disable; + } + public created(attribute:string) { + this.create.emit(attribute); + } } diff --git a/frontend/src/app/features/dashboards/dashboard/dashboard.component.ts b/frontend/src/app/features/dashboards/dashboard/dashboard.component.ts index 276289cfbe..1373b3b65a 100644 --- a/frontend/src/app/features/dashboards/dashboard/dashboard.component.ts +++ b/frontend/src/app/features/dashboards/dashboard/dashboard.component.ts @@ -1,12 +1,12 @@ import { Component } from '@angular/core'; -import { GridPageComponent } from "core-app/shared/components/grids/grid/page/grid-page.component"; -import { GRID_PROVIDERS } from "core-app/shared/components/grids/grid/grid.component"; +import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component'; +import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component'; @Component({ selector: 'dashboard', templateUrl: '../../../shared/components/grids/grid/page/grid-page.component.html', styleUrls: ['../../../shared/components/grids/grid/page/grid-page.component.sass'], - providers: GRID_PROVIDERS + providers: GRID_PROVIDERS, }) export class DashboardComponent extends GridPageComponent { protected i18nNamespace():string { diff --git a/frontend/src/app/features/dashboards/openproject-dashboards.module.ts b/frontend/src/app/features/dashboards/openproject-dashboards.module.ts index 802421d13a..8948baaf8b 100644 --- a/frontend/src/app/features/dashboards/openproject-dashboards.module.ts +++ b/frontend/src/app/features/dashboards/openproject-dashboards.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import { NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { Ng2StateDeclaration, UIRouter, UIRouterModule } from "@uirouter/angular"; -import { DashboardComponent } from "core-app/features/dashboards/dashboard/dashboard.component"; -import { OpenprojectGridsModule } from "core-app/shared/components/grids/openproject-grids.module"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { Ng2StateDeclaration, UIRouter, UIRouterModule } from '@uirouter/angular'; +import { DashboardComponent } from 'core-app/features/dashboards/dashboard/dashboard.component'; +import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openproject-grids.module'; const menuItemClass = 'dashboards-menu-item'; @@ -43,10 +43,10 @@ export const DASHBOARDS_ROUTES:Ng2StateDeclaration[] = [ url: '/dashboards/', data: { bodyClasses: ['router--dashboards-view-base', 'widget-grid-layout'], - menuItem: menuItemClass + menuItem: menuItemClass, }, - component: DashboardComponent - } + component: DashboardComponent, + }, ]; export function uiRouterDashboardsConfiguration(uiRouter:UIRouter) { @@ -54,8 +54,8 @@ export function uiRouterDashboardsConfiguration(uiRouter:UIRouter) { // cf., https://community.openproject.com/wp/29754 uiRouter.urlService.rules .when( - new RegExp("^/projects/(.*)/dashboards$"), - match => `/projects/${match[1]}/dashboards/` + new RegExp('^/projects/(.*)/dashboards$'), + (match) => `/projects/${match[1]}/dashboards/`, ); } @@ -68,15 +68,14 @@ export function uiRouterDashboardsConfiguration(uiRouter:UIRouter) { // Routes for /dashboards UIRouterModule.forChild({ states: DASHBOARDS_ROUTES, - config: uiRouterDashboardsConfiguration + config: uiRouterDashboardsConfiguration, }), ], providers: [ ], declarations: [ - DashboardComponent - ] + DashboardComponent, + ], }) export class OpenprojectDashboardsModule { } - diff --git a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts index 4930df1930..8367fb2a82 100644 --- a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,28 +26,34 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EEActiveTrialBase } from "core-app/features/enterprise/enterprise-active-trial/ee-active-trial.base"; +import { Component, ElementRef } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EEActiveTrialBase } from 'core-app/features/enterprise/enterprise-active-trial/ee-active-trial.base'; export const enterpriseActiveSavedTrialSelector = 'enterprise-active-saved-trial'; @Component({ selector: enterpriseActiveSavedTrialSelector, templateUrl: './ee-active-trial.component.html', - styleUrls: ['./ee-active-trial.component.sass'] + styleUrls: ['./ee-active-trial.component.sass'], }) export class EEActiveSavedTrialComponent extends EEActiveTrialBase { - public subscriber = this.elementRef.nativeElement.dataset['subscriber']; - public email = this.elementRef.nativeElement.dataset['email']; - public company = this.elementRef.nativeElement.dataset['company']; - public domain = this.elementRef.nativeElement.dataset['domain']; - public userCount = this.elementRef.nativeElement.dataset['userCount']; - public startsAt = this.elementRef.nativeElement.dataset['startsAt']; - public expiresAt = this.elementRef.nativeElement.dataset['expiresAt']; + public subscriber = this.elementRef.nativeElement.dataset.subscriber; + + public email = this.elementRef.nativeElement.dataset.email; + + public company = this.elementRef.nativeElement.dataset.company; + + public domain = this.elementRef.nativeElement.dataset.domain; + + public userCount = this.elementRef.nativeElement.dataset.userCount; + + public startsAt = this.elementRef.nativeElement.dataset.startsAt; + + public expiresAt = this.elementRef.nativeElement.dataset.expiresAt; constructor(readonly elementRef:ElementRef, - readonly I18n:I18nService) { + readonly I18n:I18nService) { super(I18n); } } diff --git a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.base.ts b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.base.ts index f60a716c4b..e536c70ac9 100644 --- a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.base.ts +++ b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.base.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; export class EEActiveTrialBase extends UntilDestroyedMixin { public text = { @@ -37,7 +37,7 @@ export class EEActiveTrialBase extends UntilDestroyedMixin { label_company: this.I18n.t('js.admin.enterprise.trial.form.label_company'), label_domain: this.I18n.t('js.admin.enterprise.trial.form.label_domain'), label_starts_at: this.I18n.t('js.admin.enterprise.trial.form.label_starts_at'), - label_subscriber: this.I18n.t('js.admin.enterprise.trial.form.label_subscriber') + label_subscriber: this.I18n.t('js.admin.enterprise.trial.form.label_subscriber'), }; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.component.ts b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.component.ts index 4b70196bb6..4765c638bb 100644 --- a/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-active-trial/ee-active-trial.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,42 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, OnInit } from "@angular/core"; -import { distinctUntilChanged } from "rxjs/operators"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; -import { HttpClient, HttpErrorResponse } from "@angular/common/http"; -import { EEActiveTrialBase } from "core-app/features/enterprise/enterprise-active-trial/ee-active-trial.base"; -import { GonService } from "core-app/core/gon/gon.service"; +import { + ChangeDetectorRef, Component, ElementRef, OnInit, +} from '@angular/core'; +import { distinctUntilChanged } from 'rxjs/operators'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { EEActiveTrialBase } from 'core-app/features/enterprise/enterprise-active-trial/ee-active-trial.base'; +import { GonService } from 'core-app/core/gon/gon.service'; @Component({ selector: 'enterprise-active-trial', templateUrl: './ee-active-trial.component.html', - styleUrls: ['./ee-active-trial.component.sass'] + styleUrls: ['./ee-active-trial.component.sass'], }) export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit { public subscriber:string; + public email:string; + public company:string; + public domain:string; + public userCount:string; + public startsAt:string; + public expiresAt:string; constructor(readonly elementRef:ElementRef, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly http:HttpClient, - readonly Gon:GonService, - public eeTrialService:EnterpriseTrialService) { + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly http:HttpClient, + readonly Gon:GonService, + public eeTrialService:EnterpriseTrialService) { super(I18n); } @@ -63,9 +71,9 @@ export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit .values$() .pipe( distinctUntilChanged(), - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(userForm => { + .subscribe((userForm) => { this.formatUserData(userForm); this.cdRef.detectChanges(); }); @@ -79,7 +87,7 @@ export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit if (eeTrialKey && !this.eeTrialService.userData$.hasValue()) { // after reload: get data from Augur using the trial key saved in gon - this.eeTrialService.trialLink = this.eeTrialService.baseUrlAugur + '/public/v1/trials/' + eeTrialKey.value; + this.eeTrialService.trialLink = `${this.eeTrialService.baseUrlAugur}/public/v1/trials/${eeTrialKey.value}`; this.getUserDataFromAugur(); } } @@ -88,7 +96,7 @@ export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit // to get the user data from Augur private getUserDataFromAugur() { this.http - .get(this.eeTrialService.trialLink + '/details') + .get(`${this.eeTrialService.trialLink}/details`) .toPromise() .then((userForm:any) => { this.eeTrialService.userData$.putValue(userForm); @@ -101,10 +109,9 @@ export class EEActiveTrialComponent extends EEActiveTrialBase implements OnInit } private formatUserData(userForm:any) { - this.subscriber = userForm.first_name + ' ' + userForm.last_name; + this.subscriber = `${userForm.first_name} ${userForm.last_name}`; this.email = userForm.email; this.company = userForm.company; this.domain = userForm.domain; } - } diff --git a/frontend/src/app/features/enterprise/enterprise-base.component.ts b/frontend/src/app/features/enterprise/enterprise-base.component.ts index 88d1a4c89c..109e089e41 100644 --- a/frontend/src/app/features/enterprise/enterprise-base.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-base.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Injector } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EnterpriseTrialModal } from "core-app/features/enterprise/enterprise-modal/enterprise-trial.modal"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; +import { Component, Injector } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EnterpriseTrialModalComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial.modal'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; export const enterpriseBaseSelector = 'enterprise-base'; @Component({ selector: enterpriseBaseSelector, templateUrl: './enterprise-base.component.html', - styleUrls: ['./enterprise-base.component.sass'] + styleUrls: ['./enterprise-base.component.sass'], }) export class EnterpriseBaseComponent { public text = { @@ -49,20 +49,20 @@ export class EnterpriseBaseComponent { email_not_received: this.I18n.t('js.admin.enterprise.trial.email_not_received'), enterprise_edition: this.I18n.t('js.admin.enterprise.upsale.text'), confidence: this.I18n.t('js.admin.enterprise.upsale.confidence'), - try_another_email: this.I18n.t('js.admin.enterprise.trial.try_another_email') + try_another_email: this.I18n.t('js.admin.enterprise.trial.try_another_email'), }; constructor(protected I18n:I18nService, - protected opModalService:OpModalService, - readonly injector:Injector, - public eeTrialService:EnterpriseTrialService) { + protected opModalService:OpModalService, + readonly injector:Injector, + public eeTrialService:EnterpriseTrialService) { } public openTrialModal() { // cancel request and open first modal window this.eeTrialService.cancelled = true; this.eeTrialService.modalOpen = true; - this.opModalService.show(EnterpriseTrialModal, this.injector); + this.opModalService.show(EnterpriseTrialModalComponent, this.injector); } public get noTrialRequested() { diff --git a/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component.ts b/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component.ts index 5548a47467..6367c40899 100644 --- a/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Component, ElementRef } from "@angular/core"; -import { FormBuilder, Validators } from "@angular/forms"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EnterpriseTrialData, EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { I18nHelpers } from "core-app/shared/helpers/i18n/localized-link"; +import { Component, ElementRef } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EnterpriseTrialData, EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { I18nHelpers } from 'core-app/shared/helpers/i18n/localized-link'; const newsletterURL = 'https://www.openproject.com/newsletter/'; @Component({ selector: 'enterprise-trial-form', templateUrl: './ee-trial-form.component.html', - styleUrls: ['./ee-trial-form.component.sass'] + styleUrls: ['./ee-trial-form.component.sass'], }) export class EETrialFormComponent { // Retain used values @@ -53,7 +52,7 @@ export class EETrialFormComponent { domain: [this.userData.domain || window.location.host, Validators.required], general_consent: [null, Validators.required], newsletter_consent: null, - language: this.currentUserService.language + language: this.currentUserService.language, }); public text = { @@ -64,8 +63,8 @@ export class EETrialFormComponent { }), link_privacy: I18nHelpers.localizeLink({ en: 'https://www.openproject.org/data-privacy-and-security/', - de: 'https://www.openproject.org/de/datenschutz/' - }) + de: 'https://www.openproject.org/de/datenschutz/', + }), }), label_test_ee: this.I18n.t('js.admin.enterprise.trial.form.test_ee'), label_company: this.I18n.t('js.admin.enterprise.trial.form.label_company'), @@ -75,14 +74,14 @@ export class EETrialFormComponent { label_domain: this.I18n.t('js.admin.enterprise.trial.form.label_domain'), privacy_policy: this.I18n.t('js.admin.enterprise.trial.form.privacy_policy'), receive_newsletter: this.I18n.t('js.admin.enterprise.trial.form.receive_newsletter', { link: newsletterURL }), - terms_of_service: this.I18n.t('js.admin.enterprise.trial.form.terms_of_service') + terms_of_service: this.I18n.t('js.admin.enterprise.trial.form.terms_of_service'), }; constructor(readonly elementRef:ElementRef, - readonly I18n:I18nService, - private formBuilder:FormBuilder, - readonly currentUserService:CurrentUserService, - public eeTrialService:EnterpriseTrialService) { + readonly I18n:I18nService, + private formBuilder:FormBuilder, + readonly currentUserService:CurrentUserService, + public eeTrialService:EnterpriseTrialService) { } diff --git a/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial.modal.ts b/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial.modal.ts index 38523a2081..ea5bdf20ea 100644 --- a/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial.modal.ts +++ b/frontend/src/app/features/enterprise/enterprise-modal/enterprise-trial.modal.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,35 +26,41 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, Input, ViewChild } from "@angular/core"; -import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; -import { FormControl, FormGroup } from "@angular/forms"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EETrialFormComponent } from "core-app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component"; -import { EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; +import { + AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, Input, ViewChild, +} from '@angular/core'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { FormControl, FormGroup } from '@angular/forms'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EETrialFormComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component'; +import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; export const eeOnboardingVideoURL = 'https://www.youtube.com/embed/zLMSydhFSkw?autoplay=1'; @Component({ selector: 'enterprise-trial-modal', templateUrl: './enterprise-trial.modal.html', - styleUrls: ['./enterprise-trial.modal.sass'] + styleUrls: ['./enterprise-trial.modal.sass'], }) -export class EnterpriseTrialModal extends OpModalComponent implements AfterViewInit { +export class EnterpriseTrialModalComponent extends OpModalComponent implements AfterViewInit { @ViewChild(EETrialFormComponent, { static: false }) formComponent:EETrialFormComponent; + @Input() public opReferrer:string; public trialForm:FormGroup; // modal configuration public showClose = true; + public closeOnEscape = false; + public closeOnOutsideClick = false; public trustedEEVideoURL:SafeResourceUrl; + public text = { button_submit: this.I18n.t('js.modals.button_submit'), button_cancel: this.I18n.t('js.modals.button_cancel'), @@ -63,15 +69,15 @@ export class EnterpriseTrialModal extends OpModalComponent implements AfterViewI heading_confirmation: this.I18n.t('js.admin.enterprise.trial.confirmation'), heading_next_steps: this.I18n.t('js.admin.enterprise.trial.next_steps'), heading_test_ee: this.I18n.t('js.admin.enterprise.trial.test_ee'), - quick_overview: this.I18n.t('js.admin.enterprise.trial.quick_overview') + quick_overview: this.I18n.t('js.admin.enterprise.trial.quick_overview'), }; constructor(readonly elementRef:ElementRef, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly domSanitizer:DomSanitizer, - public eeTrialService:EnterpriseTrialService) { + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly domSanitizer:DomSanitizer, + public eeTrialService:EnterpriseTrialService) { super(locals, cdRef, elementRef); this.trustedEEVideoURL = this.trustedURL(eeOnboardingVideoURL); } @@ -96,11 +102,10 @@ export class EnterpriseTrialModal extends OpModalComponent implements AfterViewI public headerText() { if (this.eeTrialService.mailSubmitted) { return this.text.heading_confirmation; - } else if (this.eeTrialService.trialStarted) { + } if (this.eeTrialService.trialStarted) { return this.text.heading_next_steps; - } else { - return this.text.heading_test_ee; } + return this.text.heading_test_ee; } public closeModal(event:any) { @@ -119,11 +124,9 @@ export class EnterpriseTrialModal extends OpModalComponent implements AfterViewI public openWindow():number { if (!this.eeTrialService.status || this.eeTrialService.cancelled) { return 1; - } else if (this.eeTrialService.mailSubmitted && !this.eeTrialService.cancelled) { + } if (this.eeTrialService.mailSubmitted && !this.eeTrialService.cancelled) { return 2; - } else { - return 3; } + return 3; } } - diff --git a/frontend/src/app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component.ts b/frontend/src/app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component.ts index f64aa8d8bc..05848baef7 100644 --- a/frontend/src/app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component.ts +++ b/frontend/src/app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,27 +26,28 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, OnInit } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; -import { HttpClient } from "@angular/common/http"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { distinctUntilChanged } from "rxjs/operators"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { Component, ElementRef, OnInit } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; +import { HttpClient } from '@angular/common/http'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { distinctUntilChanged } from 'rxjs/operators'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ selector: 'enterprise-trial-waiting', templateUrl: './ee-trial-waiting.component.html', - styleUrls: ['./ee-trial-waiting.component.sass'] + styleUrls: ['./ee-trial-waiting.component.sass'], }) export class EETrialWaitingComponent implements OnInit { created = this.timezoneService.formattedDate(new Date().toString()); + email = ''; public text = { - confirmation_info: (date:string, email:string) => this.I18n.t('js.admin.enterprise.trial.confirmation_info',{ - date: date, - email: email + confirmation_info: (date:string, email:string) => this.I18n.t('js.admin.enterprise.trial.confirmation_info', { + date, + email, }), resend: this.I18n.t('js.admin.enterprise.trial.resend_link'), resend_success: this.I18n.t('js.admin.enterprise.trial.resend_success'), @@ -54,15 +55,15 @@ export class EETrialWaitingComponent implements OnInit { session_timeout: this.I18n.t('js.admin.enterprise.trial.session_timeout'), status_confirmed: this.I18n.t('js.admin.enterprise.trial.status_confirmed'), status_label: this.I18n.t('js.admin.enterprise.trial.status_label'), - status_waiting: this.I18n.t('js.admin.enterprise.trial.status_waiting') + status_waiting: this.I18n.t('js.admin.enterprise.trial.status_waiting'), }; constructor(readonly elementRef:ElementRef, - readonly I18n:I18nService, - protected http:HttpClient, - protected notificationsService:NotificationsService, - public eeTrialService:EnterpriseTrialService, - readonly timezoneService:TimezoneService) { + readonly I18n:I18nService, + protected http:HttpClient, + protected notificationsService:NotificationsService, + public eeTrialService:EnterpriseTrialService, + readonly timezoneService:TimezoneService) { } ngOnInit() { @@ -77,7 +78,7 @@ export class EETrialWaitingComponent implements OnInit { .pipe( distinctUntilChanged(), ) - .subscribe(userForm => { + .subscribe((userForm) => { this.email = userForm.email; }); } @@ -101,4 +102,3 @@ export class EETrialWaitingComponent implements OnInit { }); } } - diff --git a/frontend/src/app/features/enterprise/enterprise-trial.service.ts b/frontend/src/app/features/enterprise/enterprise-trial.service.ts index 681291c02c..9decfa6e4c 100644 --- a/frontend/src/app/features/enterprise/enterprise-trial.service.ts +++ b/frontend/src/app/features/enterprise/enterprise-trial.service.ts @@ -1,10 +1,10 @@ -import { Injectable } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { FormGroup } from "@angular/forms"; -import { input } from "reactivestates"; +import { Injectable } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { FormGroup } from '@angular/forms'; +import { input } from 'reactivestates'; export interface EnterpriseTrialData { id?:string; @@ -23,17 +23,25 @@ export class EnterpriseTrialService { userData$ = input(); public readonly baseUrlAugur:string; + public readonly tokenVersion:string; public trialLink:string; + public resendLink:string; public modalOpen = false; + public confirmed:boolean; + public cancelled = false; + public status:'mailSubmitted'|'startTrial'|undefined; + public error:HttpErrorResponse|undefined; + public emailInvalid = false; + public text = { invalid_email: this.I18n.t('js.admin.enterprise.trial.form.invalid_email'), taken_email: this.I18n.t('js.admin.enterprise.trial.form.taken_email'), @@ -41,10 +49,10 @@ export class EnterpriseTrialService { }; constructor(readonly I18n:I18nService, - protected http:HttpClient, - readonly pathHelper:PathHelperService, - protected notificationsService:NotificationsService) { - const gon = (window as any).gon; + protected http:HttpClient, + readonly pathHelper:PathHelperService, + protected notificationsService:NotificationsService) { + const { gon } = window as any; this.baseUrlAugur = gon.augur_url; this.tokenVersion = gon.token_version; @@ -57,7 +65,7 @@ export class EnterpriseTrialService { // receive an enterprise trial link to access a token public sendForm(form:FormGroup) { const request = { ...form.value, token_version: this.tokenVersion }; - this.http.post(this.baseUrlAugur + '/public/v1/trials', request) + this.http.post(`${this.baseUrlAugur}/public/v1/trials`, request) .toPromise() .then((enterpriseTrial:any) => { this.userData$.putValue(form.value); @@ -121,9 +129,9 @@ export class EnterpriseTrialService { // extract token from resend link const trialKey = resendlink.split('/')[6]; return this.http.post( - this.pathHelper.appBasePath + '/admin/enterprise/save_trial_key', + `${this.pathHelper.appBasePath}/admin/enterprise/save_trial_key`, { trial_key: trialKey }, - { withCredentials: true } + { withCredentials: true }, ) .toPromise() .catch((e:any) => { @@ -134,9 +142,9 @@ export class EnterpriseTrialService { // save received token in controller private saveToken(token:string) { return this.http.post( - this.pathHelper.appBasePath + '/admin/enterprise', + `${this.pathHelper.appBasePath}/admin/enterprise`, { enterprise_token: { encoded_token: token } }, - { withCredentials: true } + { withCredentials: true }, ) .toPromise() .then(() => { @@ -152,8 +160,8 @@ export class EnterpriseTrialService { // Without this deletion, we run into an endless loop of an confirmed mail, but no saved token. this.http .delete( - this.pathHelper.api.v3.apiV3Base + '/admin/enterprise/delete_trial_key', - { withCredentials: true } + `${this.pathHelper.api.v3.apiV3Base}/admin/enterprise/delete_trial_key`, + { withCredentials: true }, ) .toPromise(); @@ -164,7 +172,7 @@ export class EnterpriseTrialService { // retry request while waiting for mail confirmation public retryConfirmation(delay = 5000, retries = 60) { if (this.cancelled || this.confirmed) { - return; + } else if (retries === 0) { this.cancelled = true; } else { @@ -202,11 +210,10 @@ export class EnterpriseTrialService { public get emailError():boolean { if (this.emailInvalid) { return true; - } else if (this.error) { + } if (this.error) { return this.emailTaken; - } else { - return false; } + return false; } public get errorMsg() { diff --git a/frontend/src/app/features/enterprise/openproject-enterprise.module.ts b/frontend/src/app/features/enterprise/openproject-enterprise.module.ts index 4bcb38228f..c20b25f61b 100644 --- a/frontend/src/app/features/enterprise/openproject-enterprise.module.ts +++ b/frontend/src/app/features/enterprise/openproject-enterprise.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,16 +27,16 @@ //++ import { NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { EnterpriseTrialService } from "core-app/features/enterprise/enterprise-trial.service"; -import { EnterpriseBaseComponent } from "core-app/features/enterprise/enterprise-base.component"; -import { EnterpriseTrialModal } from "core-app/features/enterprise/enterprise-modal/enterprise-trial.modal"; -import { EETrialFormComponent } from "core-app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component"; -import { EETrialWaitingComponent } from "core-app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component"; -import { EEActiveTrialComponent } from "core-app/features/enterprise/enterprise-active-trial/ee-active-trial.component"; -import { EEActiveSavedTrialComponent } from "core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { EnterpriseTrialService } from 'core-app/features/enterprise/enterprise-trial.service'; +import { EnterpriseBaseComponent } from 'core-app/features/enterprise/enterprise-base.component'; +import { EnterpriseTrialModalComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial.modal'; +import { EETrialFormComponent } from 'core-app/features/enterprise/enterprise-modal/enterprise-trial-form/ee-trial-form.component'; +import { EETrialWaitingComponent } from 'core-app/features/enterprise/enterprise-trial-waiting/ee-trial-waiting.component'; +import { EEActiveTrialComponent } from 'core-app/features/enterprise/enterprise-active-trial/ee-active-trial.component'; +import { EEActiveSavedTrialComponent } from 'core-app/features/enterprise/enterprise-active-trial/ee-active-saved-trial.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ imports: [ @@ -46,16 +46,16 @@ import { FormsModule, ReactiveFormsModule } from "@angular/forms"; ReactiveFormsModule, ], providers: [ - EnterpriseTrialService + EnterpriseTrialService, ], declarations: [ EnterpriseBaseComponent, - EnterpriseTrialModal, + EnterpriseTrialModalComponent, EETrialFormComponent, EETrialWaitingComponent, EEActiveTrialComponent, EEActiveSavedTrialComponent, - ] + ], }) export class OpenprojectEnterpriseModule { } diff --git a/frontend/src/app/features/hal/hal-link/hal-link.ts b/frontend/src/app/features/hal/hal-link/hal-link.ts index 6fc1705141..57cd55f145 100644 --- a/frontend/src/app/features/hal/hal-link/hal-link.ts +++ b/frontend/src/app/features/hal/hal-link/hal-link.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HTTPSupportedMethods } from "core-app/features/hal/http/http.interfaces"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HTTPSupportedMethods } from 'core-app/features/hal/http/http.interfaces'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; export interface HalLinkInterface { href:string|null; @@ -52,13 +52,13 @@ export interface CallableHalLink extends HalLinkInterface { export class HalLink implements HalLinkInterface { constructor(public requestMethod:(method:HTTPSupportedMethods, href:string, data:any, headers:any) => Promise, - public href:string|null = null, - public title:string = '', - public method:HTTPSupportedMethods = 'get', - public templated:boolean = false, - public payload?:any, - public type:string = 'application/json', - public identifier?:string) { + public href:string|null = null, + public title:string = '', + public method:HTTPSupportedMethods = 'get', + public templated:boolean = false, + public payload?:any, + public type:string = 'application/json', + public identifier?:string) { } /** @@ -66,15 +66,14 @@ export class HalLink implements HalLinkInterface { */ public static fromObject(halResourceService:HalResourceService, link:HalLinkInterface):HalLink { return new HalLink( - (method:HTTPSupportedMethods, href:string, data:any, headers:any) => - halResourceService.request(method, href, data, headers).toPromise(), + (method:HTTPSupportedMethods, href:string, data:any, headers:any) => halResourceService.request(method, href, data, headers).toPromise(), link.href, link.title, link.method, link.templated, link.payload, link.type, - link.identifier + link.identifier, ); } @@ -93,12 +92,12 @@ export class HalLink implements HalLinkInterface { */ public $prepare(templateValues:{ [templateKey:string]:string }) { if (!this.templated) { - throw 'The link ' + this.href + ' is not templated.'; + throw new Error(`The link ${this.href} is not templated.`); } let href = _.clone(this.href) || ''; _.each(templateValues, (value:string, key:string) => { - const regexp = new RegExp('{' + key + '}'); + const regexp = new RegExp(`{${key}}`); href = href.replace(regexp, value); }); @@ -110,7 +109,7 @@ export class HalLink implements HalLinkInterface { false, this.payload, this.type, - this.identifier + this.identifier, ).$callable(); } diff --git a/frontend/src/app/features/hal/helpers/hal-resource-builder.ts b/frontend/src/app/features/hal/helpers/hal-resource-builder.ts index 59c6a732f7..036d699ed7 100644 --- a/frontend/src/app/features/hal/helpers/hal-resource-builder.ts +++ b/frontend/src/app/features/hal/helpers/hal-resource-builder.ts @@ -1,8 +1,8 @@ import * as ObservableArray from 'observable-array'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { OpenprojectHalModuleHelpers } from "core-app/features/hal/helpers/lazy-accessor"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { OpenprojectHalModuleHelpers } from 'core-app/features/hal/helpers/lazy-accessor'; interface HalSource { _links:any; @@ -14,17 +14,15 @@ interface HalSource { export function cloneHalResourceCollection(values:T[]|undefined):T[] { if (_.isNil(values)) { return []; - } else { - return values.map(v => v.$copy()); } + return values.map((v) => v.$copy()); } export function cloneHalResource(value:T|undefined):T|undefined { if (_.isNil(value)) { return value; - } else { - return value.$copy(); } + return value.$copy(); } export function initializeHalProperties(halResourceService:HalResourceService, halResource:T) { @@ -70,7 +68,7 @@ export function initializeHalProperties(halResourceServic }, enumerable: true, - configurable: true + configurable: true, }); }); } @@ -82,17 +80,17 @@ export function initializeHalProperties(halResourceServic const link:any = halResource.$links[linkName].$link || halResource.$links[linkName]; if (Array.isArray(link)) { - var items = link.map(item => halResourceService.createLinkedResource(halResource, + const items = link.map((item) => halResourceService.createLinkedResource(halResource, linkName, item.$link)); var property:HalResource[] = new ObservableArray(...items).on('change', () => { - property.forEach(item => { + property.forEach((item) => { if (!item.$link) { property.splice(property.indexOf(item), 1); } }); - halResource.$source._links[linkName] = property.map(item => item.$link); + halResource.$source._links[linkName] = property.map((item) => item.$link); }); return property; @@ -108,8 +106,7 @@ export function initializeHalProperties(halResourceServic return null; }, - (val:any) => setter(val, linkName) - ); + (val:any) => setter(val, linkName)); }); } @@ -118,7 +115,7 @@ export function initializeHalProperties(halResourceServic return; } - Object.keys(halResource.$source._embedded).forEach(name => { + Object.keys(halResource.$source._embedded).forEach((name) => { OpenprojectHalModuleHelpers.lazy(halResource, name, () => halResource.$embedded[name], @@ -127,12 +124,12 @@ export function initializeHalProperties(halResourceServic } function setupProperty(name:string, callback:(element:any) => any) { - const instanceName = '$' + name; - const sourceName = '_' + name; + const instanceName = `$${name}`; + const sourceName = `_${name}`; const sourceObj:any = halResource.$source[sourceName]; if (_.isObject(sourceObj)) { - Object.keys(sourceObj).forEach(propName => { + Object.keys(sourceObj).forEach((propName) => { OpenprojectHalModuleHelpers.lazy((halResource)[instanceName], propName, () => callback((sourceObj as any)[propName])); @@ -144,16 +141,14 @@ export function initializeHalProperties(halResourceServic setupProperty('links', (link) => { if (Array.isArray(link)) { - return link.map(l => HalLink.fromObject(halResourceService, l).$callable()); - } else { - return HalLink.fromObject(halResourceService, link).$callable(); + return link.map((l) => HalLink.fromObject(halResourceService, l).$callable()); } + return HalLink.fromObject(halResourceService, link).$callable(); }); } function setupEmbedded() { setupProperty('embedded', (element:any) => { - if (Array.isArray(element)) { return element.map((source) => asHalResource(source, true)); } @@ -178,9 +173,7 @@ export function initializeHalProperties(halResourceServic if (!val) { halResource.$source._links[linkName] = { href: null }; } else if (isArray) { - halResource.$source._links[linkName] = (val as HalResource[]).map((el:any) => { - return { href: el.href }; - }); + halResource.$source._links[linkName] = (val as HalResource[]).map((el:any) => ({ href: el.href })); } else if (val.hasOwnProperty('$link')) { const link = (val as HalResource).$link; @@ -195,7 +188,7 @@ export function initializeHalProperties(halResourceServic halResource.$embedded[linkName] = val; if (isArray) { - halResource.$source._embedded[linkName] = (val as HalResource[]).map(el => el.$source); + halResource.$source._embedded[linkName] = (val as HalResource[]).map((el) => el.$source); } else { halResource.$source._embedded[linkName] = _.get(val, '$source', val); } diff --git a/frontend/src/app/features/hal/helpers/lazy-accessor.spec.ts b/frontend/src/app/features/hal/helpers/lazy-accessor.spec.ts index 57a5d421d8..e07c3b5061 100644 --- a/frontend/src/app/features/hal/helpers/lazy-accessor.spec.ts +++ b/frontend/src/app/features/hal/helpers/lazy-accessor.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { OpenprojectHalModuleHelpers } from "core-app/features/hal/helpers/lazy-accessor"; +import { OpenprojectHalModuleHelpers } from 'core-app/features/hal/helpers/lazy-accessor'; describe('lazy service', () => { - var lazy = OpenprojectHalModuleHelpers.lazy; - + const { lazy } = OpenprojectHalModuleHelpers; it('should exist', () => { expect(lazy).toBeDefined(); @@ -38,7 +37,7 @@ describe('lazy service', () => { it('should add a property with the given name to the object', () => { const obj:any = { - prop: void 0 + prop: void 0, }; lazy(obj, 'prop', () => ''); expect(obj.prop).toBeDefined(); @@ -46,7 +45,7 @@ describe('lazy service', () => { it('should add an enumerable property', () => { const obj:any = { - prop: void 0 + prop: void 0, }; lazy(obj, 'prop', () => ''); expect(obj.propertyIsEnumerable('prop')).toBeTruthy(); @@ -54,7 +53,7 @@ describe('lazy service', () => { it('should add a configurable property', () => { const obj:any = { - prop: void 0 + prop: void 0, }; lazy(obj, 'prop', () => ''); expect((Object as any).getOwnPropertyDescriptor(obj, 'prop').configurable).toBeTruthy(); @@ -62,7 +61,7 @@ describe('lazy service', () => { it('should set the value of the property provided by the setter', () => { const obj:any = { - prop: void 0 + prop: void 0, }; lazy(obj, 'prop', () => '', (val:any) => val); obj.prop = 'hello'; @@ -71,7 +70,7 @@ describe('lazy service', () => { it('should not be settable, if no setter is provided', () => { const obj:any = { - prop: void 0 + prop: void 0, }; lazy(obj, 'prop', () => ''); try { diff --git a/frontend/src/app/features/hal/helpers/lazy-accessor.ts b/frontend/src/app/features/hal/helpers/lazy-accessor.ts index 401500ec5d..a11fa5234b 100644 --- a/frontend/src/app/features/hal/helpers/lazy-accessor.ts +++ b/frontend/src/app/features/hal/helpers/lazy-accessor.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export namespace OpenprojectHalModuleHelpers { export function lazy(obj:HalResource, property:string, getter:{ ():any }, setter?:{ (value:any):void }):void { - if (_.isObject(obj)) { let done = false; let value:any; @@ -49,7 +47,7 @@ export namespace OpenprojectHalModuleHelpers { set: ():void => undefined, configurable: true, - enumerable: true + enumerable: true, }; if (setter) { diff --git a/frontend/src/app/features/hal/http/http.interfaces.ts b/frontend/src/app/features/hal/http/http.interfaces.ts index 256238c294..bf729a05e3 100644 --- a/frontend/src/app/features/hal/http/http.interfaces.ts +++ b/frontend/src/app/features/hal/http/http.interfaces.ts @@ -1,4 +1,4 @@ -import { HttpHeaders, HttpParams } from "@angular/common/http"; +import { HttpHeaders, HttpParams } from '@angular/common/http'; export type HTTPSupportedMethods = 'get'|'post'|'put'|'patch'|'delete'; @@ -14,4 +14,4 @@ export interface HTTPClientOptions { export type HTTPClientParamMap = { [key:string]:any }; export type HTTPClientHeaders = HttpHeaders|HTTPClientParamMap; -export type HTTPClientParams = HttpParams|HTTPClientParamMap; \ No newline at end of file +export type HTTPClientParams = HttpParams|HTTPClientParamMap; diff --git a/frontend/src/app/features/hal/http/openproject-header-interceptor.ts b/frontend/src/app/features/hal/http/openproject-header-interceptor.ts index 260a05fe26..058cbda20c 100644 --- a/frontend/src/app/features/hal/http/openproject-header-interceptor.ts +++ b/frontend/src/app/features/hal/http/openproject-header-interceptor.ts @@ -1,11 +1,8 @@ import { - HttpEvent, - HttpInterceptor, - HttpHandler, - HttpRequest, + HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; @Injectable() export class OpenProjectHeaderInterceptor implements HttpInterceptor { @@ -13,19 +10,18 @@ export class OpenProjectHeaderInterceptor implements HttpInterceptor { const csrf_token:string|undefined = jQuery('meta[name=csrf-token]').attr('content'); if (req.withCredentials !== false) { - let newHeaders = req.headers .set('X-Authentication-Scheme', 'Session') .set('X-Requested-With', 'XMLHttpRequest'); if (csrf_token) { - newHeaders = newHeaders.set('X-CSRF-TOKEN', csrf_token); + newHeaders = newHeaders.set('X-CSRF-TOKEN', csrf_token); } // Clone the request to add the new header const clonedRequest = req.clone({ withCredentials: true, - headers: newHeaders + headers: newHeaders, }); // Pass the cloned request instead of the original request to the next handle diff --git a/frontend/src/app/features/hal/openproject-hal.module.ts b/frontend/src/app/features/hal/openproject-hal.module.ts index 071fc4f25a..85e95342b6 100644 --- a/frontend/src/app/features/hal/openproject-hal.module.ts +++ b/frontend/src/app/features/hal/openproject-hal.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,12 +28,12 @@ import { APP_INITIALIZER, ErrorHandler, NgModule } from '@angular/core'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; -import { CommonModule } from "@angular/common"; -import { OpenProjectHeaderInterceptor } from "core-app/features/hal/http/openproject-header-interceptor"; -import { HalAwareErrorHandler } from "core-app/features/hal/services/hal-aware-error-handler"; -import { initializeHalResourceConfig } from "core-app/features/hal/services/hal-resource.config"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { CommonModule } from '@angular/common'; +import { OpenProjectHeaderInterceptor } from 'core-app/features/hal/http/openproject-header-interceptor'; +import { HalAwareErrorHandler } from 'core-app/features/hal/services/hal-aware-error-handler'; +import { initializeHalResourceConfig } from 'core-app/features/hal/services/hal-resource.config'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; @NgModule({ imports: [ @@ -43,9 +43,10 @@ import { HalResourceNotificationService } from "core-app/features/hal/services/h providers: [ { provide: ErrorHandler, useClass: HalAwareErrorHandler }, { provide: HTTP_INTERCEPTORS, useClass: OpenProjectHeaderInterceptor, multi: true }, - { provide: APP_INITIALIZER, useFactory: initializeHalResourceConfig, deps: [HalResourceService], multi: true }, + { + provide: APP_INITIALIZER, useFactory: initializeHalResourceConfig, deps: [HalResourceService], multi: true, + }, HalResourceNotificationService, - ] + ], }) export class OpenprojectHalModule { } - diff --git a/frontend/src/app/features/hal/resources/attachment-collection-resource.ts b/frontend/src/app/features/hal/resources/attachment-collection-resource.ts index dd863f686c..6001aed59d 100644 --- a/frontend/src/app/features/hal/resources/attachment-collection-resource.ts +++ b/frontend/src/app/features/hal/resources/attachment-collection-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export class AttachmentCollectionResource extends CollectionResource { public $initialize(source:any) { @@ -35,10 +35,8 @@ export class AttachmentCollectionResource extends CollectionResource { this.elements = this.elements || []; } - } export interface AttachmentCollectionResource { elements:HalResource[]; } - diff --git a/frontend/src/app/features/hal/resources/capability-resource.ts b/frontend/src/app/features/hal/resources/capability-resource.ts index bea416d361..aeb3f32b1d 100644 --- a/frontend/src/app/features/hal/resources/capability-resource.ts +++ b/frontend/src/app/features/hal/resources/capability-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,6 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class CapabilityResource extends HalResource {} diff --git a/frontend/src/app/features/hal/resources/collection-resource.ts b/frontend/src/app/features/hal/resources/collection-resource.ts index caf6fa83c7..7d12efd3d8 100644 --- a/frontend/src/app/features/hal/resources/collection-resource.ts +++ b/frontend/src/app/features/hal/resources/collection-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class CollectionResource extends HalResource { public elements:T[]; + public count:number; + public total:number; + public pageSize:number; + public offset:number; /** @@ -42,8 +46,7 @@ export class CollectionResource extends HalResource { public updateElements():Promise { if (this.href) { return this.$load().then((collection:this) => this.elements = collection.elements); - } else { - return Promise.resolve(); } + return Promise.resolve(); } } diff --git a/frontend/src/app/features/hal/resources/configuration-resource.ts b/frontend/src/app/features/hal/resources/configuration-resource.ts index 45717e5eb7..f4b38de13a 100644 --- a/frontend/src/app/features/hal/resources/configuration-resource.ts +++ b/frontend/src/app/features/hal/resources/configuration-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class ConfigurationResource extends HalResource { - public perPageOptions:Array; } diff --git a/frontend/src/app/features/hal/resources/custom-action-resource.ts b/frontend/src/app/features/hal/resources/custom-action-resource.ts index f10dce313c..419519d700 100644 --- a/frontend/src/app/features/hal/resources/custom-action-resource.ts +++ b/frontend/src/app/features/hal/resources/custom-action-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export interface CustomActionResourceLinks { self():Promise; diff --git a/frontend/src/app/features/hal/resources/error-resource.ts b/frontend/src/app/features/hal/resources/error-resource.ts index afdac5cf33..49f1d890a0 100644 --- a/frontend/src/app/features/hal/resources/error-resource.ts +++ b/frontend/src/app/features/hal/resources/error-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HttpErrorResponse } from "@angular/common/http"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HttpErrorResponse } from '@angular/common/http'; export const v3ErrorIdentifierQueryInvalid = 'urn:openproject-org:api:v3:errors:InvalidQuery'; export const v3ErrorIdentifierMultipleErrors = 'urn:openproject-org:api:v3:errors:MultipleErrors'; export class ErrorResource extends HalResource { public errors:any[]; + public message:string; + public details:any; + public errorIdentifier:string; /** We may get a reference to the underlying http error */ @@ -53,7 +56,7 @@ export class ErrorResource extends HalResource { public get errorMessages():string[] { if (this.isMultiErrorMessage()) { - return this.errors.map(error => error.message); + return this.errors.map((error) => error.message); } return [this.message]; @@ -64,7 +67,7 @@ export class ErrorResource extends HalResource { } public getInvolvedAttributes():string[] { - var columns = []; + let columns = []; if (this.details) { columns = [{ details: this.details }]; @@ -75,9 +78,8 @@ export class ErrorResource extends HalResource { return _.flatten(columns.map((resource:ErrorResource) => { if (resource.errorIdentifier === v3ErrorIdentifierMultipleErrors) { return this.extractMultiError(resource)[0]; - } else { - return resource.details.attribute; } + return resource.details.attribute; })); } @@ -104,7 +106,7 @@ export class ErrorResource extends HalResource { } protected extractMultiError(resource:ErrorResource):[string, string[]] { - const attribute = resource.errors[0].details.attribute; + const { attribute } = resource.errors[0].details; const messages = resource.errors.map((el:ErrorResource) => el.message); return [attribute, messages]; diff --git a/frontend/src/app/features/hal/resources/form-resource.ts b/frontend/src/app/features/hal/resources/form-resource.ts index 902bce60db..69e245854a 100644 --- a/frontend/src/app/features/hal/resources/form-resource.ts +++ b/frontend/src/app/features/hal/resources/form-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ErrorResource, v3ErrorIdentifierMultipleErrors } from "core-app/features/hal/resources/error-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; - +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ErrorResource, v3ErrorIdentifierMultipleErrors } from 'core-app/features/hal/resources/error-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export interface FormResourceLinks { commit(payload:any):Promise; @@ -42,6 +41,7 @@ export interface FormResourceEmbedded { export class FormResource extends HalResource { public schema:SchemaResource; + public validationErrors:{ [attribute:string]:ErrorResource }; public getErrors():ErrorResource|null { diff --git a/frontend/src/app/features/hal/resources/grid-resource.ts b/frontend/src/app/features/hal/resources/grid-resource.ts index 5719d5f3b4..b74f9887e2 100644 --- a/frontend/src/app/features/hal/resources/grid-resource.ts +++ b/frontend/src/app/features/hal/resources/grid-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; export interface GridResourceLinks { update(payload:unknown):Promise; @@ -38,8 +38,11 @@ export interface GridResourceLinks { export class GridBaseResource extends HalResource { public widgets:GridWidgetResource[]; - public options:{[key:string]:unknown}; + + public options:{ [key:string]:unknown }; + public rowCount:number; + public columnCount:number; public $initialize(source:any) { @@ -48,12 +51,11 @@ export class GridBaseResource extends HalResource { this.widgets = this .widgets .map((widget:Object) => { - const widgetResource = new GridWidgetResource( this.injector, + const widgetResource = new GridWidgetResource(this.injector, widget, true, this.halInitializer, - 'GridWidget' - ); + 'GridWidget'); widgetResource.grid = this; @@ -71,7 +73,6 @@ export class GridBaseResource extends HalResource { } } - export const GridResource = Attachable(GridBaseResource); export interface GridResource extends Partial, GridBaseResource { diff --git a/frontend/src/app/features/hal/resources/grid-widget-resource.ts b/frontend/src/app/features/hal/resources/grid-widget-resource.ts index 456d61a647..cf7338388f 100644 --- a/frontend/src/app/features/hal/resources/grid-widget-resource.ts +++ b/frontend/src/app/features/hal/resources/grid-widget-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; export class GridWidgetResource extends HalResource { @InjectField() protected halResource:HalResourceService; + public identifier:string; + public startRow:number; + public endRow:number; + public startColumn:number; + public endColumn:number; - public options:{[key:string]:unknown}; + public options:{ [key:string]:unknown }; public get height() { return this.endRow - this.startRow; @@ -53,6 +58,6 @@ export class GridWidgetResource extends HalResource { public grid:GridResource; public get schema():SchemaResource { - return this.halResource.createHalResource({ '_type': 'Schema' }, true); + return this.halResource.createHalResource({ _type: 'Schema' }, true); } } diff --git a/frontend/src/app/features/hal/resources/group-resource.ts b/frontend/src/app/features/hal/resources/group-resource.ts index 4177cfe88c..684e2de748 100644 --- a/frontend/src/app/features/hal/resources/group-resource.ts +++ b/frontend/src/app/features/hal/resources/group-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class GroupResource extends HalResource { } diff --git a/frontend/src/app/features/hal/resources/hal-resource.spec.ts b/frontend/src/app/features/hal/resources/hal-resource.spec.ts index 15d9b6aed6..d6422e8ead 100644 --- a/frontend/src/app/features/hal/resources/hal-resource.spec.ts +++ b/frontend/src/app/features/hal/resources/hal-resource.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,13 +29,13 @@ import { Injector } from '@angular/core'; import { TestBed, waitForAsync } from '@angular/core/testing'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { States } from 'core-app/core/states/states.service'; import { of } from 'rxjs'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; +import { HalLink, HalLinkInterface } from 'core-app/features/hal/hal-link/hal-link'; import Spy = jasmine.Spy; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; -import { HalLink, HalLinkInterface } from "core-app/features/hal/hal-link/hal-link"; describe('HalResource', () => { let halResourceService:HalResourceService; @@ -51,13 +51,13 @@ describe('HalResource', () => { // noinspection JSIgnoredPromiseFromCall TestBed.configureTestingModule({ imports: [ - OpenprojectHalModule + OpenprojectHalModule, ], providers: [ HalResourceService, States, I18nService, - ] + ], }) .compileComponents() .then(() => { @@ -78,17 +78,16 @@ describe('HalResource', () => { source = { _links: { self: { - href: '/api/hello' - } - } + href: '/api/hello', + }, + }, }; getStub = spyOn(halResourceService, 'request').and.callFake((verb:string, path:string) => { if (verb === 'get' && path === '/api/hello') { return of(halResourceService.createHalResource(source)) as any; - } else { - return false as any; } + return false as any; }); }); @@ -117,14 +116,14 @@ describe('HalResource', () => { _type: 'Other', _links: { someResource: { - href: 'foo' - } - } + href: 'foo', + }, + }, }; halResourceService.registerResource( 'Other', - { cls: OtherResource, attrTypes: { someResource: 'Other' } } + { cls: OtherResource, attrTypes: { someResource: 'Other' } }, ); resource = halResourceService.createHalResource(source, false); }); @@ -156,14 +155,14 @@ describe('HalResource', () => { get link() { linkFn(); return {}; - } + }, }, _embedded: { get resource() { embeddedFn(); return {}; - } - } + }, + }, }); }); @@ -195,8 +194,8 @@ describe('HalResource', () => { _embedded: {}, property: 'foo', obj: { - foo: 'bar' - } + foo: 'bar', + }, }; resource = halResourceService.createHalResource(source, true); }); @@ -240,9 +239,9 @@ describe('HalResource', () => { _links: { self: { href: '/api/hello', - title: 'some title' - } - } + title: 'some title', + }, + }, }; resource = halResourceService.createHalResource(source, false); }); @@ -275,9 +274,9 @@ describe('HalResource', () => { _links: { resource: { method: 'get', - href: 'resource/1' - } - } + href: 'resource/1', + }, + }, }; resource = halResourceService.createHalResource(source); resource.resource = null; @@ -297,9 +296,9 @@ describe('HalResource', () => { source = { _links: { property: { - href: null - } - } + href: null, + }, + }, }; resource = halResourceService.createHalResource(source); }); @@ -310,12 +309,12 @@ describe('HalResource', () => { }); describe('when using $plain', () => { - var plain:any; + let plain:any; beforeEach(() => { source = { _links: { self: { href: 'bunny' } }, - rabbit: 'fluffy' + rabbit: 'fluffy', }; plain = halResourceService.createHalResource(source).$plain(); }); @@ -348,12 +347,12 @@ describe('HalResource', () => { source = { _links: { self: { - href: 'unicorn/69' + href: 'unicorn/69', }, beaver: { - href: 'justin/420' - } - } + href: 'justin/420', + }, + }, }; resource = halResourceService.createHalResource(source); }); @@ -378,9 +377,8 @@ describe('HalResource', () => { spyOn(halResourceService, 'request').and.callFake((verb:string, path:string) => { if (verb === 'get' && path === 'unicorn/69') { return of(halResourceService.createHalResource({})) as any; - } else { - return null as any; } + return null as any; }); expect(() => resource.$links.self()).not.toThrow(Error); @@ -390,9 +388,8 @@ describe('HalResource', () => { spyOn(halResourceService, 'request').and.callFake((verb:string, path:string) => { if (verb === 'get' && path === 'justin/420') { return of(halResourceService.createHalResource({})) as any; - } else { - return null as any; } + return null as any; }); expect(() => resource.$links.beaver()).not.toThrow(Error); @@ -413,7 +410,7 @@ describe('HalResource', () => { source = { _embedded: { resource: { _links: {} }, - } + }, }; resource = halResourceService.createHalResource(source); @@ -436,12 +433,12 @@ describe('HalResource', () => { }); describe('when overriding the property with a resource', () => { - var link:HalLinkInterface; + let link:HalLinkInterface; beforeEach(() => { link = { href: 'pony', - method: 'get' + method: 'get', }; resource.resource = HalLink.fromObject(halResourceService, link); }); @@ -452,8 +449,8 @@ describe('HalResource', () => { }); describe('when the embedded resources are nested', () => { - var first:any; - var deep:any; + let first:any; + let deep:any; beforeEach(() => { source._embedded.resource._embedded = { @@ -461,11 +458,11 @@ describe('HalResource', () => { _embedded: { second: { _links: {}, - property: 'yet another value' - } + property: 'yet another value', + }, }, - property: 'another value' - } + property: 'another value', + }, }; first = resource.$embedded.resource.$embedded.first; @@ -484,7 +481,7 @@ describe('HalResource', () => { }); describe('when creating a resource from a source with a linked array property', () => { - var expectLengthsToBe = (length:any, update = 'update') => { + const expectLengthsToBe = (length:any, update = 'update') => { it(`should ${update} the values of the resource`, () => { expect(resource.values.length).toEqual(length); }); @@ -504,14 +501,14 @@ describe('HalResource', () => { values: [ { href: '/api/value/1', - title: 'val1' + title: 'val1', }, { href: '/api/value/2', - title: 'val2' - } - ] - } + title: 'val2', + }, + ], + }, }; resource = halResourceService.createHalResource(source); }); @@ -567,8 +564,8 @@ describe('HalResource', () => { beforeEach(() => { source = { _embedded: { - elements: [{ _links: {} }, { _links: {} }] - } + elements: [{ _links: {} }, { _links: {} }], + }, }; resource = halResourceService.createHalResource(source); @@ -590,36 +587,36 @@ describe('HalResource', () => { _links: { property: { href: '/api/property', - title: 'Property' + title: 'Property', }, embedded: { href: '/api/embedded', }, action: { href: '/api/action', - method: 'post' + method: 'post', }, self: { - href: '/api/self' - } + href: '/api/self', + }, }, _embedded: { embedded: { _links: { self: { - href: '/api/embedded' - } + href: '/api/embedded', + }, }, - name: 'name' + name: 'name', }, notLinked: { _links: { self: { - href: '/api/not-linked' - } - } - } - } + href: '/api/not-linked', + }, + }, + }, + }, }; resource = halResourceService.createHalResource(source); @@ -666,13 +663,13 @@ describe('HalResource', () => { }); describe('when a resource that is linked and embedded is updated', () => { - var embeddedResource; + let embeddedResource; beforeEach(() => { embeddedResource = { $link: { method: 'get', - href: 'newHref' - } + href: 'newHref', + }, }; resource.embedded = embeddedResource; @@ -720,15 +717,14 @@ describe('HalResource', () => { const result = halResourceService.createHalResource({ _links: {}, name: 'name', - foo: 'bar' + foo: 'bar', }); getStub = spyOn(halResourceService, 'request').and.callFake((verb:string, path:string) => { if (verb === 'get' && path === '/api/property') { return of(result) as any; - } else { - return false as any; } + return false as any; }); resource = resource.property; @@ -753,10 +749,10 @@ describe('HalResource', () => { it('should have properties that have a getter and setter', () => { const descriptor = Object.getOwnPropertyDescriptor(newResult, 'foo'); - expect(descriptor).toBeDefined("Descriptor should be defined"); + expect(descriptor).toBeDefined('Descriptor should be defined'); - expect(descriptor!.get).toBeDefined("Descriptor getter should be defined"); - expect(descriptor!.set).toBeDefined("Descriptor setter should be defined"); + expect(descriptor!.get).toBeDefined('Descriptor getter should be defined'); + expect(descriptor!.set).toBeDefined('Descriptor setter should be defined'); }); it('should return itself in a promise if already loaded', () => { diff --git a/frontend/src/app/features/hal/resources/hal-resource.ts b/frontend/src/app/features/hal/resources/hal-resource.ts index 866b840596..1b64744f83 100644 --- a/frontend/src/app/features/hal/resources/hal-resource.ts +++ b/frontend/src/app/features/hal/resources/hal-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { InputState } from "reactivestates"; +import { InputState } from 'reactivestates'; import { Injector } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { ICKEditorContext } from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { HalLinkInterface } from "core-app/features/hal/hal-link/hal-link"; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { ICKEditorContext } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { HalLinkInterface } from 'core-app/features/hal/hal-link/hal-link'; export interface HalResourceClass { new(injector:Injector, - source:any, - $loaded:boolean, - halInitializer:(halResource:T) => void, - $halType:string):T; + source:any, + $loaded:boolean, + halInitializer:(halResource:T) => void, + $halType:string):T; } export type HalSourceLink = { href:string|null, title?:string }; @@ -75,6 +75,7 @@ export class HalResource { public $halType:string; @InjectField() states:States; + @InjectField() I18n!:I18nService; /** @@ -91,20 +92,22 @@ export class HalResource { * */ public constructor(public injector:Injector, - public $source:any, - public $loaded:boolean, - public halInitializer:(halResource:any) => void, - $halType:string) { + public $source:any, + public $loaded:boolean, + public halInitializer:(halResource:any) => void, + $halType:string) { this.$halType = $halType; this.$initialize($source); } public static getEmptyResource(self:{ href:string|null } = { href: null }):any { - return { _links: { self: self } }; + return { _links: { self } }; } public $links:any = {}; + public $embedded:any = {}; + public $self:Promise; public _name:string; @@ -114,7 +117,7 @@ export class HalResource { } public static matchFromLink(href:string, expectedResource:string):string|null { - const match = href.match(new RegExp(`/api/v3/${expectedResource}/(\\d+)`)); + const match = new RegExp(`/api/v3/${expectedResource}/(\\d+)`).exec(href); return match && match[1]; } @@ -138,9 +141,8 @@ export class HalResource { public toString() { if (this.href) { return `[HalResource href=${this.href}]`; - } else { - return `[HalResource id=${this.id}]`; } + return `[HalResource id=${this.id}]`; } /** @@ -155,7 +157,7 @@ export class HalResource { } const id = this.idFromLink; - if (id.match(/^\d+$/)) { + if (/^\d+$/.exec(id)) { return id; } @@ -185,7 +187,6 @@ export class HalResource { this.__initialized_at = other.__initialized_at; } - /** * Create a HalResource from the copied source of the given, other HalResource. * @@ -257,7 +258,7 @@ export class HalResource { return this.$loadResource(force); } - const state = this.state; + const { state } = this; if (force) { state.clear(); diff --git a/frontend/src/app/features/hal/resources/help-text-resource.ts b/frontend/src/app/features/hal/resources/help-text-resource.ts index 396014e954..b2d2b6c5a2 100644 --- a/frontend/src/app/features/hal/resources/help-text-resource.ts +++ b/frontend/src/app/features/hal/resources/help-text-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; -import { CallableHalLink } from "core-app/features/hal/hal-link/hal-link"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; +import { CallableHalLink } from 'core-app/features/hal/hal-link/hal-link'; export class HelpTextBaseResource extends HalResource { public attribute:string; + public attributeCaption:string; + public scope:string; + public helpText:api.v3.Formattable; } diff --git a/frontend/src/app/features/hal/resources/meeting-content-resource.ts b/frontend/src/app/features/hal/resources/meeting-content-resource.ts index 1b0ee0898e..7804657b66 100644 --- a/frontend/src/app/features/hal/resources/meeting-content-resource.ts +++ b/frontend/src/app/features/hal/resources/meeting-content-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; - - +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; export interface MeetingContentResourceLinks { addAttachment(attachment:HalResource):Promise; diff --git a/frontend/src/app/features/hal/resources/membership-resource.ts b/frontend/src/app/features/hal/resources/membership-resource.ts index 4d874baecf..161d76adc1 100644 --- a/frontend/src/app/features/hal/resources/membership-resource.ts +++ b/frontend/src/app/features/hal/resources/membership-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { RoleResource } from "core-app/features/hal/resources/role-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; import Formattable = api.v3.Formattable; export interface MembershipResourceLinks { diff --git a/frontend/src/app/features/hal/resources/mixins/attachable-mixin.ts b/frontend/src/app/features/hal/resources/mixins/attachable-mixin.ts index 41751713d1..ee5f98594a 100644 --- a/frontend/src/app/features/hal/resources/mixins/attachable-mixin.ts +++ b/frontend/src/app/features/hal/resources/mixins/attachable-mixin.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { HttpErrorResponse } from "@angular/common/http"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { HttpErrorResponse } from '@angular/common/http'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { OpenProjectDirectFileUploadService } from 'core-app/core/file-upload/op-direct-file-upload.service'; -import { OpenProjectFileUploadService, UploadFile } from "core-app/core/file-upload/op-file-upload.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { AttachmentCollectionResource } from "core-app/features/hal/resources/attachment-collection-resource"; +import { OpenProjectFileUploadService, UploadFile } from 'core-app/core/file-upload/op-file-upload.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { AttachmentCollectionResource } from 'core-app/features/hal/resources/attachment-collection-resource'; type Constructor = new (...args:any[]) => T; @@ -44,11 +44,17 @@ export function Attachable>(Base:TBase) { public attachments:AttachmentCollectionResource; private NotificationsService:NotificationsService; + private halNotification:HalResourceNotificationService; + private opFileUpload:OpenProjectFileUploadService; + private opDirectFileUpload:OpenProjectDirectFileUploadService; + private pathHelper:PathHelperService; + private apiV3Service:APIV3Service; + private config:ConfigurationService; /** @@ -145,7 +151,7 @@ export function Attachable>(Base:TBase) { setTimeout(() => this.NotificationsService.remove(notification), 700); this.attachments.count += result.length; - result.forEach(r => { + result.forEach((r) => { this.attachments.elements.push(r.response); }); this.updateState(); @@ -154,11 +160,11 @@ export function Attachable>(Base:TBase) { }) .catch((error:HttpErrorResponse) => { let message:undefined|string; - console.error("Error while uploading: %O", error); + console.error('Error while uploading: %O', error); if (error.error instanceof ErrorEvent) { // A client-side or network error occurred. - message = this.I18n.t('js.error_attachment_upload', { error: error }); + message = this.I18n.t('js.error_attachment_upload', { error }); } else if (_.get(error, 'error._type') === 'Error') { message = error.error.message; } else { @@ -176,7 +182,7 @@ export function Attachable>(Base:TBase) { if (href) { return this.opDirectFileUpload.uploadAndMapResponse(href, files); - } else if (this.isNew || !this.id || !this.attachmentsBackend) { + } if (this.isNew || !this.id || !this.attachmentsBackend) { href = this.apiV3Service.attachments.path; } else { href = this.addAttachment.$link.href; @@ -192,9 +198,8 @@ export function Attachable>(Base:TBase) { if (this.isNew) { return this.config.prepareAttachmentURL; - } else { - return null; } + return null; } private updateState() { @@ -235,7 +240,7 @@ export function Attachable>(Base:TBase) { attachments, false, this.halInitializer, - 'HalResource' + 'HalResource', ); } }; diff --git a/frontend/src/app/features/hal/resources/news-resource.ts b/frontend/src/app/features/hal/resources/news-resource.ts index 040dde07f2..e1c93c0b4f 100644 --- a/frontend/src/app/features/hal/resources/news-resource.ts +++ b/frontend/src/app/features/hal/resources/news-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class NewsResource extends HalResource { } diff --git a/frontend/src/app/features/hal/resources/placeholder-user-resource.ts b/frontend/src/app/features/hal/resources/placeholder-user-resource.ts index ac5484d0f5..3c8187e211 100644 --- a/frontend/src/app/features/hal/resources/placeholder-user-resource.ts +++ b/frontend/src/app/features/hal/resources/placeholder-user-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InputState } from 'reactivestates'; export class PlaceholderUserResource extends HalResource { // Links public updateImmediately:HalResource; + public delete:HalResource; + public showUser:HalResource; public get state():InputState { diff --git a/frontend/src/app/features/hal/resources/post-resource.ts b/frontend/src/app/features/hal/resources/post-resource.ts index 316015b60e..0c302095d4 100644 --- a/frontend/src/app/features/hal/resources/post-resource.ts +++ b/frontend/src/app/features/hal/resources/post-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; export interface PostResourceLinks { addAttachment(attachment:HalResource):Promise; diff --git a/frontend/src/app/features/hal/resources/project-resource.ts b/frontend/src/app/features/hal/resources/project-resource.ts index 84028f5a78..6afb3e6403 100644 --- a/frontend/src/app/features/hal/resources/project-resource.ts +++ b/frontend/src/app/features/hal/resources/project-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ICKEditorContext } from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ICKEditorContext } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; export class ProjectResource extends HalResource { public get state() { diff --git a/frontend/src/app/features/hal/resources/query-filter-instance-resource.ts b/frontend/src/app/features/hal/resources/query-filter-instance-resource.ts index 007781bb26..d67a1446cf 100644 --- a/frontend/src/app/features/hal/resources/query-filter-instance-resource.ts +++ b/frontend/src/app/features/hal/resources/query-filter-instance-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,29 +26,32 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { QueryOperatorResource } from "core-app/features/hal/resources/query-operator-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { QueryOperatorResource } from 'core-app/features/hal/resources/query-operator-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; export class QueryFilterInstanceResource extends HalResource { public filter:QueryFilterResource; + public operator:QueryOperatorResource; + public values:HalResource[]|string[]; + private memoizedCurrentSchemas:{ [key:string]:QueryFilterInstanceSchemaResource } = {}; @InjectField(SchemaCacheService) schemaCache:SchemaCacheService; + @InjectField(PathHelperService) pathHelper:PathHelperService; public $initialize(source:any) { super.$initialize(source); - this.$links['schema'] = { - href: this.pathHelper.api.v3.apiV3Base + '/queries/filter_instance_schemas/' + this.filter.idFromLink + this.$links.schema = { + href: `${this.pathHelper.api.v3.apiV3Base}/queries/filter_instance_schemas/${this.filter.idFromLink}`, }; } @@ -77,8 +80,8 @@ export class QueryFilterInstanceResource extends HalResource { if (this.memoizedCurrentSchemas[key] === undefined) { try { this.memoizedCurrentSchemas[key] = this.schemaCache.of(this).resultingSchema(this.operator); - } catch(e) { - console.error("Failed to access filter schema" + e); + } catch (e) { + console.error(`Failed to access filter schema${e}`); } } diff --git a/frontend/src/app/features/hal/resources/query-filter-instance-schema-resource.ts b/frontend/src/app/features/hal/resources/query-filter-instance-schema-resource.ts index 9155beb11a..46ea2a5b0a 100644 --- a/frontend/src/app/features/hal/resources/query-filter-instance-schema-resource.ts +++ b/frontend/src/app/features/hal/resources/query-filter-instance-schema-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; -import { QueryOperatorResource } from "core-app/features/hal/resources/query-operator-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { SchemaAttributeObject, SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; -import { SchemaDependencyResource } from "core-app/features/hal/resources/schema-dependency-resource"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; +import { QueryOperatorResource } from 'core-app/features/hal/resources/query-operator-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { SchemaAttributeObject, SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; +import { SchemaDependencyResource } from 'core-app/features/hal/resources/schema-dependency-resource'; export interface QueryFilterInstanceSchemaResourceLinks { self:HalLink; @@ -41,13 +41,16 @@ export interface QueryFilterInstanceSchemaResourceLinks { } export class QueryFilterInstanceSchemaResource extends SchemaResource { - public $links:QueryFilterInstanceSchemaResourceLinks; public operator:SchemaAttributeObject; + public filter:SchemaAttributeObject; + public dependency:SchemaDependencyResource; + public values:SchemaAttributeObject|null; + public type = 'QueryFilterInstanceSchema'; public get availableOperators():HalResource[] | CollectionResource { @@ -78,14 +81,14 @@ export class QueryFilterInstanceSchemaResource extends SchemaResource { _links: { filter: filter.$source._links.self, schema: this.$source._links.self, - operator: operator.$source._links.self - } + operator: operator.$source._links.self, + }, }; if (this.definesAllowedValues()) { - source._links['values'] = []; + source._links.values = []; } else { - source['values'] = []; + source.values = []; } return new QueryFilterInstanceResource(this.injector, source, true, this.halInitializer, 'QueryFilterInstance'); diff --git a/frontend/src/app/features/hal/resources/query-filter-resource.ts b/frontend/src/app/features/hal/resources/query-filter-resource.ts index aeddb98f76..45f9da1f6a 100644 --- a/frontend/src/app/features/hal/resources/query-filter-resource.ts +++ b/frontend/src/app/features/hal/resources/query-filter-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - - -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; export interface QueryFilterResourceEmbedded { schema:QueryFilterInstanceSchemaResource; @@ -37,6 +35,7 @@ export interface QueryFilterResourceEmbedded { export class QueryFilterResource extends HalResource { public $embedded:QueryFilterResourceEmbedded; + public values:any[]; public get id():string { diff --git a/frontend/src/app/features/hal/resources/query-form-resource.ts b/frontend/src/app/features/hal/resources/query-form-resource.ts index abb6e83db1..885113c2d0 100644 --- a/frontend/src/app/features/hal/resources/query-form-resource.ts +++ b/frontend/src/app/features/hal/resources/query-form-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export interface QueryFormResourceEmbedded { filtersSchemas:CollectionResource; @@ -38,6 +38,7 @@ export interface QueryFormResourceEmbedded { export class QueryFormResource extends FormResource { public $embedded:QueryFormResourceEmbedded; + public schema:SchemaResource; public get filtersSchemas():QueryFilterInstanceSchemaResource[] { diff --git a/frontend/src/app/features/hal/resources/query-group-by-resource.ts b/frontend/src/app/features/hal/resources/query-group-by-resource.ts index 9cffd307a7..c616e009bd 100644 --- a/frontend/src/app/features/hal/resources/query-group-by-resource.ts +++ b/frontend/src/app/features/hal/resources/query-group-by-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class QueryGroupByResource extends HalResource { } diff --git a/frontend/src/app/features/hal/resources/query-operator-resource.ts b/frontend/src/app/features/hal/resources/query-operator-resource.ts index 81f192b4b4..32351ed0be 100644 --- a/frontend/src/app/features/hal/resources/query-operator-resource.ts +++ b/frontend/src/app/features/hal/resources/query-operator-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - - -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class QueryOperatorResource extends HalResource { public get id():string { @@ -44,9 +42,7 @@ export class QueryOperatorResource extends HalResource { return ''; } - public set id(val:string) { this.$source.id = val; } } - diff --git a/frontend/src/app/features/hal/resources/query-resource.ts b/frontend/src/app/features/hal/resources/query-resource.ts index c5088ee577..53c44ff9ea 100644 --- a/frontend/src/app/features/hal/resources/query-resource.ts +++ b/frontend/src/app/features/hal/resources/query-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,14 +27,14 @@ //++ import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { QueryOrder } from "core-app/core/apiv3/endpoints/queries/apiv3-query-order"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { QueryOrder } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-order'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; export interface QueryResourceEmbedded { results:WorkPackageCollectionResource; @@ -55,24 +55,43 @@ export interface TimelineLabels { export class QueryResource extends HalResource { public $embedded:QueryResourceEmbedded; + public results:WorkPackageCollectionResource; + public columns:QueryColumn[]; + public groupBy:QueryGroupByResource|undefined; + public sortBy:QuerySortByResource[]; + public filters:QueryFilterInstanceResource[]; + public starred:boolean; + public sums:boolean; + public hasError:boolean; + public timelineVisible:boolean; + public timelineZoomLevel:TimelineZoomLevel; + public highlightingMode:HighlightingMode; + public highlightedAttributes:HalResource[]|undefined; + public displayRepresentation:string|undefined; + public timelineLabels:TimelineLabels; + public showHierarchies:boolean; + public public:boolean; + public hidden:boolean; + public project:ProjectResource; + public ordered_work_packages:QueryOrder; public $initialize(source:any) { @@ -85,9 +104,8 @@ export class QueryResource extends HalResource { filter, true, this.halInitializer, - 'QueryFilterInstance' - ) - ); + 'QueryFilterInstance', + )); } } @@ -96,4 +114,3 @@ export interface QueryResourceLinks { } export interface QueryResource extends QueryResourceLinks {} - diff --git a/frontend/src/app/features/hal/resources/query-schema-resource.ts b/frontend/src/app/features/hal/resources/query-schema-resource.ts index 5eeaee5320..b5797e1490 100644 --- a/frontend/src/app/features/hal/resources/query-schema-resource.ts +++ b/frontend/src/app/features/hal/resources/query-schema-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,16 +27,20 @@ //++ import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; export class QuerySchemaResource extends SchemaResource { columns:{ allowedValues:QueryColumn[] }; + filtersSchemas:CollectionResource; + sortBy:{ allowedValues:QuerySortByResource[] }; + groupBy:{ allowedValues:QueryGroupByResource[] }; + displayRepresentation:{ allowedValues:'list'|'card' }; } diff --git a/frontend/src/app/features/hal/resources/query-sort-by-resource.ts b/frontend/src/app/features/hal/resources/query-sort-by-resource.ts index 17c25cead2..2dd5788f12 100644 --- a/frontend/src/app/features/hal/resources/query-sort-by-resource.ts +++ b/frontend/src/app/features/hal/resources/query-sort-by-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -export const QUERY_SORT_BY_ASC = "urn:openproject-org:api:v3:queries:directions:asc"; -export const QUERY_SORT_BY_DESC = "urn:openproject-org:api:v3:queries:directions:desc"; +export const QUERY_SORT_BY_ASC = 'urn:openproject-org:api:v3:queries:directions:asc'; +export const QUERY_SORT_BY_DESC = 'urn:openproject-org:api:v3:queries:directions:desc'; export interface QuerySortByResourceEmbedded { column:QueryColumn; @@ -39,7 +39,9 @@ export interface QuerySortByResourceEmbedded { export class QuerySortByResource extends HalResource { public $embedded:QuerySortByResourceEmbedded; + public column:QueryColumn; + public direction:QuerySortByDirection; } diff --git a/frontend/src/app/features/hal/resources/relation-resource.ts b/frontend/src/app/features/hal/resources/relation-resource.ts index f6e242ec76..8b92fb75e1 100644 --- a/frontend/src/app/features/hal/resources/relation-resource.ts +++ b/frontend/src/app/features/hal/resources/relation-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; export interface RelationResourceLinks { delete():Promise; @@ -36,7 +36,6 @@ export interface RelationResourceLinks { } export class RelationResource extends HalResource { - static RELATION_TYPES(includeParentChild = true):string[] { const types = [ 'relates', @@ -49,7 +48,7 @@ export class RelationResource extends HalResource { 'includes', 'partof', 'requires', - 'required' + 'required', ]; if (includeParentChild) { @@ -62,9 +61,7 @@ export class RelationResource extends HalResource { static LOCALIZED_RELATION_TYPES(includeParentchild = true) { const relationTypes = RelationResource.RELATION_TYPES(includeParentchild); - return relationTypes.map((key:string) => { - return { name: key, label: I18n.t('js.relation_labels.' + key) }; - }); + return relationTypes.map((key:string) => ({ name: key, label: I18n.t(`js.relation_labels.${key}`) })); } static DEFAULT() { @@ -73,12 +70,16 @@ export class RelationResource extends HalResource { // Properties public description:string|null; + public type:any; + public reverseType:string; // Links public $links:RelationResourceLinks; + public to:WorkPackageResource; + public from:WorkPackageResource; public normalizedType(workPackage:WorkPackageResource) { @@ -98,7 +99,7 @@ export class RelationResource extends HalResource { target: this[target], targetId: this[target].id!, relationType: target === 'from' ? this.reverseType : this.type, - reverseRelationType: target === 'from' ? this.type : this.reverseType + reverseRelationType: target === 'from' ? this.type : this.reverseType, }; } @@ -117,16 +118,16 @@ export class RelationResource extends HalResource { public get ids() { return { from: WorkPackageResource.idFromLink(this.from.href!), - to: WorkPackageResource.idFromLink(this.to.href!) + to: WorkPackageResource.idFromLink(this.to.href!), }; } public updateDescription(description:string) { - return this.$links.updateImmediately({ description: description }); + return this.$links.updateImmediately({ description }); } public updateType(type:any) { - return this.$links.updateImmediately({ type: type }); + return this.$links.updateImmediately({ type }); } } @@ -138,4 +139,3 @@ export interface DenormalizedRelationData { relationType:string; reverseRelationType:string; } - diff --git a/frontend/src/app/features/hal/resources/role-resource.ts b/frontend/src/app/features/hal/resources/role-resource.ts index 6e6c9d2905..15148df8d2 100644 --- a/frontend/src/app/features/hal/resources/role-resource.ts +++ b/frontend/src/app/features/hal/resources/role-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class RoleResource extends HalResource { } diff --git a/frontend/src/app/features/hal/resources/root-resource.ts b/frontend/src/app/features/hal/resources/root-resource.ts index 83eefaef9b..33915f00cd 100644 --- a/frontend/src/app/features/hal/resources/root-resource.ts +++ b/frontend/src/app/features/hal/resources/root-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; export class RootResource extends HalResource { - public user:UserResource; } - diff --git a/frontend/src/app/features/hal/resources/schema-dependency-resource.ts b/frontend/src/app/features/hal/resources/schema-dependency-resource.ts index d08736a5de..ed3a24c07a 100644 --- a/frontend/src/app/features/hal/resources/schema-dependency-resource.ts +++ b/frontend/src/app/features/hal/resources/schema-dependency-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class SchemaDependencyResource extends HalResource { - public dependencies:any; public forValue(value:string):any { diff --git a/frontend/src/app/features/hal/resources/schema-resource.ts b/frontend/src/app/features/hal/resources/schema-resource.ts index 3911f6894f..387763b660 100644 --- a/frontend/src/app/features/hal/resources/schema-resource.ts +++ b/frontend/src/app/features/hal/resources/schema-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; import { InputState } from 'reactivestates'; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; export class SchemaResource extends HalResource { - public get state():InputState { return this.states.schemas.get(this.href as string) as any; } public get availableAttributes() { - return _.keys(this.$source).filter(name => name.indexOf('_') !== 0); + return _.keys(this.$source).filter((name) => name.indexOf('_') !== 0); } // Find the attribute name with a matching (localized) name; @@ -59,9 +57,14 @@ export class SchemaResource extends HalResource { export class SchemaAttributeObject { public type:string; + public name:string; + public required:boolean; + public hasDefault:boolean; + public writable:boolean; + public allowedValues:T[] | CollectionResource; } diff --git a/frontend/src/app/features/hal/resources/status-resource.ts b/frontend/src/app/features/hal/resources/status-resource.ts index df5641b246..7a0f6bfc4b 100644 --- a/frontend/src/app/features/hal/resources/status-resource.ts +++ b/frontend/src/app/features/hal/resources/status-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InputState } from 'reactivestates'; export class StatusResource extends HalResource { - isClosed:boolean; + isDefault:boolean; public get state():InputState { return this.states.statuses.get(this.href as string) as any; } } - diff --git a/frontend/src/app/features/hal/resources/time-entry-resource.ts b/frontend/src/app/features/hal/resources/time-entry-resource.ts index c4911a4f29..abe395ae17 100644 --- a/frontend/src/app/features/hal/resources/time-entry-resource.ts +++ b/frontend/src/app/features/hal/resources/time-entry-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class TimeEntryResource extends HalResource { public get state() { diff --git a/frontend/src/app/features/hal/resources/type-resource.ts b/frontend/src/app/features/hal/resources/type-resource.ts index 03873abddc..9172633bbd 100644 --- a/frontend/src/app/features/hal/resources/type-resource.ts +++ b/frontend/src/app/features/hal/resources/type-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InputState } from 'reactivestates'; export class TypeResource extends HalResource { diff --git a/frontend/src/app/features/hal/resources/user-resource.ts b/frontend/src/app/features/hal/resources/user-resource.ts index 5cb5c6b57c..b1cdbabf22 100644 --- a/frontend/src/app/features/hal/resources/user-resource.ts +++ b/frontend/src/app/features/hal/resources/user-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,22 +27,29 @@ //++ import { InputState } from 'reactivestates'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class UserResource extends HalResource { - // Properties public login:string; + public firstName:string; + public lastName:string; + public email:string; + public avatar:string; + public status:string; // Links public lock:HalResource; + public unlock:HalResource; + public delete:HalResource; + public showUser:HalResource; public static get active_user_statuses() { diff --git a/frontend/src/app/features/hal/resources/version-resource.ts b/frontend/src/app/features/hal/resources/version-resource.ts index 2b3fa50401..8594213bb4 100644 --- a/frontend/src/app/features/hal/resources/version-resource.ts +++ b/frontend/src/app/features/hal/resources/version-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class VersionResource extends HalResource { status:string; @@ -45,4 +45,3 @@ export class VersionResource extends HalResource { return this.status === 'closed'; } } - diff --git a/frontend/src/app/features/hal/resources/wiki-page-resource.ts b/frontend/src/app/features/hal/resources/wiki-page-resource.ts index 31946fc554..b13f451327 100644 --- a/frontend/src/app/features/hal/resources/wiki-page-resource.ts +++ b/frontend/src/app/features/hal/resources/wiki-page-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; - +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; export interface WikiPageResourceLinks { addAttachment(attachment:HalResource):Promise; diff --git a/frontend/src/app/features/hal/resources/work-package-resource.spec.ts b/frontend/src/app/features/hal/resources/work-package-resource.spec.ts index 8ec59a16b5..1e12a3b0a9 100644 --- a/frontend/src/app/features/hal/resources/work-package-resource.spec.ts +++ b/frontend/src/app/features/hal/resources/work-package-resource.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,27 +27,27 @@ //++ import { TestBed, waitForAsync } from '@angular/core/testing'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { Injector } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { StateService } from "@uirouter/core"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { StateService } from '@uirouter/core'; import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { OpenProjectDirectFileUploadService } from "core-app/core/file-upload/op-direct-file-upload.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { AttachmentCollectionResource } from "core-app/features/hal/resources/attachment-collection-resource"; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { OpenProjectDirectFileUploadService } from 'core-app/core/file-upload/op-direct-file-upload.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { AttachmentCollectionResource } from 'core-app/features/hal/resources/attachment-collection-resource'; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; describe('WorkPackage', () => { let halResourceService:HalResourceService; @@ -67,7 +67,7 @@ describe('WorkPackage', () => { // noinspection JSIgnoredPromiseFromCall TestBed.configureTestingModule({ imports: [ - OpenprojectHalModule + OpenprojectHalModule, ], providers: [ HalResourceService, @@ -87,7 +87,7 @@ describe('WorkPackage', () => { { provide: WorkPackageCreateService, useValue: {} }, { provide: StateService, useValue: {} }, { provide: SchemaCacheService, useValue: {} }, - ] + ], }) .compileComponents() .then(() => { @@ -142,9 +142,9 @@ describe('WorkPackage', () => { _links: { schema: { _type: 'Schema', href: 'schema' }, attachments: { href: 'attachments' }, - activities: { href: 'activities' } + activities: { href: 'activities' }, }, - isNew: true + isNew: true, }; createWorkPackage(); }); @@ -158,7 +158,7 @@ describe('WorkPackage', () => { file = {}; attachment = { $isHal: true, - 'delete': () => undefined + delete: () => undefined, }; createWorkPackage(); diff --git a/frontend/src/app/features/hal/resources/work-package-resource.ts b/frontend/src/app/features/hal/resources/work-package-resource.ts index c2f26ca484..45824ce48e 100644 --- a/frontend/src/app/features/hal/resources/work-package-resource.ts +++ b/frontend/src/app/features/hal/resources/work-package-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - import { I18nService } from 'core-app/core/i18n/i18n.service'; import { States } from 'core-app/core/states/states.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { InputState } from "reactivestates"; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { ICKEditorContext } from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { AttachmentCollectionResource } from "core-app/features/hal/resources/attachment-collection-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { Attachable } from "core-app/features/hal/resources/mixins/attachable-mixin"; +import { InputState } from 'reactivestates'; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { ICKEditorContext } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { AttachmentCollectionResource } from 'core-app/features/hal/resources/attachment-collection-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { Attachable } from 'core-app/features/hal/resources/mixins/attachable-mixin'; export interface WorkPackageResourceEmbedded { activities:CollectionResource; @@ -113,21 +112,35 @@ export interface WorkPackageLinksObject extends WorkPackageResourceLinks { export class WorkPackageBaseResource extends HalResource { public $embedded:WorkPackageResourceEmbedded; + public $links:WorkPackageLinksObject; + public subject:string; + public updatedAt:Date; + public lockVersion:number; + public description:any; + public activities:CollectionResource; + public attachments:AttachmentCollectionResource; @InjectField() I18n!:I18nService; + @InjectField() states:States; + @InjectField() wpActivity:WorkPackagesActivityService; + @InjectField() apiV3Service:APIV3Service; + @InjectField() NotificationsService:NotificationsService; + @InjectField() workPackageNotificationService:WorkPackageNotificationService; + @InjectField() pathHelper:PathHelperService; + @InjectField() opFileUpload:OpenProjectFileUploadService; readonly attachmentsBackend = true; @@ -136,7 +149,7 @@ export class WorkPackageBaseResource extends HalResource { * Return the ids of all its ancestors, if any */ public get ancestorIds():string[] { - const ancestors = (this as any).ancestors; + const { ancestors } = this as any; return ancestors.map((el:WorkPackageResource) => el.id!); } @@ -161,16 +174,15 @@ export class WorkPackageBaseResource extends HalResource { } public get isLeaf():boolean { - const children = this.$links.children; + const { children } = this.$links; return !(children && children.length > 0); } public previewPath() { if (!this.isNew) { return this.apiV3Service.work_packages.id(this.id!).path; - } else { - return super.previewPath(); } + return super.previewPath(); } public getEditorContext(fieldName:string):ICKEditorContext { @@ -191,7 +203,7 @@ export class WorkPackageBaseResource extends HalResource { public updateLinkedResources(...resourceNames:string[]):Promise { const resources:{ [id:string]:Promise } = {}; - resourceNames.forEach(name => { + resourceNames.forEach((name) => { const linked = this[name]; resources[name] = linked ? linked.$update() : Promise.reject(undefined); }); @@ -214,7 +226,7 @@ export class WorkPackageBaseResource extends HalResource { _.get(attachments, '$source', attachments), false, this.halInitializer, - 'HalResource' + 'HalResource', ); } @@ -236,7 +248,7 @@ export class WorkPackageBaseResource extends HalResource { * Update the state */ public push(newValue:this):Promise { - this.wpActivity.clear(newValue.id!); + this.wpActivity.clear(newValue.id); // If there is a parent, its view has to be updated as well if (newValue.parent) { diff --git a/frontend/src/app/features/hal/resources/wp-collection-resource.ts b/frontend/src/app/features/hal/resources/wp-collection-resource.ts index 652f72c345..7ae60ab12b 100644 --- a/frontend/src/app/features/hal/resources/wp-collection-resource.ts +++ b/frontend/src/app/features/hal/resources/wp-collection-resource.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export interface WorkPackageCollectionResourceEmbedded { elements:WorkPackageResource[]; @@ -38,11 +38,17 @@ export interface WorkPackageCollectionResourceEmbedded { export class WorkPackageCollectionResource extends CollectionResource { public schemas:CollectionResource; + public createWorkPackage:any; + public elements:WorkPackageResource[]; + public groups:GroupObject[]; - public totalSums?:{[key:string]:number}; + + public totalSums?:{ [key:string]:number }; + public sumsSchema?:SchemaResource; + public representations:Array; } @@ -58,7 +64,7 @@ export interface GroupObject { collapsed?:boolean; index:number; identifier:string; - sums:{[attribute:string]:number|null}; + sums:{ [attribute:string]:number|null }; href:{ href:string }[]; _links:{ valueLink:{ href:string }[]; diff --git a/frontend/src/app/features/hal/schemas/hal-payload.helper.ts b/frontend/src/app/features/hal/schemas/hal-payload.helper.ts index b57ba25820..0245d55cf3 100644 --- a/frontend/src/app/features/hal/schemas/hal-payload.helper.ts +++ b/frontend/src/app/features/hal/schemas/hal-payload.helper.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export class HalPayloadHelper { - /** * Extract payload from the given request with schema. * This will ensure we will only write writable attributes and so on. @@ -41,11 +40,10 @@ export class HalPayloadHelper { static extractPayload(resource:T|Object|null, schema:SchemaResource|null = null):Object { if (resource instanceof HalResource && schema) { return this.extractPayloadFromSchema(resource, schema); - } else if (resource && !(resource instanceof HalResource)) { + } if (resource && !(resource instanceof HalResource)) { return resource; - } else { - return {}; } + return {}; } /** @@ -59,7 +57,7 @@ export class HalPayloadHelper { */ static extractPayloadFromSchema(resource:T, schema:SchemaResource) { const payload:any = { - '_links': {} + _links: {}, }; const nonLinkProperties = []; @@ -68,12 +66,10 @@ export class HalPayloadHelper { if (schema.hasOwnProperty(key) && schema[key] && schema[key].writable) { if (resource.$links[key]) { if (Array.isArray(resource[key])) { - payload['_links'][key] = _.map(resource[key], element => { - return { href: (element as HalResource).href }; - }); + payload._links[key] = _.map(resource[key], (element) => ({ href: (element as HalResource).href })); } else { - payload['_links'][key] = { - href: (resource[key] && resource[key].href) + payload._links[key] = { + href: (resource[key] && resource[key].href), }; } } else { @@ -82,15 +78,14 @@ export class HalPayloadHelper { } } - _.each(nonLinkProperties, property => { + _.each(nonLinkProperties, (property) => { if (resource.hasOwnProperty(property) || resource[property]) { if (Array.isArray(resource[property])) { payload[property] = _.map(resource[property], (element:any) => { if (element instanceof HalResource) { return this.extractPayloadFromSchema(element, element.currentSchema || element.schema); - } else { - return element; } + return element; }); } else { payload[property] = resource[property]; diff --git a/frontend/src/app/features/hal/schemas/schema-proxy.ts b/frontend/src/app/features/hal/schemas/schema-proxy.ts index 28546b80c0..5c69e2c02b 100644 --- a/frontend/src/app/features/hal/schemas/schema-proxy.ts +++ b/frontend/src/app/features/hal/schemas/schema-proxy.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; export interface ISchemaProxy extends SchemaResource { ofProperty(property:string):IFieldSchema; @@ -38,33 +38,33 @@ export interface ISchemaProxy extends SchemaResource { export class SchemaProxy implements ProxyHandler { constructor(protected schema:SchemaResource, - protected resource:HalResource) { + protected resource:HalResource) { } static create(schema:SchemaResource, resource:HalResource) { return new Proxy( schema, - new this(schema, resource) + new this(schema, resource), ) as ISchemaProxy; } get(schema:SchemaResource, property:PropertyKey, receiver:any):any { switch (property) { - case 'ofProperty': { - return this.proxyMethod(this.ofProperty); - } - case 'isAttributeEditable': { - return this.proxyMethod(this.isAttributeEditable); - } - case 'mappedName': { - return this.proxyMethod(this.mappedName); - } - case 'isEditable': { - return this.isEditable; - } - default: { - return Reflect.get(schema, property, receiver); - } + case 'ofProperty': { + return this.proxyMethod(this.ofProperty); + } + case 'isAttributeEditable': { + return this.proxyMethod(this.isAttributeEditable); + } + case 'mappedName': { + return this.proxyMethod(this.mappedName); + } + case 'isEditable': { + return this.isEditable; + } + default: { + return Reflect.get(schema, property, receiver); + } } } @@ -82,10 +82,9 @@ export class SchemaProxy implements ProxyHandler { const propertySchema = this.schema[this.mappedName(property)]; if (propertySchema) { - return Object.assign({}, propertySchema, { writable: this.isEditable && propertySchema && propertySchema.writable }); - } else { - return null; + return { ...propertySchema, writable: this.isEditable && propertySchema && propertySchema.writable }; } + return null; } /** @@ -122,9 +121,9 @@ export class SchemaProxy implements ProxyHandler { // Returning a Proxy here so that the call is bound // to the SchemaProxy instance. return new Proxy(method, { - apply: function (_, __, argumentsList) { + apply(_, __, argumentsList) { return method.apply(self, [argumentsList[0]]); - } + }, }); } } diff --git a/frontend/src/app/features/hal/schemas/work-package-schema-proxy.ts b/frontend/src/app/features/hal/schemas/work-package-schema-proxy.ts index 083f43a1a3..9630ea0d0d 100644 --- a/frontend/src/app/features/hal/schemas/work-package-schema-proxy.ts +++ b/frontend/src/app/features/hal/schemas/work-package-schema-proxy.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { SchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { SchemaProxy } from 'core-app/features/hal/schemas/schema-proxy'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; export class WorkPackageSchemaProxy extends SchemaProxy { get(schema:SchemaResource, property:PropertyKey, receiver:any):any { switch (property) { - case 'isMilestone': { - return this.isMilestone; - } - case 'isReadonly': { - return this.isReadonly; - } - default: { - return super.get(schema, property, receiver); - } + case 'isMilestone': { + return this.isMilestone; + } + case 'isReadonly': { + return this.isReadonly; + } + default: { + return super.get(schema, property, receiver); + } } } @@ -62,14 +62,13 @@ export class WorkPackageSchemaProxy extends SchemaProxy { return null; } - propertySchema.writable = propertySchema.writable || - this.isAttributeEditable('dueDate') || - this.isAttributeEditable('scheduleManually'); + propertySchema.writable = propertySchema.writable + || this.isAttributeEditable('dueDate') + || this.isAttributeEditable('scheduleManually'); return propertySchema; - } else { - return super.ofProperty(property); } + return super.ofProperty(property); } public get isReadonly():boolean { @@ -85,13 +84,12 @@ export class WorkPackageSchemaProxy extends SchemaProxy { public isAttributeEditable(property:string):boolean { if (this.isReadonly && property !== 'status') { return false; - } else if (['startDate', 'dueDate', 'date'].includes(property) && - this.resource.scheduleManually) { + } if (['startDate', 'dueDate', 'date'].includes(property) + && this.resource.scheduleManually) { // This is a blatant shortcut but should be adequate. return super.isAttributeEditable('scheduleManually'); - } else { - return super.isAttributeEditable(property); } + return super.isAttributeEditable(property); } public get isMilestone():boolean { @@ -101,8 +99,7 @@ export class WorkPackageSchemaProxy extends SchemaProxy { public mappedName(property:string):string { if (this.isMilestone && (property === 'startDate' || property === 'dueDate')) { return 'date'; - } else { - return property; } + return property; } } diff --git a/frontend/src/app/features/hal/services/hal-aware-error-handler.ts b/frontend/src/app/features/hal/services/hal-aware-error-handler.ts index 76440eb3d7..a9be076628 100644 --- a/frontend/src/app/features/hal/services/hal-aware-error-handler.ts +++ b/frontend/src/app/features/hal/services/hal-aware-error-handler.ts @@ -1,12 +1,12 @@ -import { ErrorHandler, Injectable } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; +import { ErrorHandler, Injectable } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; @Injectable() export class HalAwareErrorHandler extends ErrorHandler { private text = { - internal_error: this.I18n.t('js.error.internal') + internal_error: this.I18n.t('js.error.internal'), }; constructor(private readonly I18n:I18nService) { @@ -17,10 +17,10 @@ export class HalAwareErrorHandler extends ErrorHandler { let message:string = this.text.internal_error; if (error instanceof ErrorResource) { - console.error("Returned error resource %O", error); - message += ` ${error.errorMessages.join("\n")}`; + console.error('Returned error resource %O', error); + message += ` ${error.errorMessages.join('\n')}`; } else if (error instanceof HalResource) { - console.error("Returned hal resource %O", error); + console.error('Returned hal resource %O', error); message += `Resource returned ${error.name}`; } else if (error instanceof Error) { window.ErrorReporter.captureException(error); diff --git a/frontend/src/app/features/hal/services/hal-events.service.ts b/frontend/src/app/features/hal/services/hal-events.service.ts index 8a3a4f84d9..3d80499436 100644 --- a/frontend/src/app/features/hal/services/hal-events.service.ts +++ b/frontend/src/app/features/hal/services/hal-events.service.ts @@ -1,8 +1,8 @@ -import { Injectable } from "@angular/core"; -import { Observable, Subject } from "rxjs"; -import { buffer, debounceTime, filter } from "rxjs/operators"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ResourceChangesetCommit } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; +import { Injectable } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { buffer, debounceTime, filter } from 'rxjs/operators'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ResourceChangesetCommit } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; export interface HalEvent { id:string; @@ -45,7 +45,7 @@ export class HalEventsService { .events$ .pipe( filter((event:HalEvent) => event.resourceType === resourceType), - buffer(this.events$.pipe(debounceTime(debounceTimeInMs))) + buffer(this.events$.pipe(debounceTime(debounceTimeInMs))), ); } diff --git a/frontend/src/app/features/hal/services/hal-resource-notification.service.ts b/frontend/src/app/features/hal/services/hal-resource-notification.service.ts index 0c20d5c100..e98b0802be 100644 --- a/frontend/src/app/features/hal/services/hal-resource-notification.service.ts +++ b/frontend/src/app/features/hal/services/hal-resource-notification.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,25 +27,29 @@ //++ import { StateService } from '@uirouter/core'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { Injectable, Injector } from '@angular/core'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HttpErrorResponse } from "@angular/common/http"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; @Injectable() export class HalResourceNotificationService { - @InjectField() protected I18n:I18nService; + @InjectField() protected $state:StateService; + @InjectField() protected halResourceService:HalResourceService; + @InjectField() protected NotificationsService:NotificationsService; + @InjectField() protected loadingIndicator:LoadingIndicatorService; + @InjectField() protected schemaCache:SchemaCacheService; constructor(public injector:Injector) { @@ -53,7 +57,7 @@ export class HalResourceNotificationService { public showSave(resource:HalResource, isCreate = false) { const message:any = { - message: this.I18n.t('js.notice_successful_' + (isCreate ? 'create' : 'update')), + message: this.I18n.t(`js.notice_successful_${isCreate ? 'create' : 'update'}`), }; this.NotificationsService.addSuccess(message); @@ -70,7 +74,7 @@ export class HalResourceNotificationService { * @param resource */ public handleRawError(response:unknown, resource?:HalResource) { - console.error("Handling error message %O for work package %O", response, resource); + console.error('Handling error message %O for work package %O', response, resource); // Some transformation may already have returned the error as a HAL resource, // which we will forward to handleErrorResponse @@ -157,7 +161,7 @@ export class HalResourceNotificationService { let error = this.I18n.t('js.error.internal'); if (typeof (message) === 'string' || _.has(message, 'toString')) { - error += ' ' + (message as any).toString(); + error += ` ${(message as any).toString()}`; } this.NotificationsService.addError(error); @@ -166,18 +170,16 @@ export class HalResourceNotificationService { public showEditingBlockedError(attribute:string) { this.NotificationsService.addError(this.I18n.t( 'js.hal.error.edit_prohibited', - { attribute: attribute } + { attribute }, )); } protected showCustomError(errorResource:any, resource:HalResource) { - if (errorResource.errorIdentifier === 'urn:openproject-org:api:v3:errors:PropertyFormatError') { - const schema = this.schemaCache.of(resource).ofProperty(errorResource.details.attribute); const attributeName = schema.name; const attributeType = schema.type.toLowerCase(); - const i18nString = 'js.hal.error.format.' + attributeType; + const i18nString = `js.hal.error.format.${attributeType}`; if (this.I18n.lookup(i18nString) === undefined) { return false; diff --git a/frontend/src/app/features/hal/services/hal-resource-sorting.service.ts b/frontend/src/app/features/hal/services/hal-resource-sorting.service.ts index 0400f809df..ee1cf62273 100644 --- a/frontend/src/app/features/hal/services/hal-resource-sorting.service.ts +++ b/frontend/src/app/features/hal/services/hal-resource-sorting.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,17 +27,16 @@ //++ import { Injectable } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable({ providedIn: 'root' }) export class HalResourceSortingService { - /** * List of sortable properties by HAL type */ private config:{ [typeName:string]:string } = { - 'user': 'name', - 'project': 'name' + user: 'name', + project: 'name', }; constructor() { @@ -63,10 +62,9 @@ export class HalResourceSortingService { const property = this.sortingProperty(halType); if (property) { - return _.sortBy(elements, v => v[property].toLowerCase()); - } else { - return elements; + return _.sortBy(elements, (v) => v[property].toLowerCase()); } + return elements; } /** @@ -90,5 +88,4 @@ export class HalResourceSortingService { public hasSortingProperty(type:string) { return this.sortingProperty(type) !== undefined; } - } diff --git a/frontend/src/app/features/hal/services/hal-resource.config.ts b/frontend/src/app/features/hal/services/hal-resource.config.ts index e49bbdc888..16dc2a265b 100644 --- a/frontend/src/app/features/hal/services/hal-resource.config.ts +++ b/frontend/src/app/features/hal/services/hal-resource.config.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,43 +26,43 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { CustomActionResource } from "core-app/features/hal/resources/custom-action-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { WikiPageResource } from "core-app/features/hal/resources/wiki-page-resource"; -import { MeetingContentResource } from "core-app/features/hal/resources/meeting-content-resource"; -import { PostResource } from "core-app/features/hal/resources/post-resource"; -import { StatusResource } from "core-app/features/hal/resources/status-resource"; -import { AttachmentCollectionResource } from "core-app/features/hal/resources/attachment-collection-resource"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { NewsResource } from "core-app/features/hal/resources/news-resource"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { MembershipResource } from "core-app/features/hal/resources/membership-resource"; -import { RoleResource } from "core-app/features/hal/resources/role-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { GroupResource } from "core-app/features/hal/resources/group-resource"; -import { RootResource } from "core-app/features/hal/resources/root-resource"; -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { QueryOperatorResource } from "core-app/features/hal/resources/query-operator-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { HelpTextResource } from "core-app/features/hal/resources/help-text-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { CustomActionResource } from 'core-app/features/hal/resources/custom-action-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { WikiPageResource } from 'core-app/features/hal/resources/wiki-page-resource'; +import { MeetingContentResource } from 'core-app/features/hal/resources/meeting-content-resource'; +import { PostResource } from 'core-app/features/hal/resources/post-resource'; +import { StatusResource } from 'core-app/features/hal/resources/status-resource'; +import { AttachmentCollectionResource } from 'core-app/features/hal/resources/attachment-collection-resource'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { NewsResource } from 'core-app/features/hal/resources/news-resource'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { MembershipResource } from 'core-app/features/hal/resources/membership-resource'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { GroupResource } from 'core-app/features/hal/resources/group-resource'; +import { RootResource } from 'core-app/features/hal/resources/root-resource'; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { QueryOperatorResource } from 'core-app/features/hal/resources/query-operator-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { HelpTextResource } from 'core-app/features/hal/resources/help-text-resource'; import { HalResourceFactoryConfigInterface, HalResourceService, -} from "core-app/features/hal/services/hal-resource.service"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; -import { SchemaDependencyResource } from "core-app/features/hal/resources/schema-dependency-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +} from 'core-app/features/hal/services/hal-resource.service'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; +import { SchemaDependencyResource } from 'core-app/features/hal/resources/schema-dependency-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; const halResourceDefaultConfig:{ [typeName:string]:HalResourceFactoryConfigInterface } = { WorkPackage: { @@ -74,89 +74,89 @@ const halResourceDefaultConfig:{ [typeName:string]:HalResourceFactoryConfigInter relations: 'Relation', schema: 'Schema', status: 'Status', - type: 'Type' - } + type: 'Type', + }, }, Activity: { cls: HalResource, attrTypes: { - user: 'User' - } + user: 'User', + }, }, 'Activity::Comment': { cls: HalResource, attrTypes: { - user: 'User' - } + user: 'User', + }, }, 'Activity::Revision': { cls: HalResource, attrTypes: { - user: 'User' - } + user: 'User', + }, }, Relation: { cls: RelationResource, attrTypes: { from: 'WorkPackage', - to: 'WorkPackage' - } + to: 'WorkPackage', + }, }, Schema: { - cls: SchemaResource + cls: SchemaResource, }, Type: { - cls: TypeResource + cls: TypeResource, }, Status: { - cls: StatusResource + cls: StatusResource, }, SchemaDependency: { - cls: SchemaDependencyResource + cls: SchemaDependencyResource, }, Error: { - cls: ErrorResource + cls: ErrorResource, }, User: { - cls: UserResource + cls: UserResource, }, Group: { - cls: GroupResource + cls: GroupResource, }, Collection: { - cls: CollectionResource + cls: CollectionResource, }, WorkPackageCollection: { - cls: WorkPackageCollectionResource + cls: WorkPackageCollectionResource, }, AttachmentCollection: { - cls: AttachmentCollectionResource + cls: AttachmentCollectionResource, }, Query: { cls: QueryResource, attrTypes: { - filters: 'QueryFilterInstance' - } + filters: 'QueryFilterInstance', + }, }, Form: { cls: FormResource, attrTypes: { - payload: 'FormPayload' - } + payload: 'FormPayload', + }, }, FormPayload: { cls: HalResource, attrTypes: { - attachments: 'AttachmentsCollection' - } + attachments: 'AttachmentsCollection', + }, }, QueryFilterInstance: { cls: QueryFilterInstanceResource, attrTypes: { schema: 'QueryFilterInstanceSchema', filter: 'QueryFilter', - operator: 'QueryOperator' - } + operator: 'QueryOperator', + }, }, QueryFilterInstanceSchema: { cls: QueryFilterInstanceSchemaResource, @@ -174,41 +174,41 @@ const halResourceDefaultConfig:{ [typeName:string]:HalResourceFactoryConfigInter cls: HelpTextResource, }, CustomAction: { - cls: CustomActionResource + cls: CustomActionResource, }, WikiPage: { - cls: WikiPageResource + cls: WikiPageResource, }, MeetingContent: { - cls: MeetingContentResource + cls: MeetingContentResource, }, Post: { - cls: PostResource + cls: PostResource, }, Project: { - cls: ProjectResource + cls: ProjectResource, }, Role: { - cls: RoleResource + cls: RoleResource, }, Grid: { cls: GridResource, }, GridWidget: { - cls: GridWidgetResource + cls: GridWidgetResource, }, TimeEntry: { - cls: TimeEntryResource + cls: TimeEntryResource, }, Membership: { - cls: MembershipResource + cls: MembershipResource, }, News: { - cls: NewsResource + cls: NewsResource, }, Version: { - cls: VersionResource - } + cls: VersionResource, + }, }; export function initializeHalResourceConfig(halResourceService:HalResourceService) { @@ -216,4 +216,3 @@ export function initializeHalResourceConfig(halResourceService:HalResourceServic _.each(halResourceDefaultConfig, (value, key) => halResourceService.registerResource(key, value)); }; } - diff --git a/frontend/src/app/features/hal/services/hal-resource.service.ts b/frontend/src/app/features/hal/services/hal-resource.service.ts index ad634828fd..cbcf49d360 100644 --- a/frontend/src/app/features/hal/services/hal-resource.service.ts +++ b/frontend/src/app/features/hal/services/hal-resource.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,45 +30,42 @@ import { Injectable, Injector } from '@angular/core'; import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; import { catchError, map } from 'rxjs/operators'; import { Observable, throwError } from 'rxjs'; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; -import * as Pako from 'pako'; -import * as base64 from "byte-base64"; -import { initializeHalProperties } from "../helpers/hal-resource-builder"; -import { whenDebugging } from "core-app/shared/helpers/debug_output"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; +import * as Pako from 'pako'; +import * as base64 from 'byte-base64'; +import { whenDebugging } from 'core-app/shared/helpers/debug_output'; import { HTTPClientHeaders, HTTPClientOptions, HTTPClientParamMap, HTTPSupportedMethods, -} from "core-app/features/hal/http/http.interfaces"; -import { HalLink, HalLinkInterface } from "core-app/features/hal/hal-link/hal-link"; -import { URLParamsEncoder } from "core-app/features/hal/services/url-params-encoder"; -import { HalResource, HalResourceClass } from "core-app/features/hal/resources/hal-resource"; +} from 'core-app/features/hal/http/http.interfaces'; +import { HalLink, HalLinkInterface } from 'core-app/features/hal/hal-link/hal-link'; +import { URLParamsEncoder } from 'core-app/features/hal/services/url-params-encoder'; +import { HalResource, HalResourceClass } from 'core-app/features/hal/resources/hal-resource'; +import { initializeHalProperties } from '../helpers/hal-resource-builder'; export interface HalResourceFactoryConfigInterface { cls?:any; attrTypes?:{ [attrName:string]:string }; } - @Injectable({ providedIn: 'root' }) export class HalResourceService { - /** * List of all known hal resources, extendable. */ private config:{ [typeName:string]:HalResourceFactoryConfigInterface } = {}; constructor(readonly injector:Injector, - readonly http:HttpClient) { + readonly http:HttpClient) { } /** * Perform a HTTP request and return a HalResource promise. */ public request(method:HTTPSupportedMethods, href:string, data?:any, headers:HTTPClientHeaders = {}):Observable { - // HttpClient requires us to create HttpParams instead of passing data for get // so forward to that method instead. if (method === 'get') { @@ -77,7 +74,7 @@ export class HalResourceService { const config:HTTPClientOptions = { body: data || {}, - headers: headers, + headers, withCredentials: true, responseType: 'json', }; @@ -108,7 +105,7 @@ export class HalResourceService { */ public get(href:string, params?:HTTPClientParamMap, headers?:HTTPClientHeaders):Observable { const config:HTTPClientOptions = { - headers: headers, + headers, params: new HttpParams({ encoder: new URLParamsEncoder(), fromObject: params }), withCredentials: true, responseType: 'json', @@ -144,7 +141,7 @@ export class HalResourceService { const results = await promise; if (results.count === 0) { - throw 'No more results for this query, but expected more.'; + throw new Error('No more results for this query, but expected more.'); } allResults.push(results); @@ -278,7 +275,7 @@ export class HalResourceService { * @param href Self link of the HAL resource */ public fromSelfLink(href:string|null) { - const source = { _links: { self: { href: href } } }; + const source = { _links: { self: { href } } }; return this.createHalResource(source); } @@ -321,7 +318,7 @@ export class HalResourceService { protected toEprops(params:unknown):{ eprops:string } { const deflatedArray = Pako.deflate(JSON.stringify(params)); - const compressed = base64.bytesToBase64(deflatedArray) + const compressed = base64.bytesToBase64(deflatedArray); return { eprops: compressed }; } diff --git a/frontend/src/app/features/hal/services/url-params-encoder.ts b/frontend/src/app/features/hal/services/url-params-encoder.ts index 0d12bef6b7..e93f87444f 100644 --- a/frontend/src/app/features/hal/services/url-params-encoder.ts +++ b/frontend/src/app/features/hal/services/url-params-encoder.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -45,6 +45,3 @@ export class URLParamsEncoder implements HttpParameterCodec { return decodeURIComponent(value); } } - - - diff --git a/frontend/src/app/features/homescreen/blocks/new-features.component.spec.ts b/frontend/src/app/features/homescreen/blocks/new-features.component.spec.ts index 642044eb97..5d3f497c05 100644 --- a/frontend/src/app/features/homescreen/blocks/new-features.component.spec.ts +++ b/frontend/src/app/features/homescreen/blocks/new-features.component.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DebugElement } from "@angular/core"; +import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; -import { By } from "@angular/platform-browser"; +import { By } from '@angular/platform-browser'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HomescreenNewFeaturesBlockComponent } from './new-features.component'; @@ -42,12 +42,11 @@ describe('shows edition-specific content', () => { // noinspection JSIgnoredPromiseFromCall TestBed.configureTestingModule({ declarations: [ - HomescreenNewFeaturesBlockComponent + HomescreenNewFeaturesBlockComponent, ], - providers: [I18nService] + providers: [I18nService], }).compileComponents(); - fixture = TestBed.createComponent(HomescreenNewFeaturesBlockComponent); app = fixture.debugElement.componentInstance; element = fixture.debugElement.query(By.css('div.widget-box--description p')); @@ -59,7 +58,7 @@ describe('shows edition-specific content', () => { fixture.detectChanges(); // checking for missing translation key as translations are not loaded in specs - expect(element.nativeElement.textContent).toContain(".bim.current_new_feature_html"); + expect(element.nativeElement.textContent).toContain('.bim.current_new_feature_html'); })); it('should render standard text for standard edition', fakeAsync(() => { @@ -68,6 +67,6 @@ describe('shows edition-specific content', () => { fixture.detectChanges(); // checking for missing translation key as translations are not loaded in specs - expect(element.nativeElement.textContent).toContain(".standard.current_new_feature_html"); + expect(element.nativeElement.textContent).toContain('.standard.current_new_feature_html'); })); }); diff --git a/frontend/src/app/features/homescreen/blocks/new-features.component.ts b/frontend/src/app/features/homescreen/blocks/new-features.component.ts index 877d82fff0..284d1e634b 100644 --- a/frontend/src/app/features/homescreen/blocks/new-features.component.ts +++ b/frontend/src/app/features/homescreen/blocks/new-features.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,11 +27,10 @@ //++ import { Component } from '@angular/core'; -import { DomSanitizer } from "@angular/platform-browser"; -import imagePath = ImageHelpers.imagePath; -import { BcfRestApi } from "core-app/features/bim/bcf/bcf-constants.const"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { ImageHelpers } from "core-app/shared/helpers/images/path-helper"; +import { DomSanitizer } from '@angular/platform-browser'; +import { BcfRestApi } from 'core-app/features/bim/bcf/bcf-constants.const'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block'; @@ -52,7 +51,6 @@ export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block styleUrls: ['./new-features.component.sass'], }) - /** * Component for the homescreen block to promote new features. * When updating this for the next release, be sure to cleanup stuff is not needed any more: @@ -60,7 +58,9 @@ export const homescreenNewFeaturesBlockSelector = 'homescreen-new-features-block */ export class HomescreenNewFeaturesBlockComponent { public isStandardEdition:boolean; - new_features_image = ImageHelpers.imagePath('11_3_features.png'); + + new_features_image = imagePath('11_3_features.png'); + public text = { newFeatures: this.i18n.t('js.label_new_features'), descriptionNewFeatures: this.i18n.t('js.homescreen.blocks.new_features.text_new_features'), @@ -69,7 +69,7 @@ export class HomescreenNewFeaturesBlockComponent { constructor( readonly i18n:I18nService, - readonly domSanitizer:DomSanitizer + readonly domSanitizer:DomSanitizer, ) { this.isStandardEdition = window.OpenProject.isStandardEdition; } @@ -84,12 +84,12 @@ export class HomescreenNewFeaturesBlockComponent { } private translated(key:string):string { - return this.i18n.t(this.i18nBase + this.i18nPrefix + '.' + key, { list_styling_class: 'widget-box--arrow-links', bcf_api_link: BcfRestApi }); + return this.i18n.t(`${this.i18nBase + this.i18nPrefix}.${key}`, { list_styling_class: 'widget-box--arrow-links', bcf_api_link: BcfRestApi }); } private i18nBase = 'js.homescreen.blocks.new_features.'; private get i18nPrefix():string { - return this.isStandardEdition ? "standard" : "bim"; + return this.isStandardEdition ? 'standard' : 'bim'; } } diff --git a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts index 1c2afa4062..fd5789d2f0 100644 --- a/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts +++ b/frontend/src/app/features/in-app-notifications/bell/in-app-notification-bell.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { InAppNotificationsQuery } from "core-app/features/in-app-notifications/store/in-app-notifications.query"; -import { InAppNotificationsService } from "core-app/features/in-app-notifications/store/in-app-notifications.service"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { InAppNotificationCenterComponent } from "core-app/features/in-app-notifications/center/in-app-notification-center.component"; -import { merge, timer } from "rxjs"; -import { filter, switchMap } from "rxjs/operators"; -import { ActiveWindowService } from "core-app/core/active-window/active-window.service"; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { InAppNotificationCenterComponent } from 'core-app/features/in-app-notifications/center/in-app-notification-center.component'; +import { merge, timer } from 'rxjs'; +import { filter, switchMap } from 'rxjs/operators'; +import { ActiveWindowService } from 'core-app/core/active-window/active-window.service'; export const opInAppNotificationBellSelector = 'op-in-app-notification-bell'; const POLLING_INTERVAL = 10000; @@ -25,13 +25,13 @@ export class InAppNotificationBellComponent { unreadCount$ = merge( this.polling$, - this.inAppQuery.unreadCount$ + this.inAppQuery.unreadCount$, ); constructor(readonly inAppQuery:InAppNotificationsQuery, - readonly inAppService:InAppNotificationsService, - readonly activeWindow:ActiveWindowService, - readonly modalService:OpModalService) { + readonly inAppService:InAppNotificationsService, + readonly activeWindow:ActiveWindowService, + readonly modalService:OpModalService) { } openCenter(event:MouseEvent) { diff --git a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts index fe255702d4..c897fc3fa1 100644 --- a/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts +++ b/frontend/src/app/features/in-app-notifications/center/in-app-notification-center.component.ts @@ -1,24 +1,31 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit } from '@angular/core'; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { InAppNotificationsQuery } from "core-app/features/in-app-notifications/store/in-app-notifications.query"; -import { InAppNotificationsService } from "core-app/features/in-app-notifications/store/in-app-notifications.service"; -import { NOTIFICATIONS_MAX_SIZE } from "core-app/features/in-app-notifications/store/in-app-notification.model"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, +} from '@angular/core'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; +import { NOTIFICATIONS_MAX_SIZE } from 'core-app/features/in-app-notifications/store/in-app-notification.model'; @Component({ selector: 'op-in-app-notification-center', templateUrl: './in-app-notification-center.component.html', styleUrls: ['./in-app-notification-center.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class InAppNotificationCenterComponent extends OpModalComponent implements OnInit { activeFacet$ = this.ianQuery.activeFacet$; + notifications$ = this.ianQuery.selectAll(); + notificationsCount$ = this.ianQuery.selectCount(); + hasNotifications$ = this.ianQuery.hasNotifications$; + hasMoreThanPageSize$ = this.ianQuery.hasMoreThanPageSize$; + maxSize = NOTIFICATIONS_MAX_SIZE; facets:string[] = ['unread', 'all']; @@ -67,5 +74,4 @@ export class InAppNotificationCenterComponent extends OpModalComponent implement { newest_count: NOTIFICATIONS_MAX_SIZE, more_count: state.notShowing }, ); } - } diff --git a/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts b/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts index 47813566a4..ae240002a0 100644 --- a/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts +++ b/frontend/src/app/features/in-app-notifications/entry/in-app-notification-entry.component.ts @@ -1,27 +1,30 @@ -import { EventEmitter, ChangeDetectionStrategy, Component, Input, OnInit, Output } from '@angular/core'; +import { + EventEmitter, ChangeDetectionStrategy, Component, Input, OnInit, Output, +} from '@angular/core'; import { InAppNotification, - InAppNotificationDetail -} from "core-app/features/in-app-notifications/store/in-app-notification.model"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { NEVER, Observable, timer } from "rxjs"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { HalResource, HalSourceLink } from "core-app/features/hal/resources/hal-resource"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { InAppNotificationsService } from "core-app/features/in-app-notifications/store/in-app-notifications.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { distinctUntilChanged, map, mapTo } from "rxjs/operators"; -import { PrincipalLike } from "core-app/shared/components/principal/principal-types"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; + InAppNotificationDetail, +} from 'core-app/features/in-app-notifications/store/in-app-notification.model'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Observable, timer } from 'rxjs'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { InAppNotificationsService } from 'core-app/features/in-app-notifications/store/in-app-notifications.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { distinctUntilChanged, map } from 'rxjs/operators'; +import { PrincipalLike } from 'core-app/shared/components/principal/principal-types'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Component({ selector: 'op-in-app-notification-entry', templateUrl: './in-app-notification-entry.component.html', styleUrls: ['./in-app-notification-entry.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class InAppNotificationEntryComponent implements OnInit { @Input() notification:InAppNotification; + @Output() resourceLinkClicked = new EventEmitter(); workPackage$:Observable|null = null; @@ -41,6 +44,7 @@ export class InAppNotificationEntryComponent implements OnInit { // Format relative elapsed time (n seconds/minutes/hours ago) // at an interval for auto updating relativeTime$:Observable; + fixedTime:string; project?:{ href:string, title:string, showUrl:string }; @@ -49,7 +53,6 @@ export class InAppNotificationEntryComponent implements OnInit { loading: this.I18n.t('js.ajax.loading'), }; - constructor( readonly apiV3Service:APIV3Service, readonly I18n:I18nService, @@ -83,8 +86,8 @@ export class InAppNotificationEntryComponent implements OnInit { private buildDetails() { const details = this.notification.details || []; - this.body = details.filter(el => el.format === 'markdown'); - this.details = details.filter(el => el.format === 'custom'); + this.body = details.filter((el) => el.format === 'markdown'); + this.details = details.filter((el) => el.format === 'custom'); } private buildTime() { @@ -92,7 +95,7 @@ export class InAppNotificationEntryComponent implements OnInit { this.relativeTime$ = timer(0, 10000) .pipe( map(() => this.timezoneService.formattedRelativeDateTime(this.notification.updatedAt)), - distinctUntilChanged() + distinctUntilChanged(), ); } @@ -108,7 +111,7 @@ export class InAppNotificationEntryComponent implements OnInit { } private buildActor() { - const actor = this.notification._links.actor; + const { actor } = this.notification._links; if (actor) { this.actor = { @@ -120,8 +123,8 @@ export class InAppNotificationEntryComponent implements OnInit { private buildTranslatedReason() { this.translatedReason = this.I18n.t( - 'js.notifications.reasons.' + this.notification.reason, - { defaultValue: this.notification.reason } + `js.notifications.reasons.${this.notification.reason}`, + { defaultValue: this.notification.reason }, ); } @@ -130,7 +133,7 @@ export class InAppNotificationEntryComponent implements OnInit { } private buildProject() { - const project = this.notification._links.project; + const { project } = this.notification._links; if (project) { this.project = { diff --git a/frontend/src/app/features/in-app-notifications/in-app-notifications.module.ts b/frontend/src/app/features/in-app-notifications/in-app-notifications.module.ts index eb2572ade6..f4ff9ee607 100644 --- a/frontend/src/app/features/in-app-notifications/in-app-notifications.module.ts +++ b/frontend/src/app/features/in-app-notifications/in-app-notifications.module.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IconModule } from "core-app/shared/components/icon/icon.module"; -import { InAppNotificationBellComponent } from "core-app/features/in-app-notifications/bell/in-app-notification-bell.component"; +import { IconModule } from 'core-app/shared/components/icon/icon.module'; +import { InAppNotificationBellComponent } from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { InAppNotificationEntryComponent } from 'core-app/features/in-app-notifications/entry/in-app-notification-entry.component'; +import { OpenprojectPrincipalRenderingModule } from 'core-app/shared/components/principal/principal-rendering.module'; +import { UIRouterModule } from '@uirouter/angular'; +import { ScrollingModule } from '@angular/cdk/scrolling'; import { InAppNotificationCenterComponent } from './center/in-app-notification-center.component'; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { InAppNotificationEntryComponent } from "core-app/features/in-app-notifications/entry/in-app-notification-entry.component"; -import { OpenprojectPrincipalRenderingModule } from "core-app/shared/components/principal/principal-rendering.module"; -import { UIRouterModule } from "@uirouter/angular"; -import { ScrollingModule } from "@angular/cdk/scrolling"; @NgModule({ declarations: [ @@ -22,6 +22,6 @@ import { ScrollingModule } from "@angular/cdk/scrolling"; OpenprojectModalModule, OpenprojectPrincipalRenderingModule, ScrollingModule, - ] + ], }) export class OpenProjectInAppNotificationsModule { } diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notification.model.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notification.model.ts index e570c862b1..2a6d379ce9 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notification.model.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notification.model.ts @@ -1,4 +1,4 @@ -import { ID } from "@datorama/akita"; +import { ID } from '@datorama/akita'; export interface HalResourceLink { href:string; @@ -35,4 +35,4 @@ export interface InAppNotification { resource?:HalResourceLink, activity?:HalResourceLink, }; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts index 44b0cabe15..28c61d4d82 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.query.ts @@ -1,29 +1,28 @@ import { Injectable } from '@angular/core'; import { QueryEntity } from '@datorama/akita'; +import { map } from 'rxjs/operators'; import { InAppNotificationsStore, InAppNotificationsState } from './in-app-notifications.store'; -import { map, switchMap } from "rxjs/operators"; @Injectable({ providedIn: 'root' }) export class InAppNotificationsQuery extends QueryEntity { - /** Get the number of unread items */ unreadCount$ = this.select('count'); /** Do we have any unread items? */ - hasUnread$ = this.unreadCount$.pipe(map(count => count > 0)); + hasUnread$ = this.unreadCount$.pipe(map((count) => count > 0)); /** Get the unread items */ unread$ = this.selectAll({ - filterBy: ({ readIAN }) => !readIAN + filterBy: ({ readIAN }) => !readIAN, }); /** Get all items that shall be kept in the notification center */ keep$ = this.selectAll({ - filterBy: ({ keep }) => !!keep + filterBy: ({ keep }) => !!keep, }); /** Do we have any notification that shall be visible the notification center? */ - hasNotifications$ = this.selectCount().pipe(map(count => count > 0)); + hasNotifications$ = this.selectCount().pipe(map((count) => count > 0)); activeFacet$ = this.select('activeFacet'); diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts index 742244fb3e..864f7bc670 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.service.ts @@ -1,18 +1,17 @@ import { Injectable } from '@angular/core'; import { applyTransaction, ID, transaction } from '@datorama/akita'; -import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from './in-app-notification.model'; +import { Observable } from 'rxjs'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { map, switchMap, tap } from 'rxjs/operators'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { InAppNotificationsQuery } from 'core-app/features/in-app-notifications/store/in-app-notifications.query'; +import { take } from 'rxjs/internal/operators/take'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { InAppNotificationsStore } from './in-app-notifications.store'; -import { forkJoin, Observable } from "rxjs"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { map, switchMap, tap } from "rxjs/operators"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { InAppNotificationsQuery } from "core-app/features/in-app-notifications/store/in-app-notifications.query"; -import { take } from "rxjs/internal/operators/take"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { InAppNotification, NOTIFICATIONS_MAX_SIZE } from './in-app-notification.model'; @Injectable({ providedIn: 'root' }) export class InAppNotificationsService { - constructor( private store:InAppNotificationsStore, private query:InAppNotificationsQuery, @@ -31,21 +30,21 @@ export class InAppNotificationsService { .notifications .facet(facet, { pageSize: NOTIFICATIONS_MAX_SIZE }) .pipe( - tap(events => this.sideLoadInvolvedWorkPackages(events._embedded.elements)) + tap((events) => this.sideLoadInvolvedWorkPackages(events._embedded.elements)), ) .subscribe( - events => { + (events) => { applyTransaction(() => { this.store.set(events._embedded.elements); this.store.update({ count: events.total, notShowing: events.total - events.count }); }); }, - error => { + (error) => { this.notifications.addError(error); }, ) .add( - () => this.store.setLoading(false) + () => this.store.setLoading(false), ); } @@ -55,15 +54,15 @@ export class InAppNotificationsService { .notifications .unread({ pageSize: 0 }) .pipe( - map(events => events.total), - tap(count => this.store.update({ count })) + map((events) => events.total), + tap((count) => this.store.update({ count })), ); } @transaction() add(inAppNotification:InAppNotification):void { this.store.add(inAppNotification); - this.store.update(state => ({ ...state, count: state.count + 1 })); + this.store.update((state) => ({ ...state, count: state.count + 1 })); } update(id:ID, inAppNotification:Partial):void { @@ -73,11 +72,11 @@ export class InAppNotificationsService { @transaction() remove(id:ID):void { this.store.remove(id); - this.store.update(state => ({ ...state, count: state.count + 1 })); + this.store.update((state) => ({ ...state, count: state.count + 1 })); } setActiveFacet(facet:string) { - this.store.update(state => ({ ...state, activeFacet: facet })); + this.store.update((state) => ({ ...state, activeFacet: facet })); } markAllRead():void { @@ -85,9 +84,7 @@ export class InAppNotificationsService { .unread$ .pipe( take(1), - switchMap(events => - this.apiV3Service.notifications.markRead(events.map(event => event.id)) - ) + switchMap((events) => this.apiV3Service.notifications.markRead(events.map((event) => event.id))), ) .subscribe(() => { applyTransaction(() => { @@ -98,7 +95,7 @@ export class InAppNotificationsService { } private sideLoadInvolvedWorkPackages(elements:InAppNotification[]) { - const wpIds = elements.map(element => { + const wpIds = elements.map((element) => { const href = element._links.resource?.href; return href && HalResource.matchFromLink(href, 'work_packages'); }); @@ -113,8 +110,8 @@ export class InAppNotificationsService { this.store.update( notification.id, { - expanded: false - } + expanded: false, + }, ); } @@ -122,8 +119,8 @@ export class InAppNotificationsService { this.store.update( notification.id, { - expanded: true - } + expanded: true, + }, ); } @@ -139,11 +136,11 @@ export class InAppNotificationsService { notification.id, { readIAN: true, - keep: true - } + keep: true, + }, ); this.store.update( - ({ count }) => ({ count: count - 1 }) + ({ count }) => ({ count: count - 1 }), ); }); }); diff --git a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts index 72b269af35..c6504bbe74 100644 --- a/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts +++ b/frontend/src/app/features/in-app-notifications/store/in-app-notifications.store.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { EntityState, EntityStore, StoreConfig } from '@datorama/akita'; import { InAppNotification } from './in-app-notification.model'; -import { CurrentUserState } from "core-app/core/current-user/current-user.store"; export interface InAppNotificationsState extends EntityState { count:number; @@ -9,7 +8,7 @@ export interface InAppNotificationsState extends EntityState expanded:boolean; } -export function createInitialState(): InAppNotificationsState { +export function createInitialState():InAppNotificationsState { return { count: 0, notShowing: 0, diff --git a/frontend/src/app/features/invite-user-modal/button/invite-user-button.component.ts b/frontend/src/app/features/invite-user-modal/button/invite-user-button.component.ts index be2af7b533..176bdb6d1c 100644 --- a/frontend/src/app/features/invite-user-modal/button/invite-user-button.component.ts +++ b/frontend/src/app/features/invite-user-modal/button/invite-user-button.component.ts @@ -1,18 +1,19 @@ import { Component, Input } from '@angular/core'; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { Observable } from "rxjs"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { OpInviteUserModalService } from "core-app/features/invite-user-modal/invite-user-modal.service"; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { Observable } from 'rxjs'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { OpInviteUserModalService } from 'core-app/features/invite-user-modal/invite-user-modal.service'; @Component({ selector: 'op-invite-user-button', templateUrl: './invite-user-button.component.html', - styleUrls: ['./invite-user-button.component.sass'] + styleUrls: ['./invite-user-button.component.sass'], }) export class InviteUserButtonComponent { @Input() projectId:string|null; + /** This component does not provide an output, because both primary usecases were in places where the button was * destroyed before the modal closed, causing the data from the modal to never arrive at the parent. * If you want to do something with the output from the modal that is opened, use the OpInviteUserModalService @@ -37,7 +38,7 @@ export class InviteUserButtonComponent { this.projectId = this.projectId || this.currentProjectService.id; this.canInviteUsersToProject$ = this.currentUserService.hasCapabilities$( 'memberships/create', - this.projectId || undefined + this.projectId || undefined, ); } @@ -46,4 +47,4 @@ export class InviteUserButtonComponent { this.opInviteUserModalService.open(this.projectId); this.ngSelectComponent.close(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/invite-user-modal/button/invite-user-button.module.ts b/frontend/src/app/features/invite-user-modal/button/invite-user-button.module.ts index 725323c0d1..d39b3f6877 100644 --- a/frontend/src/app/features/invite-user-modal/button/invite-user-button.module.ts +++ b/frontend/src/app/features/invite-user-modal/button/invite-user-button.module.ts @@ -1,9 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { InviteUserButtonComponent } from "core-app/features/invite-user-modal/button/invite-user-button.component"; -import { IconModule } from "core-app/shared/components/icon/icon.module"; - - +import { InviteUserButtonComponent } from 'core-app/features/invite-user-modal/button/invite-user-button.component'; +import { IconModule } from 'core-app/shared/components/icon/icon.module'; @NgModule({ declarations: [ @@ -15,6 +13,6 @@ import { IconModule } from "core-app/shared/components/icon/icon.module"; ], exports: [ InviteUserButtonComponent, - ] + ], }) export class InviteUserButtonModule { } diff --git a/frontend/src/app/features/invite-user-modal/invite-user-modal-augment.service.ts b/frontend/src/app/features/invite-user-modal/invite-user-modal-augment.service.ts index d52e0a6c35..c8d1bb3e05 100644 --- a/frontend/src/app/features/invite-user-modal/invite-user-modal-augment.service.ts +++ b/frontend/src/app/features/invite-user-modal/invite-user-modal-augment.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import {Inject, Injectable} from "@angular/core"; -import {DOCUMENT} from "@angular/common"; -import {OpModalService} from "core-app/shared/components/modal/modal.service"; -import {CurrentProjectService} from "core-app/core/current-project/current-project.service"; -import {InviteUserModalComponent} from "./invite-user.component"; +import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { InviteUserModalComponent } from './invite-user.component'; import ClickEvent = JQuery.ClickEvent; const attributeSelector = '[invite-user-modal-augment]'; @@ -42,10 +41,9 @@ const attributeSelector = '[invite-user-modal-augment]'; */ @Injectable({ providedIn: 'root' }) export class OpInviteUserModalAugmentService { - constructor(@Inject(DOCUMENT) protected documentElement:Document, - protected opModalService:OpModalService, - protected currentProjectService:CurrentProjectService) { + protected opModalService:OpModalService, + protected currentProjectService:CurrentProjectService) { } /** @@ -65,7 +63,7 @@ export class OpInviteUserModalAugmentService { const modal = this.opModalService.show( InviteUserModalComponent, 'global', - { projectId: this.currentProjectService.id } + { projectId: this.currentProjectService.id }, ); modal diff --git a/frontend/src/app/features/invite-user-modal/invite-user-modal.module.ts b/frontend/src/app/features/invite-user-modal/invite-user-modal.module.ts index 162d5d7136..8355b33437 100644 --- a/frontend/src/app/features/invite-user-modal/invite-user-modal.module.ts +++ b/frontend/src/app/features/invite-user-modal/invite-user-modal.module.ts @@ -1,25 +1,25 @@ -import { APP_INITIALIZER, Injector, NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; -import { CommonModule } from "@angular/common"; +import { APP_INITIALIZER, Injector, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; import { TextFieldModule } from '@angular/cdk/text-field'; -import { NgSelectModule } from "@ng-select/ng-select"; -import { InviteUserModalComponent } from "./invite-user.component"; -import { ProjectSelectionComponent } from "./project-selection/project-selection.component"; -import { ProjectSearchComponent } from "./project-selection/project-search.component"; -import { PrincipalComponent } from "./principal/principal.component"; -import { PrincipalSearchComponent } from "./principal/principal-search.component"; -import { RoleComponent } from "./role/role.component"; -import { RoleSearchComponent } from "./role/role-search.component"; -import { MessageComponent } from "./message/message.component"; -import { SummaryComponent } from "./summary/summary.component"; -import { SuccessComponent } from "./success/success.component"; -import { CurrentUserModule } from "core-app/core/current-user/current-user.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { InviteUserButtonModule } from "core-app/features/invite-user-modal/button/invite-user-button.module"; -import { DynamicFormsModule } from "core-app/shared/components/dynamic-forms/dynamic-forms.module"; -import { OpInviteUserModalAugmentService } from "core-app/features/invite-user-modal/invite-user-modal-augment.service"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpInviteUserModalService } from "core-app/features/invite-user-modal/invite-user-modal.service"; +import { NgSelectModule } from '@ng-select/ng-select'; +import { CurrentUserModule } from 'core-app/core/current-user/current-user.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { InviteUserButtonModule } from 'core-app/features/invite-user-modal/button/invite-user-button.module'; +import { DynamicFormsModule } from 'core-app/shared/components/dynamic-forms/dynamic-forms.module'; +import { OpInviteUserModalAugmentService } from 'core-app/features/invite-user-modal/invite-user-modal-augment.service'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpInviteUserModalService } from 'core-app/features/invite-user-modal/invite-user-modal.service'; +import { InviteUserModalComponent } from './invite-user.component'; +import { ProjectSelectionComponent } from './project-selection/project-selection.component'; +import { ProjectSearchComponent } from './project-selection/project-search.component'; +import { PrincipalComponent } from './principal/principal.component'; +import { PrincipalSearchComponent } from './principal/principal-search.component'; +import { RoleComponent } from './role/role.component'; +import { RoleSearchComponent } from './role/role-search.component'; +import { MessageComponent } from './message/message.component'; +import { SummaryComponent } from './summary/summary.component'; +import { SuccessComponent } from './success/success.component'; export function initializeServices(injector:Injector) { return function () { @@ -57,8 +57,10 @@ export function initializeServices(injector:Injector) { ], providers: [ OpInviteUserModalService, - { provide: APP_INITIALIZER, useFactory: initializeServices, deps: [Injector], multi: true }, + { + provide: APP_INITIALIZER, useFactory: initializeServices, deps: [Injector], multi: true, + }, ], }) export class OpenprojectInviteUserModalModule { -} \ No newline at end of file +} diff --git a/frontend/src/app/features/invite-user-modal/invite-user-modal.service.ts b/frontend/src/app/features/invite-user-modal/invite-user-modal.service.ts index 5ae47d9be2..4a4c87ab0d 100644 --- a/frontend/src/app/features/invite-user-modal/invite-user-modal.service.ts +++ b/frontend/src/app/features/invite-user-modal/invite-user-modal.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Injectable, EventEmitter } from "@angular/core"; -import { InviteUserModalComponent } from "./invite-user.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; +import { Injectable, EventEmitter } from '@angular/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { InviteUserModalComponent } from './invite-user.component'; /** * This service triggers user-invite modals to clicks on elements diff --git a/frontend/src/app/features/invite-user-modal/invite-user.component.ts b/frontend/src/app/features/invite-user-modal/invite-user.component.ts index e51d6d3139..d89700f42d 100644 --- a/frontend/src/app/features/invite-user-modal/invite-user.component.ts +++ b/frontend/src/app/features/invite-user-modal/invite-user.component.ts @@ -9,12 +9,12 @@ import { } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { PrincipalData } from "core-app/shared/components/principal/principal-types"; -import { RoleResource } from "core-app/features/hal/resources/role-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { PrincipalData } from 'core-app/shared/components/principal/principal-types'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; enum Steps { ProjectSelection, @@ -39,6 +39,7 @@ export enum PrincipalType { }) export class InviteUserModalComponent extends OpModalComponent implements OnInit { public Steps = Steps; + public step = Steps.ProjectSelection; /* Close on outside click */ @@ -48,13 +49,18 @@ export class InviteUserModalComponent extends OpModalComponent implements OnInit public data:any = null; public type:PrincipalType|null = null; + public project:ProjectResource|null = null; + public principalData:PrincipalData = { principal: null, customFields: {}, }; + public role:RoleResource|null = null; + public message = ''; + public createdNewPrincipal = false; public get loading() { @@ -75,7 +81,7 @@ export class InviteUserModalComponent extends OpModalComponent implements OnInit if (this.locals.projectId) { this.apiV3Service.projects.id(this.locals.projectId).get().subscribe( - data => { + (data) => { this.project = data; this.cdRef.markForCheck(); }, @@ -84,7 +90,7 @@ export class InviteUserModalComponent extends OpModalComponent implements OnInit this.cdRef.markForCheck(); }, ); - } + } } onProjectSelectionSave({ type, project }:{ type:PrincipalType, project:any }) { diff --git a/frontend/src/app/features/invite-user-modal/message/message.component.ts b/frontend/src/app/features/invite-user-modal/message/message.component.ts index 7900d3a58f..2a03f47587 100644 --- a/frontend/src/app/features/invite-user-modal/message/message.component.ts +++ b/frontend/src/app/features/invite-user-modal/message/message.component.ts @@ -11,10 +11,10 @@ import { FormControl, FormGroup, } from '@angular/forms'; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {PrincipalType} from '../invite-user.component'; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { PrincipalType } from '../invite-user.component'; @Component({ selector: 'op-ium-message', @@ -23,15 +23,20 @@ import { HalResource } from "core-app/features/hal/resources/hal-resource"; }) export class MessageComponent implements OnInit { @Input() type:PrincipalType; + @Input() project:ProjectResource; + @Input() principal:HalResource; - @Input() message:string = ''; + + @Input() message = ''; @Output() close = new EventEmitter(); + @Output() back = new EventEmitter(); - @Output() save = new EventEmitter<{message:string}>(); - @ViewChild('input') input: ElementRef; + @Output() save = new EventEmitter<{ message:string }>(); + + @ViewChild('input') input:ElementRef; public text = { title: () => this.I18n.t('js.invite_user_modal.title.invite_principal_to_project', { @@ -50,7 +55,9 @@ export class MessageComponent implements OnInit { message: new FormControl(''), }); - get messageControl() { return this.messageForm.get('message'); } + get messageControl() { + return this.messageForm.get('message'); + } constructor( readonly I18n:I18nService, diff --git a/frontend/src/app/features/invite-user-modal/principal/principal-search.component.ts b/frontend/src/app/features/invite-user-modal/principal/principal-search.component.ts index e894c81698..dee1adf2a4 100644 --- a/frontend/src/app/features/invite-user-modal/principal/principal-search.component.ts +++ b/frontend/src/app/features/invite-user-modal/principal/principal-search.component.ts @@ -6,23 +6,27 @@ import { Output, ElementRef, } from '@angular/core'; -import {FormControl} from "@angular/forms"; -import {Observable, BehaviorSubject, combineLatest, forkJoin} from "rxjs"; -import {debounceTime, distinctUntilChanged, share, map, shareReplay, switchMap} from "rxjs/operators"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {UntilDestroyedMixin} from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import {ProjectResource} from "core-app/features/hal/resources/project-resource"; -import {UserResource} from "core-app/features/hal/resources/user-resource"; -import {PrincipalLike} from "core-app/shared/components/principal/principal-types"; -import {CurrentUserService} from "core-app/core/current-user/current-user.service"; -import {PrincipalType} from '../invite-user.component'; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { FormControl } from '@angular/forms'; +import { + Observable, BehaviorSubject, combineLatest, forkJoin, +} from 'rxjs'; +import { + debounceTime, distinctUntilChanged, share, map, shareReplay, switchMap, +} from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { PrincipalLike } from 'core-app/shared/components/principal/principal-types'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { PrincipalType } from '../invite-user.component'; interface NgSelectPrincipalOption { - principal: PrincipalLike, - disabled: boolean; -}; + principal:PrincipalLike, + disabled:boolean; +} @Component({ selector: 'op-ium-principal-search', @@ -30,34 +34,37 @@ interface NgSelectPrincipalOption { }) export class PrincipalSearchComponent extends UntilDestroyedMixin implements OnInit { @Input('opFormBinding') principalControl:FormControl; + @Input() type:PrincipalType; + @Input() project:ProjectResource; @Output() createNew = new EventEmitter(); public input$ = new BehaviorSubject(''); + public input = ''; - public items$: Observable = this.input$.pipe( + + public items$:Observable = this.input$.pipe( this.untilDestroyed(), debounceTime(200), distinctUntilChanged(), switchMap(this.loadPrincipalData.bind(this)), share(), ); - private emailRegExp:RegExp = /^\S+@\S+\.\S+$/; + + private emailRegExp = /^\S+@\S+\.\S+$/; public canInviteByEmail$ = combineLatest( this.items$, this.input$, this.currentUserService.hasCapabilities$('users/create'), ).pipe( - map(([elements, input, canCreateUsers]) => { - return canCreateUsers + map(([elements, input, canCreateUsers]) => canCreateUsers && this.type === PrincipalType.User && !!input && this.emailRegExp.test(input) - && !elements.find((el) => (el.principal as UserResource).email === input); - }), + && !elements.find((el) => (el.principal as UserResource).email === input)), ); public canCreateNewPlaceholder$ = combineLatest( @@ -109,8 +116,10 @@ export class PrincipalSearchComponent extends UntilDestroyedMixin implements OnI this.canInviteByEmail$, this.canCreateNewPlaceholder$, ).pipe( - map(([canInviteByEmail, canCreateNewPlaceholder]:boolean[]) => canInviteByEmail || canCreateNewPlaceholder) - ).subscribe(showAddTag => { this.showAddTag = showAddTag; }); + map(([canInviteByEmail, canCreateNewPlaceholder]:boolean[]) => canInviteByEmail || canCreateNewPlaceholder), + ).subscribe((showAddTag) => { + this.showAddTag = showAddTag; + }); } ngOnInit() { @@ -129,7 +138,7 @@ export class PrincipalSearchComponent extends UntilDestroyedMixin implements OnI } nonMemberFilter.add('status', '!', [3]); nonMemberFilter.add('type', '=', [this.type]); - nonMemberFilter.add('member', '!', [this.project?.id]); + nonMemberFilter.add('member', '!', [this.project?.id || '']); const nonMembers = this.apiV3Service.principals.filtered(nonMemberFilter).get(); const memberFilter = new ApiV3FilterBuilder(); @@ -138,7 +147,7 @@ export class PrincipalSearchComponent extends UntilDestroyedMixin implements OnI } memberFilter.add('status', '!', [3]); memberFilter.add('type', '=', [this.type]); - memberFilter.add('member', '=', [this.project?.id]); + memberFilter.add('member', '=', [this.project?.id || '']); const members = this.apiV3Service.principals.filtered(memberFilter).get(); return forkJoin({ @@ -160,7 +169,5 @@ export class PrincipalSearchComponent extends UntilDestroyedMixin implements OnI ); } - compareWith = (a: NgSelectPrincipalOption, b: PrincipalLike) => { - return a.principal.id === b.id; - } + compareWith = (a:NgSelectPrincipalOption, b:PrincipalLike) => a.principal.id === b.id; } diff --git a/frontend/src/app/features/invite-user-modal/principal/principal.component.ts b/frontend/src/app/features/invite-user-modal/principal/principal.component.ts index d0e4d351bd..b282c3b62a 100644 --- a/frontend/src/app/features/invite-user-modal/principal/principal.component.ts +++ b/frontend/src/app/features/invite-user-modal/principal/principal.component.ts @@ -1,19 +1,21 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, } from '@angular/core'; -import { HttpClient } from "@angular/common/http"; +import { + ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, +} from '@angular/core'; +import { HttpClient } from '@angular/common/http'; import { FormGroup, FormControl, Validators, } from '@angular/forms'; -import { PrincipalType } from '../invite-user.component'; import { take } from 'rxjs/internal/operators/take'; import { map } from 'rxjs/operators'; import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"; -import { PrincipalData, PrincipalLike } from "core-app/shared/components/principal/principal-types"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { DynamicFormComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component'; +import { PrincipalData, PrincipalLike } from 'core-app/shared/components/principal/principal-types'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { PrincipalType } from '../invite-user.component'; function extractCustomFieldsFromSchema(schema:IOPFormSettings['_embedded']['schema']) { return Object.keys(schema) @@ -36,11 +38,15 @@ function extractCustomFieldsFromSchema(schema:IOPFormSettings['_embedded']['sche }) export class PrincipalComponent implements OnInit { @Input() principalData:PrincipalData; + @Input() project:ProjectResource; + @Input() type:PrincipalType; @Output() close = new EventEmitter(); + @Output() save = new EventEmitter<{ principalData:PrincipalData, isAlreadyMember:boolean }>(); + @Output() back = new EventEmitter(); @ViewChild(DynamicFormComponent) dynamicForm:DynamicFormComponent; @@ -56,7 +62,7 @@ export class PrincipalComponent implements OnInit { User: this.I18n.t('js.invite_user_modal.principal.label.name_or_email'), PlaceholderUser: this.I18n.t('js.invite_user_modal.principal.label.name'), Group: this.I18n.t('js.invite_user_modal.principal.label.name'), - Email: this.I18n.t('js.label_email') + Email: this.I18n.t('js.label_email'), }, change: this.I18n.t('js.label_change'), inviteUser: this.I18n.t('js.invite_user_modal.principal.invite_user'), @@ -106,9 +112,8 @@ export class PrincipalComponent implements OnInit { get textLabel() { if (this.type === PrincipalType.User && this.isNewPrincipal) { return this.text.label.Email; - } else { - return this.text.label[this.type]; } + return this.text.label[this.type]; } get isNewPrincipal() { @@ -140,7 +145,7 @@ export class PrincipalComponent implements OnInit { take(1), // The subsequent code expects to not work with a HalResource but rather with the raw // api response. - map(formResource => formResource.$source) + map((formResource) => formResource.$source), ) .subscribe((formConfig) => { this.userDynamicFieldConfig.schema = extractCustomFieldsFromSchema(formConfig._embedded?.schema); @@ -177,13 +182,13 @@ export class PrincipalComponent implements OnInit { const fieldsSchema = this.userDynamicFieldConfig.schema || {}; const customFields = Object.keys(fieldsSchema) .reduce((result, fieldKey) => { - let fieldSchema = fieldsSchema[fieldKey]; + const fieldSchema = fieldsSchema[fieldKey]; let fieldValue = this.customFields[fieldKey]; if (fieldSchema.location === '_links') { fieldValue = Array.isArray(fieldValue) - ? fieldValue.map((opt: any) => opt._links ? opt._links.self : opt) - : (fieldValue._links ? fieldValue._links.self : fieldValue) + ? fieldValue.map((opt:any) => (opt._links ? opt._links.self : opt)) + : (fieldValue._links ? fieldValue._links.self : fieldValue); } result = { diff --git a/frontend/src/app/features/invite-user-modal/project-selection/project-allowed.validator.ts b/frontend/src/app/features/invite-user-modal/project-selection/project-allowed.validator.ts index 2e52668a2f..be70ec0aa7 100644 --- a/frontend/src/app/features/invite-user-modal/project-selection/project-allowed.validator.ts +++ b/frontend/src/app/features/invite-user-modal/project-selection/project-allowed.validator.ts @@ -1,14 +1,10 @@ -import { AbstractControl } from "@angular/forms"; -import { of } from "rxjs"; -import { catchError, map, take } from "rxjs/operators"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; +import { AbstractControl } from '@angular/forms'; +import { of } from 'rxjs'; +import { catchError, map, take } from 'rxjs/operators'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -export const ProjectAllowedValidator = (currentUserService:CurrentUserService) => { - return (control: AbstractControl) => { - return currentUserService.hasCapabilities$('memberships/create', control.value.idFromLink).pipe( - take(1), - map(isAllowed => isAllowed ? null : { lackingPermission: true }), - catchError(() => of(null)) - ) - } -} \ No newline at end of file +export const ProjectAllowedValidator = (currentUserService:CurrentUserService) => (control:AbstractControl) => currentUserService.hasCapabilities$('memberships/create', control.value.idFromLink).pipe( + take(1), + map((isAllowed) => (isAllowed ? null : { lackingPermission: true })), + catchError(() => of(null)), +); diff --git a/frontend/src/app/features/invite-user-modal/project-selection/project-search.component.ts b/frontend/src/app/features/invite-user-modal/project-selection/project-search.component.ts index 9db8c30b00..028f72f5d8 100644 --- a/frontend/src/app/features/invite-user-modal/project-selection/project-search.component.ts +++ b/frontend/src/app/features/invite-user-modal/project-selection/project-search.component.ts @@ -4,19 +4,19 @@ import { OnInit, ElementRef, } from '@angular/core'; -import { FormControl } from "@angular/forms"; -import { BehaviorSubject, combineLatest } from "rxjs"; -import { debounceTime, map, switchMap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { FormControl } from '@angular/forms'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { debounceTime, map, switchMap } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; interface NgSelectProjectOption { - project: ProjectResource, - disabled: boolean; + project:ProjectResource, + disabled:boolean; } @Component({ @@ -32,6 +32,7 @@ export class ProjectSearchComponent extends UntilDestroyedMixin implements OnIni }; public input$ = new BehaviorSubject(''); + public items$ = combineLatest([ this.input$.pipe( debounceTime(100), @@ -44,25 +45,25 @@ export class ProjectSearchComponent extends UntilDestroyedMixin implements OnIni return this.apiV3Service.projects .filtered(filters) .get() - .pipe(map(collection => collection.elements)); - }) + .pipe(map((collection) => collection.elements)); + }), ), this.currentUserService.capabilities$.pipe( - map(capabilities => capabilities.filter(c => c.action.href.endsWith('/memberships/create'))) + map((capabilities) => capabilities.filter((c) => c.action.href.endsWith('/memberships/create'))), ), ]) .pipe( this.untilDestroyed(), - map(([ projects, projectInviteCapabilities ]) => { - const mapped = projects.map((project: ProjectResource) => ({ + map(([projects, projectInviteCapabilities]) => { + const mapped = projects.map((project:ProjectResource) => ({ project, - disabled: !projectInviteCapabilities.find(cap => cap.context.id === project.id), + disabled: !projectInviteCapabilities.find((cap) => cap.context.id === project.id), })); mapped.sort( - (a: NgSelectProjectOption, b: NgSelectProjectOption) => (a.disabled ? 1 : 0) - (b.disabled ? 1 : 0), + (a:NgSelectProjectOption, b:NgSelectProjectOption) => (a.disabled ? 1 : 0) - (b.disabled ? 1 : 0), ); return mapped; - }) + }), ); constructor( @@ -79,7 +80,5 @@ export class ProjectSearchComponent extends UntilDestroyedMixin implements OnIni setTimeout(() => this.input$.next('')); } - compareWith = (a: NgSelectProjectOption, b: ProjectResource) => { - return a.project.id === b.id; - } + compareWith = (a:NgSelectProjectOption, b:ProjectResource) => a.project.id === b.id; } diff --git a/frontend/src/app/features/invite-user-modal/project-selection/project-selection.component.ts b/frontend/src/app/features/invite-user-modal/project-selection/project-selection.component.ts index ffa774c708..9d04faa4b2 100644 --- a/frontend/src/app/features/invite-user-modal/project-selection/project-selection.component.ts +++ b/frontend/src/app/features/invite-user-modal/project-selection/project-selection.component.ts @@ -11,11 +11,11 @@ import { FormGroup, Validators, } from '@angular/forms'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { BannersService } from "core-app/core/enterprise/banners.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { IOpOptionListOption } from "core-app/shared/components/option-list/option-list.component"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { IOpOptionListOption } from 'core-app/shared/components/option-list/option-list.component'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; import { PrincipalType } from '../invite-user.component'; import { ProjectAllowedValidator } from './project-allowed.validator'; @@ -26,10 +26,12 @@ import { ProjectAllowedValidator } from './project-allowed.validator'; }) export class ProjectSelectionComponent implements OnInit { @Input() type:PrincipalType; + @Input() project:ProjectResource|null; @Output() close = new EventEmitter(); - @Output() save = new EventEmitter<{project:any, type:string}>(); + + @Output() save = new EventEmitter<{ project:any, type:string }>(); public text = { title: this.I18n.t('js.invite_user_modal.title.invite'), @@ -58,12 +60,17 @@ export class ProjectSelectionComponent implements OnInit { ]; projectAndTypeForm = new FormGroup({ - type: new FormControl(PrincipalType.User, [ Validators.required ]), - project: new FormControl(null, [ Validators.required ], ProjectAllowedValidator(this.currentUserService)) + type: new FormControl(PrincipalType.User, [Validators.required]), + project: new FormControl(null, [Validators.required], ProjectAllowedValidator(this.currentUserService)), }); - get typeControl() { return this.projectAndTypeForm.get('type'); } - get projectControl() { return this.projectAndTypeForm.get('project'); } + get typeControl() { + return this.projectAndTypeForm.get('type'); + } + + get projectControl() { + return this.projectAndTypeForm.get('project'); + } constructor( readonly I18n:I18nService, diff --git a/frontend/src/app/features/invite-user-modal/role/role-search.component.ts b/frontend/src/app/features/invite-user-modal/role/role-search.component.ts index 4850f9cb37..3bd931ee8d 100644 --- a/frontend/src/app/features/invite-user-modal/role/role-search.component.ts +++ b/frontend/src/app/features/invite-user-modal/role/role-search.component.ts @@ -4,13 +4,15 @@ import { Input, ElementRef, } from '@angular/core'; -import { FormControl } from "@angular/forms"; -import { Observable, Subject, combineLatest } from "rxjs"; -import { debounceTime, distinctUntilChanged, filter, map, switchMap, tap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { FormControl } from '@angular/forms'; +import { Observable, Subject, combineLatest } from 'rxjs'; +import { + debounceTime, distinctUntilChanged, filter, map, +} from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; @Component({ selector: 'op-ium-role-search', @@ -20,7 +22,9 @@ export class RoleSearchComponent extends UntilDestroyedMixin implements OnInit { @Input('opFormBinding') roleControl:FormControl; public input$ = new Subject(); + public roles$ = new Subject(); + public items$:Observable; public text = { @@ -39,13 +43,13 @@ export class RoleSearchComponent extends UntilDestroyedMixin implements OnInit { .pipe( this.untilDestroyed(), debounceTime(200), - filter(input => typeof input === 'string'), + filter((input) => typeof input === 'string'), map((input:string) => input.toLowerCase()), distinctUntilChanged(), ), this.roles$, ).pipe( - map(([input, roles]:[string, any[]]) => roles.filter((role) => !input || role.name.toLowerCase().indexOf(input) !== -1)) + map(([input, roles]:[string, any[]]) => roles.filter((role) => !input || role.name.toLowerCase().indexOf(input) !== -1)), ); } diff --git a/frontend/src/app/features/invite-user-modal/role/role.component.ts b/frontend/src/app/features/invite-user-modal/role/role.component.ts index d749510061..eb1f91ee27 100644 --- a/frontend/src/app/features/invite-user-modal/role/role.component.ts +++ b/frontend/src/app/features/invite-user-modal/role/role.component.ts @@ -10,11 +10,11 @@ import { FormGroup, Validators, } from '@angular/forms'; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {PrincipalType} from '../invite-user.component'; -import {RoleResource} from "core-app/features/hal/resources/role-resource"; -import {ProjectResource} from "core-app/features/hal/resources/project-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { PrincipalType } from '../invite-user.component'; @Component({ selector: 'op-ium-role', @@ -23,12 +23,17 @@ import { HalResource } from "core-app/features/hal/resources/hal-resource"; }) export class RoleComponent implements OnInit { @Input() type:PrincipalType; + @Input() project:ProjectResource; + @Input() principal:HalResource; + @Input() role:RoleResource; @Output() close = new EventEmitter(); + @Output() back = new EventEmitter(); + @Output() save = new EventEmitter(); public text = { @@ -48,10 +53,12 @@ export class RoleComponent implements OnInit { }; roleForm = new FormGroup({ - role: new FormControl(null, [ Validators.required ]), + role: new FormControl(null, [Validators.required]), }); - get roleControl() { return this.roleForm.get('role'); } + get roleControl() { + return this.roleForm.get('role'); + } constructor(readonly I18n:I18nService) {} diff --git a/frontend/src/app/features/invite-user-modal/success/success.component.ts b/frontend/src/app/features/invite-user-modal/success/success.component.ts index 809b340876..63d2902431 100644 --- a/frontend/src/app/features/invite-user-modal/success/success.component.ts +++ b/frontend/src/app/features/invite-user-modal/success/success.component.ts @@ -5,11 +5,11 @@ import { Output, ElementRef, } from '@angular/core'; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {PrincipalType} from '../invite-user.component'; -import {HalResource} from "core-app/features/hal/resources/hal-resource"; -import {ProjectResource} from "core-app/features/hal/resources/project-resource"; -import {ImageHelpers} from "core-app/shared/helpers/images/path-helper"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { imagePath } from 'core-app/shared/helpers/images/path-helper'; +import { PrincipalType } from '../invite-user.component'; @Component({ selector: 'op-ium-success', @@ -18,16 +18,20 @@ import {ImageHelpers} from "core-app/shared/helpers/images/path-helper"; }) export class SuccessComponent { @Input() principal:HalResource; + @Input() project:ProjectResource; + @Input() type:PrincipalType; + @Input() createdNewPrincipal:boolean; @Output() close = new EventEmitter(); public PrincipalType = PrincipalType; - user_image = ImageHelpers.imagePath('invite-user-modal/successful-invite.svg'); - placeholder_image = ImageHelpers.imagePath('invite-user-modal/placeholder-added.svg'); + user_image = imagePath('invite-user-modal/successful-invite.svg'); + + placeholder_image = imagePath('invite-user-modal/placeholder-added.svg'); public text = { title: () => this.I18n.t('js.invite_user_modal.success.title', { diff --git a/frontend/src/app/features/invite-user-modal/summary/summary.component.ts b/frontend/src/app/features/invite-user-modal/summary/summary.component.ts index 5c0ede8e95..07e200c74e 100644 --- a/frontend/src/app/features/invite-user-modal/summary/summary.component.ts +++ b/frontend/src/app/features/invite-user-modal/summary/summary.component.ts @@ -5,15 +5,15 @@ import { Output, ElementRef, } from '@angular/core'; -import {Observable, of} from "rxjs"; -import {mapTo, switchMap} from "rxjs/operators"; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; -import {RoleResource} from "core-app/features/hal/resources/role-resource"; -import {PrincipalData, PrincipalLike} from "core-app/shared/components/principal/principal-types"; -import {HalResource} from "core-app/features/hal/resources/hal-resource"; -import {PrincipalType} from '../invite-user.component'; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; +import { Observable, of } from 'rxjs'; +import { mapTo, switchMap } from 'rxjs/operators'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { RoleResource } from 'core-app/features/hal/resources/role-resource'; +import { PrincipalData } from 'core-app/shared/components/principal/principal-types'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { PrincipalType } from '../invite-user.component'; @Component({ selector: 'op-ium-summary', @@ -22,13 +22,19 @@ import { ProjectResource } from "core-app/features/hal/resources/project-resourc }) export class SummaryComponent { @Input() type:PrincipalType; + @Input() project:ProjectResource; + @Input() role:RoleResource; + @Input() principalData:PrincipalData; - @Input() message:string = ''; + + @Input() message = ''; @Output() close = new EventEmitter(); + @Output() back = new EventEmitter(); + @Output() save = new EventEmitter(); public PrincipalType = PrincipalType; @@ -69,20 +75,18 @@ export class SummaryComponent { return of(this.principalData) .pipe( switchMap((principalData:PrincipalData) => this.createPrincipal(principalData)), - switchMap((principal:HalResource) => - this.api.memberships - .post({ - principal, - project: this.project, - roles: [this.role], - notificationMessage: { - raw: this.message - } - }) - .pipe( - mapTo(principal) - ) - ) + switchMap((principal:HalResource) => this.api.memberships + .post({ + principal, + project: this.project, + roles: [this.role], + notificationMessage: { + raw: this.message, + }, + }) + .pipe( + mapTo(principal), + )), ); } @@ -102,7 +106,7 @@ export class SummaryComponent { case PrincipalType.Placeholder: return this.api.placeholder_users.post({ name: principal!.name }); default: - throw new Error("Unsupported PrincipalType given"); + throw new Error('Unsupported PrincipalType given'); } } @@ -111,8 +115,6 @@ export class SummaryComponent { this .invite() - .subscribe((principal) => - this.save.emit({ principal }) - ); + .subscribe((principal) => this.save.emit({ principal })); } } diff --git a/frontend/src/app/features/job-status/display-job-page/display-job-page.component.ts b/frontend/src/app/features/job-status/display-job-page/display-job-page.component.ts index dadcd6ab06..e685d23eb4 100644 --- a/frontend/src/app/features/job-status/display-job-page/display-job-page.component.ts +++ b/frontend/src/app/features/job-status/display-job-page/display-job-page.component.ts @@ -1,26 +1,28 @@ -import { AfterViewInit, Component, Injector, OnDestroy } from "@angular/core"; -import { StateService } from "@uirouter/core"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { JobStatusModal } from "core-app/features/job-status/job-status-modal/job-status.modal"; -import { take } from "rxjs/operators"; +import { + AfterViewInit, Component, OnDestroy, +} from '@angular/core'; +import { StateService } from '@uirouter/core'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { JobStatusModalComponent } from 'core-app/features/job-status/job-status-modal/job-status.modal'; +import { take } from 'rxjs/operators'; @Component({ - template: '' + template: '', }) export class DisplayJobPageComponent implements AfterViewInit, OnDestroy { private modal?:OpModalComponent; constructor(private $state:StateService, - private modalService:OpModalService) { + private modalService:OpModalService) { } ngAfterViewInit() { - this.modal = this.modalService.show(JobStatusModal, 'global', { jobId: this.$state.params.jobId }); + this.modal = this.modalService.show(JobStatusModalComponent, 'global', { jobId: this.$state.params.jobId }); this.modal .closingEvent .pipe( - take(1) + take(1), ).subscribe(() => { // Go back in history window.history.back(); @@ -30,4 +32,4 @@ export class DisplayJobPageComponent implements AfterViewInit, OnDestroy { ngOnDestroy() { this.modal?.closeMe(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/job-status/job-status-modal/job-status.modal.ts b/frontend/src/app/features/job-status/job-status-modal/job-status.modal.ts index 5b2ec3fbed..78dc75183e 100644 --- a/frontend/src/app/features/job-status/job-status-modal/job-status.modal.ts +++ b/frontend/src/app/features/job-status/job-status-modal/job-status.modal.ts @@ -1,27 +1,27 @@ -import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild, +} from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Observable, timer } from "rxjs"; -import { switchMap, takeWhile } from "rxjs/operators"; +import { Observable, timer } from 'rxjs'; +import { switchMap, takeWhile } from 'rxjs/operators'; import { LoadingIndicatorService, - withDelayedLoadingIndicator -} from "core-app/core/loading-indicator/loading-indicator.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { JobStatusEnum, JobStatusInterface } from "core-app/features/job-status/job-status.interface"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; - + withDelayedLoadingIndicator, +} from 'core-app/core/loading-indicator/loading-indicator.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { JobStatusEnum, JobStatusInterface } from 'core-app/features/job-status/job-status.interface'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './job-status.modal.html', - styleUrls: ['./job-status.modal.sass'] + styleUrls: ['./job-status.modal.sass'], }) -export class JobStatusModal extends OpModalComponent implements OnInit { - +export class JobStatusModalComponent extends OpModalComponent implements OnInit { /* Close on escape? */ public closeOnEscape = false; @@ -32,7 +32,7 @@ export class JobStatusModal extends OpModalComponent implements OnInit { title: this.I18n.t('js.job_status.title'), closePopup: this.I18n.t('js.close_popup_title'), redirect: this.I18n.t('js.job_status.redirect'), - redirect_errors: this.I18n.t('js.job_status.redirect_errors') + ' ', + redirect_errors: `${this.I18n.t('js.job_status.redirect_errors')} `, redirect_link: this.I18n.t('js.job_status.redirect_link'), errors: this.I18n.t('js.job_status.errors'), download_starts: this.I18n.t('js.job_status.download_starts'), @@ -66,14 +66,14 @@ export class JobStatusModal extends OpModalComponent implements OnInit { @ViewChild('downloadLink') private downloadLink:ElementRef; constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly pathHelper:PathHelperService, - readonly apiV3Service:APIV3Service, - readonly loadingIndicator:LoadingIndicatorService, - readonly notifications:NotificationsService, - readonly httpClient:HttpClient) { + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly elementRef:ElementRef, + readonly pathHelper:PathHelperService, + readonly apiV3Service:APIV3Service, + readonly loadingIndicator:LoadingIndicatorService, + readonly notifications:NotificationsService, + readonly httpClient:HttpClient) { super(locals, cdRef, elementRef); this.jobId = locals.jobId; @@ -88,28 +88,28 @@ export class JobStatusModal extends OpModalComponent implements OnInit { timer(0, 2000) .pipe( switchMap(() => this.performRequest()), - takeWhile(response => !!response.body && this.continuedStatus(response.body), true), + takeWhile((response) => !!response.body && this.continuedStatus(response.body), true), this.untilDestroyed(), withDelayedLoadingIndicator(this.loadingIndicator.getter('modal')), ).subscribe( - response => this.onResponse(response), - error => this.handleError(error), - () => this.isLoading = false + (response) => this.onResponse(response), + (error) => this.handleError(error), + () => this.isLoading = false, ); } private iconForStatus():string|null { switch (this.status) { - case "cancelled": - case "failure": - case "error": - return 'icon-error'; - break; - case "success": - return "icon-checkmark"; - break; - default: - return null; + case 'cancelled': + case 'failure': + case 'error': + return 'icon-error'; + break; + case 'success': + return 'icon-checkmark'; + break; + default: + return null; } } @@ -122,7 +122,7 @@ export class JobStatusModal extends OpModalComponent implements OnInit { } private onResponse(response:HttpResponse) { - const body = response.body; + const { body } = response; if (!body) { throw new Error(response as any); @@ -130,8 +130,8 @@ export class JobStatusModal extends OpModalComponent implements OnInit { const status = this.status = body.status; - this.message = body.message || - this.I18n.t(`js.job_status.generic_messages.${status}`, { defaultValue: status }); + this.message = body.message + || this.I18n.t(`js.job_status.generic_messages.${status}`, { defaultValue: status }); this.payload = body.payload; if (body.payload) { @@ -157,9 +157,9 @@ export class JobStatusModal extends OpModalComponent implements OnInit { this.httpClient .get(redirectionUrl, { observe: 'response', - responseType: 'text' + responseType: 'text', }) - .subscribe(response => { + .subscribe((response) => { this.downloadHref = response.url; this.cdRef.detectChanges(); @@ -172,9 +172,9 @@ export class JobStatusModal extends OpModalComponent implements OnInit { return this .httpClient .get( - this.jobUrl, - { observe: 'response', responseType: 'json' } - ); + this.jobUrl, + { observe: 'response', responseType: 'json' }, + ); } private handleError(error:HttpErrorResponse) { @@ -184,7 +184,6 @@ export class JobStatusModal extends OpModalComponent implements OnInit { return; } - this.statusIcon = 'icon-error'; this.message = error?.message || this.I18n.t('js.error.internal'); this.notifications.addError(this.message); diff --git a/frontend/src/app/features/job-status/job-status.interface.ts b/frontend/src/app/features/job-status/job-status.interface.ts index 369e3274dd..d6b1f6b5cd 100644 --- a/frontend/src/app/features/job-status/job-status.interface.ts +++ b/frontend/src/app/features/job-status/job-status.interface.ts @@ -1,4 +1,4 @@ -export type JobStatusEnum = 'in_queue'|'error'|'in_process'|'success'|'failure'|'cancelled' ; +export type JobStatusEnum = 'in_queue'|'error'|'in_process'|'success'|'failure'|'cancelled'; export interface JobStatusInterface { /** @@ -24,4 +24,4 @@ export interface JobStatusInterface { download?:string; redirect?:string; }; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/job-status/openproject-job-status.module.ts b/frontend/src/app/features/job-status/openproject-job-status.module.ts index 91b1187c52..6752bf01b9 100644 --- a/frontend/src/app/features/job-status/openproject-job-status.module.ts +++ b/frontend/src/app/features/job-status/openproject-job-status.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,10 +28,10 @@ import { NgModule } from '@angular/core'; import { OPSharedModule } from 'core-app/shared/shared.module'; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { Ng2StateDeclaration, UIRouterModule } from "@uirouter/angular"; -import { DisplayJobPageComponent } from "core-app/features/job-status/display-job-page/display-job-page.component"; -import { JobStatusModal } from "core-app/features/job-status/job-status-modal/job-status.modal"; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { Ng2StateDeclaration, UIRouterModule } from '@uirouter/angular'; +import { DisplayJobPageComponent } from 'core-app/features/job-status/display-job-page/display-job-page.component'; +import { JobStatusModalComponent } from 'core-app/features/job-status/job-status-modal/job-status.modal'; export const JOB_STATUS_ROUTE:Ng2StateDeclaration[] = [ { @@ -40,9 +40,9 @@ export const JOB_STATUS_ROUTE:Ng2StateDeclaration[] = [ parent: 'root', component: DisplayJobPageComponent, data: { - bodyClasses: 'router--job-statuses' - } - } + bodyClasses: 'router--job-statuses', + }, + }, ]; @NgModule({ @@ -57,8 +57,8 @@ export const JOB_STATUS_ROUTE:Ng2StateDeclaration[] = [ ], declarations: [ DisplayJobPageComponent, - JobStatusModal - ] + JobStatusModalComponent, + ], }) export class OpenProjectJobStatusModule { } diff --git a/frontend/src/app/features/my-page/my-page.component.ts b/frontend/src/app/features/my-page/my-page.component.ts index f6f3c9626f..c08655b0c7 100644 --- a/frontend/src/app/features/my-page/my-page.component.ts +++ b/frontend/src/app/features/my-page/my-page.component.ts @@ -1,11 +1,11 @@ -import { Component } from "@angular/core"; -import { GRID_PROVIDERS } from "core-app/shared/components/grids/grid/grid.component"; -import { GridPageComponent } from "core-app/shared/components/grids/grid/page/grid-page.component"; +import { Component } from '@angular/core'; +import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component'; +import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component'; @Component({ templateUrl: '../../shared/components/grids/grid/page/grid-page.component.html', styleUrls: ['../../shared/components/grids/grid/page/grid-page.component.sass'], - providers: GRID_PROVIDERS + providers: GRID_PROVIDERS, }) export class MyPageComponent extends GridPageComponent { protected i18nNamespace():string { diff --git a/frontend/src/app/features/my-page/openproject-my-page.module.ts b/frontend/src/app/features/my-page/openproject-my-page.module.ts index 36e42f3142..9897f5601b 100644 --- a/frontend/src/app/features/my-page/openproject-my-page.module.ts +++ b/frontend/src/app/features/my-page/openproject-my-page.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,11 +27,11 @@ //++ import { NgModule } from '@angular/core'; -import { Ng2StateDeclaration, UIRouterModule } from "@uirouter/angular"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { OpenprojectGridsModule } from "core-app/shared/components/grids/openproject-grids.module"; -import { MyPageComponent } from "core-app/features/my-page/my-page.component"; +import { Ng2StateDeclaration, UIRouterModule } from '@uirouter/angular'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openproject-grids.module'; +import { MyPageComponent } from 'core-app/features/my-page/my-page.component'; export const MY_PAGE_ROUTES:Ng2StateDeclaration[] = [ { @@ -40,8 +40,8 @@ export const MY_PAGE_ROUTES:Ng2StateDeclaration[] = [ component: MyPageComponent, data: { bodyClasses: ['router--work-packages-my-page', 'widget-grid-layout'], - parent: 'work-packages' - } + parent: 'work-packages', + }, }, ]; @@ -55,9 +55,8 @@ export const MY_PAGE_ROUTES:Ng2StateDeclaration[] = [ UIRouterModule.forChild({ states: MY_PAGE_ROUTES }), ], declarations: [ - MyPageComponent - ] + MyPageComponent, + ], }) export class OpenprojectMyPageModule { } - diff --git a/frontend/src/app/features/overview/openproject-overview.module.ts b/frontend/src/app/features/overview/openproject-overview.module.ts index 56daa83f5f..f1cbdc4693 100644 --- a/frontend/src/app/features/overview/openproject-overview.module.ts +++ b/frontend/src/app/features/overview/openproject-overview.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import { NgModule } from '@angular/core'; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { Ng2StateDeclaration, UIRouter, UIRouterModule } from "@uirouter/angular"; -import { OpenprojectGridsModule } from "core-app/shared/components/grids/openproject-grids.module"; -import { OverviewComponent } from "core-app/features/overview/overview.component"; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { Ng2StateDeclaration, UIRouter, UIRouterModule } from '@uirouter/angular'; +import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openproject-grids.module'; +import { OverviewComponent } from 'core-app/features/overview/overview.component'; const menuItemClass = 'overview-menu-item'; @@ -42,10 +42,10 @@ export const OVERVIEW_ROUTES:Ng2StateDeclaration[] = [ // cf., https://community.openproject.com/wp/29754 url: '/', data: { - menuItem: menuItemClass + menuItem: menuItemClass, }, - component: OverviewComponent - } + component: OverviewComponent, + }, ]; export function uiRouterOverviewConfiguration(uiRouter:UIRouter) { @@ -53,8 +53,8 @@ export function uiRouterOverviewConfiguration(uiRouter:UIRouter) { // cf., https://community.openproject.com/wp/29754 uiRouter.urlService.rules .when( - new RegExp("^/projects(?!/new$)/([^/]+)$"), - match => `/projects/${match[1]}/` + new RegExp('^/projects(?!/new$)/([^/]+)$'), + (match) => `/projects/${match[1]}/`, ); } @@ -66,15 +66,14 @@ export function uiRouterOverviewConfiguration(uiRouter:UIRouter) { UIRouterModule.forChild({ states: OVERVIEW_ROUTES, - config: uiRouterOverviewConfiguration + config: uiRouterOverviewConfiguration, }), ], providers: [ ], declarations: [ - OverviewComponent - ] + OverviewComponent, + ], }) export class OpenprojectOverviewModule { } - diff --git a/frontend/src/app/features/overview/overview.component.ts b/frontend/src/app/features/overview/overview.component.ts index 301d743d6e..93b900c79c 100644 --- a/frontend/src/app/features/overview/overview.component.ts +++ b/frontend/src/app/features/overview/overview.component.ts @@ -1,12 +1,12 @@ import { Component } from '@angular/core'; -import { GridPageComponent } from "core-app/shared/components/grids/grid/page/grid-page.component"; -import { GRID_PROVIDERS } from "core-app/shared/components/grids/grid/grid.component"; +import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component'; +import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component'; @Component({ selector: 'overview', templateUrl: '../../shared/components/grids/grid/page/grid-page.component.html', styleUrls: ['../../shared/components/grids/grid/page/grid-page.component.sass'], - providers: GRID_PROVIDERS + providers: GRID_PROVIDERS, }) export class OverviewComponent extends GridPageComponent { protected i18nNamespace():string { diff --git a/frontend/src/app/features/plugins/hook-service.spec.ts b/frontend/src/app/features/plugins/hook-service.spec.ts index 4de2274bc3..fe246a4198 100644 --- a/frontend/src/app/features/plugins/hook-service.spec.ts +++ b/frontend/src/app/features/plugins/hook-service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,61 +26,62 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ import { HookService } from 'core-app/features/plugins/hook-service'; -describe('HookService', function() { +describe('HookService', () => { let service:HookService = new HookService(); - var callback:any, invalidCallback:any; - var validId = 'myValidCallbacks'; + let callback:any; let + invalidCallback:any; + const validId = 'myValidCallbacks'; beforeEach(() => { service = new HookService(); }); - var shouldBehaveLikeEmptyResult = function(id:string) { - it('returns empty results', function() { + const shouldBehaveLikeEmptyResult = function (id:string) { + it('returns empty results', () => { expect(service.call(id).length).toEqual(0); }); }; - var shouldBehaveLikeResultWithElements = function(id:string, count:number) { - it('returns #count results', function() { + const shouldBehaveLikeResultWithElements = function (id:string, count:number) { + it('returns #count results', () => { expect(service.call(id).length).toEqual(count); }); }; - var shouldBehaveLikeCalledCallback = function(id:string) { - beforeEach(function() { + const shouldBehaveLikeCalledCallback = function (id:string) { + beforeEach(() => { service.call(id); }); - it('is called', function() { + it('is called', () => { expect(callback).toHaveBeenCalled(); }); }; - var shouldBehaveLikeUncalledCallback = function(id:string) { - beforeEach(function() { + const shouldBehaveLikeUncalledCallback = function (id:string) { + beforeEach(() => { service.call(id); }); - it('is not called', function() { + it('is not called', () => { expect(invalidCallback.called).toBeFalsy(); }); }; - describe('register', function() { - var invalidId = 'myInvalidCallbacks'; + describe('register', () => { + const invalidId = 'myInvalidCallbacks'; - describe('no callback registered', function() { + describe('no callback registered', () => { shouldBehaveLikeEmptyResult(invalidId); }); - describe('valid function callback registered', function() { - beforeEach(function() { + describe('valid function callback registered', () => { + beforeEach(() => { callback = jasmine.createSpy('hook'); service.register('myValidCallbacks', callback); }); @@ -91,9 +92,9 @@ describe('HookService', function() { }); }); - describe('call', function() { - describe('function that returns undefined', function() { - beforeEach(function() { + describe('call', () => { + describe('function that returns undefined', () => { + beforeEach(() => { callback = jasmine.createSpy('hook'); service.register('myValidCallbacks', callback); }); @@ -103,8 +104,8 @@ describe('HookService', function() { shouldBehaveLikeEmptyResult(validId); }); - describe('function that returns something that is not undefined', function() { - beforeEach(function() { + describe('function that returns something that is not undefined', () => { + beforeEach(() => { callback = jasmine.createSpy('hook').and.returnValue({}); service.register('myValidCallbacks', callback); @@ -115,8 +116,8 @@ describe('HookService', function() { shouldBehaveLikeResultWithElements(validId, 1); }); - describe('function that returns something that is not undefined', function() { - beforeEach(function() { + describe('function that returns something that is not undefined', () => { + beforeEach(() => { callback = jasmine.createSpy('hook').and.returnValue({}); service.register('myValidCallbacks', callback); @@ -127,8 +128,8 @@ describe('HookService', function() { shouldBehaveLikeResultWithElements(validId, 1); }); - describe('function that returns something that is not undefined', function() { - beforeEach(function() { + describe('function that returns something that is not undefined', () => { + beforeEach(() => { callback = jasmine.createSpy('hook'); invalidCallback = jasmine.createSpy('invalidHook'); @@ -142,10 +143,11 @@ describe('HookService', function() { shouldBehaveLikeUncalledCallback(validId); }); - describe('function that returns something that is not undefined', function() { - var callback1, callback2; + describe('function that returns something that is not undefined', () => { + let callback1; let + callback2; - beforeEach(function() { + beforeEach(() => { callback1 = jasmine.createSpy('hook1').and.returnValue({}); callback2 = jasmine.createSpy('hook1').and.returnValue({}); diff --git a/frontend/src/app/features/plugins/hook-service.ts b/frontend/src/app/features/plugins/hook-service.ts index dee85c9885..6c59f44cf3 100644 --- a/frontend/src/app/features/plugins/hook-service.ts +++ b/frontend/src/app/features/plugins/hook-service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,7 +29,7 @@ import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class HookService { private hooks:{ [hook:string]:Function[] } = {}; diff --git a/frontend/src/app/features/plugins/openproject-plugins.module.ts b/frontend/src/app/features/plugins/openproject-plugins.module.ts index ac5c8707fa..a835fd4ea3 100644 --- a/frontend/src/app/features/plugins/openproject-plugins.module.ts +++ b/frontend/src/app/features/plugins/openproject-plugins.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -24,14 +24,12 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See docs/COPYRIGHT.rdoc for more details. -//++ Ng1FieldControlsWrapper, - - -import { Injector, NgModule } from "@angular/core"; -import { HookService } from "core-app/features/plugins/hook-service"; -import { OpenProjectPluginContext } from "core-app/features/plugins/plugin-context"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +// ++ Ng1FieldControlsWrapper, +import { Injector, NgModule } from '@angular/core'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { OpenProjectPluginContext } from 'core-app/features/plugins/plugin-context'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; @NgModule({ providers: [ @@ -40,10 +38,8 @@ import { debugLog } from "core-app/shared/helpers/debug_output"; }) export class OpenprojectPluginsModule { constructor(injector:Injector) { - debugLog("Registering OpenProject plugin context"); + debugLog('Registering OpenProject plugin context'); const pluginContext = new OpenProjectPluginContext(injector); window.OpenProject.pluginContext.putValue(pluginContext); } } - - diff --git a/frontend/src/app/features/plugins/plugin-context.ts b/frontend/src/app/features/plugins/plugin-context.ts index cc1ba0c8bd..f02aab4f7e 100644 --- a/frontend/src/app/features/plugins/plugin-context.ts +++ b/frontend/src/app/features/plugins/plugin-context.ts @@ -1,39 +1,38 @@ -import { ApplicationRef, Injector, NgZone } from "@angular/core"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { ExternalQueryConfigurationService } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service"; -import { PasswordConfirmationModal } from "../../shared/components/modals/request-for-confirmation/password-confirmation.modal"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { DynamicContentModal } from "../../shared/components/modals/modal-wrapper/dynamic-content.modal"; -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { EditFieldService } from "core-app/shared/components/fields/edit/edit-field.service"; -import { HTMLSanitizeService } from "../../core/html-sanitize/html-sanitize.service"; -import { PathHelperService } from "../../core/path-helper/path-helper.service"; -import { DynamicBootstrapper } from "core-app/core/setup/globals/dynamic-bootstrapper"; +import { ApplicationRef, Injector, NgZone } from '@angular/core'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ExternalQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { EditFieldService } from 'core-app/shared/components/fields/edit/edit-field.service'; +import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; import { States } from 'core-app/core/states/states.service'; -import { CKEditorPreviewService } from "core-app/shared/components/editor/components/ckeditor/ckeditor-preview.service"; -import { ExternalRelationQueryConfigurationService } from "core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { EditorMacrosService } from "core-app/shared/components/modals/editor/editor-macros.service"; -import { ConfirmDialogService } from "core-app/shared/components/modals/confirm-dialog/confirm-dialog.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HookService } from "core-app/features/plugins/hook-service"; +import { CKEditorPreviewService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-preview.service'; +import { ExternalRelationQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { EditorMacrosService } from 'core-app/shared/components/modals/editor/editor-macros.service'; +import { ConfirmDialogService } from 'core-app/shared/components/modals/confirm-dialog/confirm-dialog.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { PathHelperService } from '../../core/path-helper/path-helper.service'; +import { HTMLSanitizeService } from '../../core/html-sanitize/html-sanitize.service'; +import { DynamicContentModalComponent } from '../../shared/components/modals/modal-wrapper/dynamic-content.modal'; +import { PasswordConfirmationModalComponent } from '../../shared/components/modals/request-for-confirmation/password-confirmation.modal'; /** * Plugin context bridge for plugins outside the CLI compiler context * in order to access services and parts of the core application */ export class OpenProjectPluginContext { - private _knownHookNames = [ 'workPackageTableContextMenu', 'workPackageSingleContextMenu', - 'workPackageNewInitialization' + 'workPackageNewInitialization', ]; // Common services referencable by index @@ -55,17 +54,17 @@ export class OpenProjectPluginContext { pathHelperService: this.injector.get(PathHelperService), states: this.injector.get(States), apiV3Service: this.injector.get(APIV3Service), - configurationService: this.injector.get(ConfigurationService) + configurationService: this.injector.get(ConfigurationService), }; // Random collection of classes needed outside of angular public readonly classes = { modals: { - passwordConfirmation: PasswordConfirmationModal, - dynamicContent: DynamicContentModal, + passwordConfirmation: PasswordConfirmationModalComponent, + dynamicContent: DynamicContentModalComponent, }, - HalResource: HalResource, - DisplayField: DisplayField + HalResource, + DisplayField, }; // Hooks @@ -100,7 +99,7 @@ export class OpenProjectPluginContext { public bootstrap(element:HTMLElement) { DynamicBootstrapper.bootstrapOptionalEmbeddable( this.injector.get(ApplicationRef), - element + element, ); } } diff --git a/frontend/src/app/features/projects/components/copy-project/copy-project.component.ts b/frontend/src/app/features/projects/components/copy-project/copy-project.component.ts index a995cef690..a2c906d56c 100644 --- a/frontend/src/app/features/projects/components/copy-project/copy-project.component.ts +++ b/frontend/src/app/features/projects/components/copy-project/copy-project.component.ts @@ -2,35 +2,36 @@ import { IDynamicFieldGroupConfig, IOPFormlyFieldSettings, IOPFormlyTemplateOptions, -} from "core-app/shared/components/dynamic-forms/typings"; -import { JobStatusModal } from "core-app/features/job-status/job-status-modal/job-status.modal"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { Component, OnInit } from "@angular/core"; -import { StateService } from "@uirouter/core"; +} from 'core-app/shared/components/dynamic-forms/typings'; +import { JobStatusModalComponent } from 'core-app/features/job-status/job-status-modal/job-status.modal'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { Component, OnInit } from '@angular/core'; +import { StateService } from '@uirouter/core'; @Component({ selector: 'op-copy-project', - templateUrl: './copy-project.component.html' + templateUrl: './copy-project.component.html', }) export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit { dynamicFieldsSettingsPipe = this.fieldSettingsPipe.bind(this); + fieldGroups:IDynamicFieldGroupConfig[]; formUrl:string; hiddenFields:string[] = [ 'identifier', - 'active' + 'active', ]; text = { - advancedSettingsLabel: this.I18n.t("js.forms.advanced_settings"), - copySettingsLabel: this.I18n.t("js.project.copy.copy_options"), + advancedSettingsLabel: this.I18n.t('js.forms.advanced_settings'), + copySettingsLabel: this.I18n.t('js.project.copy.copy_options'), }; constructor( @@ -59,7 +60,7 @@ export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit } onSubmitted(response:HalSource) { - this.modalService.show(JobStatusModal, 'global', { jobId: response.jobId }); + this.modalService.show(JobStatusModalComponent, 'global', { jobId: response.jobId }); } private isHiddenField(key:string|undefined):boolean { @@ -67,7 +68,7 @@ export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit } private fieldSettingsPipe(dynamicFieldsSettings:IOPFormlyFieldSettings[]):IOPFormlyFieldSettings[] { - return dynamicFieldsSettings.map(field => ({...field, hide: this.isHiddenField(field.key)})) + return dynamicFieldsSettings.map((field) => ({ ...field, hide: this.isHiddenField(field.key) })); } private isPrimaryAttribute(to?:IOPFormlyTemplateOptions):boolean { @@ -75,11 +76,11 @@ export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit return false; } - return (to.required && - !to.hasDefault && - to.payloadValue == null) || - to.property === 'name' || - to.property === 'parent'; + return (to.required + && !to.hasDefault + && to.payloadValue == null) + || to.property === 'name' + || to.property === 'parent'; } private isMeta(property:string|undefined):boolean { diff --git a/frontend/src/app/features/projects/components/new-project/new-project.component.ts b/frontend/src/app/features/projects/components/new-project/new-project.component.ts index 6dd3cd14b5..9f4bb48157 100644 --- a/frontend/src/app/features/projects/components/new-project/new-project.component.ts +++ b/frontend/src/app/features/projects/components/new-project/new-project.component.ts @@ -1,18 +1,18 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import {StateService, UIRouterGlobals} from "@uirouter/core"; -import {PathHelperService} from "core-app/core/path-helper/path-helper.service"; -import {IDynamicFieldGroupConfig, IOPFormlyFieldSettings} from "core-app/shared/components/dynamic-forms/typings"; -import {I18nService} from "core-app/core/i18n/i18n.service"; -import {FormControl, FormGroup} from "@angular/forms"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; -import {map} from "rxjs/operators"; -import {Observable} from "rxjs"; -import {JobStatusModal} from "core-app/features/job-status/job-status-modal/job-status.modal"; -import {OpModalService} from "core-app/shared/components/modal/modal.service"; -import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { StateService, UIRouterGlobals } from '@uirouter/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { IDynamicFieldGroupConfig, IOPFormlyFieldSettings } from 'core-app/shared/components/dynamic-forms/typings'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { FormControl, FormGroup } from '@angular/forms'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { JobStatusModalComponent } from 'core-app/features/job-status/job-status-modal/job-status.modal'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { DynamicFormComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export interface ProjectTemplateOption { href:string|null; @@ -26,37 +26,40 @@ export interface ProjectTemplateOption { }) export class NewProjectComponent extends UntilDestroyedMixin implements OnInit { formUrl:string|null; + resourcePath:string; + dynamicFieldsSettingsPipe = this.fieldSettingsPipe.bind(this); + fieldGroups:IDynamicFieldGroupConfig[]; + initialPayload = {}; text = { use_template: this.I18n.t('js.project.use_template'), no_template_selected: this.I18n.t('js.project.no_template_selected'), - advancedSettingsLabel: this.I18n.t("js.forms.advanced_settings"), + advancedSettingsLabel: this.I18n.t('js.forms.advanced_settings'), }; hiddenFields:string[] = [ 'identifier', 'sendNotifications', - 'active' + 'active', ]; copyableTemplateFilter = new ApiV3FilterBuilder() - .add('user_action', '=', ["projects/copy"]) // no null values + .add('user_action', '=', ['projects/copy']) // no null values .add('templated', '=', true); templateOptions$:Observable = - this - .apiV3Service - .projects - .filtered(this.copyableTemplateFilter) - .get() - .pipe( - map(response => - response.elements.map((el:HalResource) => ({ href: el.href, name: el.name }))), - ); + this + .apiV3Service + .projects + .filtered(this.copyableTemplateFilter) + .get() + .pipe( + map((response) => response.elements.map((el:HalResource) => ({ href: el.href, name: el.name }))), + ); templateForm = new FormGroup({ template: new FormControl(), @@ -83,10 +86,10 @@ export class NewProjectComponent extends UntilDestroyedMixin implements OnInit { this.resourcePath = this.apiV3Service.projects.path; this.fieldGroups = [{ name: this.text.advancedSettingsLabel, - fieldsFilter: (field) => !['name', 'parent'].includes(field.templateOptions?.property!) && - !(field.templateOptions?.required && - !field.templateOptions.hasDefault && - field.templateOptions.payloadValue == null), + fieldsFilter: (field) => !['name', 'parent'].includes(field.templateOptions?.property!) + && !(field.templateOptions?.required + && !field.templateOptions.hasDefault + && field.templateOptions.payloadValue == null), }]; if (this.uIRouterGlobals.params.parent_id) { @@ -96,7 +99,7 @@ export class NewProjectComponent extends UntilDestroyedMixin implements OnInit { onSubmitted(response:HalSource) { if (response._type === 'JobStatus') { - this.modalService.show(JobStatusModal, 'global', { jobId: response.jobId }); + this.modalService.show(JobStatusModalComponent, 'global', { jobId: response.jobId }); } else { window.location.href = this.pathHelperService.projectPath(response.identifier as string); } @@ -106,7 +109,7 @@ export class NewProjectComponent extends UntilDestroyedMixin implements OnInit { this.initialPayload = { ...this.initialPayload, name: this.dynamicForm.model.name, - } + }; this.formUrl = selected?.href ? `${selected.href}/copy` : null; } @@ -124,13 +127,13 @@ export class NewProjectComponent extends UntilDestroyedMixin implements OnInit { this.initialPayload = { _links: { parent: { - href: href - } - } + href, + }, + }, }; } private fieldSettingsPipe(dynamicFieldsSettings:IOPFormlyFieldSettings[]):IOPFormlyFieldSettings[] { - return dynamicFieldsSettings.map(field => ({...field, hide: this.isHiddenField(field.key)})) + return dynamicFieldsSettings.map((field) => ({ ...field, hide: this.isHiddenField(field.key) })); } } diff --git a/frontend/src/app/features/projects/components/projects/projects.component.spec.ts b/frontend/src/app/features/projects/components/projects/projects.component.spec.ts index fdf1cfb821..50c6ac748b 100644 --- a/frontend/src/app/features/projects/components/projects/projects.component.spec.ts +++ b/frontend/src/app/features/projects/components/projects/projects.component.spec.ts @@ -3,14 +3,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProjectsComponent } from './projects.component'; xdescribe('ProjectsComponent', () => { - let component: ProjectsComponent; - let fixture: ComponentFixture; + let component:ProjectsComponent; + let fixture:ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ProjectsComponent ] + declarations: [ProjectsComponent], }) - .compileComponents(); + .compileComponents(); }); beforeEach(() => { diff --git a/frontend/src/app/features/projects/components/projects/projects.component.ts b/frontend/src/app/features/projects/components/projects/projects.component.ts index 5d406118e4..3b8359b8fc 100644 --- a/frontend/src/app/features/projects/components/projects/projects.component.ts +++ b/frontend/src/app/features/projects/components/projects/projects.component.ts @@ -1,20 +1,24 @@ import { Component, OnInit } from '@angular/core'; -import { StateService } from "@uirouter/core"; -import { IOPFormlyFieldSettings } from "core-app/shared/components/dynamic-forms/typings"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { StateService } from '@uirouter/core'; +import { IOPFormlyFieldSettings } from 'core-app/shared/components/dynamic-forms/typings'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Component({ selector: 'app-projects', templateUrl: './projects.component.html', - styleUrls: ['./projects.component.scss'] + styleUrls: ['./projects.component.scss'], }) export class ProjectsComponent extends UntilDestroyedMixin implements OnInit { projectsPath:string; + formMethod = 'patch'; + text:{ [key:string]:string }; + dynamicFieldsSettingsPipe:(dynamicFieldsSettings:IOPFormlyFieldSettings[]) => IOPFormlyFieldSettings[]; + hiddenFields = ['identifier', 'active']; constructor( @@ -27,19 +31,17 @@ export class ProjectsComponent extends UntilDestroyedMixin implements OnInit { ngOnInit():void { this.projectsPath = this._currentProjectService.apiv3Path!; - this.dynamicFieldsSettingsPipe = (dynamicFieldsSettings) => { - return dynamicFieldsSettings - .reduce((formattedDynamicFieldsSettings:IOPFormlyFieldSettings[], dynamicFormField) => { - if (this.isFieldHidden(dynamicFormField.key)) { - dynamicFormField = { - ...dynamicFormField, - hide: true, - } - } - - return [...formattedDynamicFieldsSettings, dynamicFormField]; - }, []); - } + this.dynamicFieldsSettingsPipe = (dynamicFieldsSettings) => dynamicFieldsSettings + .reduce((formattedDynamicFieldsSettings:IOPFormlyFieldSettings[], dynamicFormField) => { + if (this.isFieldHidden(dynamicFormField.key)) { + dynamicFormField = { + ...dynamicFormField, + hide: true, + }; + } + + return [...formattedDynamicFieldsSettings, dynamicFormField]; + }, []); } private isFieldHidden(name:string|undefined) { diff --git a/frontend/src/app/features/projects/openproject-projects.module.ts b/frontend/src/app/features/projects/openproject-projects.module.ts index ac57851880..29114b396b 100644 --- a/frontend/src/app/features/projects/openproject-projects.module.ts +++ b/frontend/src/app/features/projects/openproject-projects.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,16 +27,16 @@ //++ import { NgModule } from '@angular/core'; -import { OpenprojectHalModule } from "core-app/features/hal/openproject-hal.module"; -import { UIRouterModule } from "@uirouter/angular"; +import { OpenprojectHalModule } from 'core-app/features/hal/openproject-hal.module'; +import { UIRouterModule } from '@uirouter/angular'; import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openproject-fields.module'; -import { PROJECTS_ROUTES, uiRouterProjectsConfiguration } from "core-app/features/projects/projects-routes"; +import { PROJECTS_ROUTES, uiRouterProjectsConfiguration } from 'core-app/features/projects/projects-routes'; +import { DynamicFormsModule } from 'core-app/shared/components/dynamic-forms/dynamic-forms.module'; +import { NewProjectComponent } from 'core-app/features/projects/components/new-project/new-project.component'; +import { ReactiveFormsModule } from '@angular/forms'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { CopyProjectComponent } from 'core-app/features/projects/components/copy-project/copy-project.component'; import { ProjectsComponent } from './components/projects/projects.component'; -import { DynamicFormsModule } from "core-app/shared/components/dynamic-forms/dynamic-forms.module"; -import { NewProjectComponent } from "core-app/features/projects/components/new-project/new-project.component"; -import { ReactiveFormsModule } from "@angular/forms"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { CopyProjectComponent } from "core-app/features/projects/components/copy-project/copy-project.component"; @NgModule({ imports: [ @@ -48,7 +48,7 @@ import { CopyProjectComponent } from "core-app/features/projects/components/copy OpenprojectFieldsModule, UIRouterModule.forChild({ states: PROJECTS_ROUTES, - config: uiRouterProjectsConfiguration + config: uiRouterProjectsConfiguration, }), DynamicFormsModule, ], @@ -56,7 +56,7 @@ import { CopyProjectComponent } from "core-app/features/projects/components/copy ProjectsComponent, NewProjectComponent, CopyProjectComponent, - ] + ], }) export class OpenprojectProjectsModule { } diff --git a/frontend/src/app/features/projects/projects-routes.ts b/frontend/src/app/features/projects/projects-routes.ts index 30a1ff97f1..916343c14e 100644 --- a/frontend/src/app/features/projects/projects-routes.ts +++ b/frontend/src/app/features/projects/projects-routes.ts @@ -1,7 +1,7 @@ -import { Ng2StateDeclaration, UIRouter } from "@uirouter/angular"; -import { ProjectsComponent } from "core-app/features/projects/components/projects/projects.component"; -import { NewProjectComponent } from "core-app/features/projects/components/new-project/new-project.component"; -import {CopyProjectComponent} from "core-app/features/projects/components/copy-project/copy-project.component"; +import { Ng2StateDeclaration, UIRouter } from '@uirouter/angular'; +import { ProjectsComponent } from 'core-app/features/projects/components/projects/projects.component'; +import { NewProjectComponent } from 'core-app/features/projects/components/new-project/new-project.component'; +import { CopyProjectComponent } from 'core-app/features/projects/components/copy-project/copy-project.component'; export const PROJECTS_ROUTES:Ng2StateDeclaration[] = [ { @@ -28,7 +28,7 @@ export function uiRouterProjectsConfiguration(uiRouter:UIRouter) { // cf., https://community.openproject.com/wp/29754 uiRouter.urlService.rules .when( - new RegExp("^/projects/(.*)/settings/generic$"), - match => `/projects/${match[1]}/settings/generic/` + new RegExp('^/projects/(.*)/settings/generic$'), + (match) => `/projects/${match[1]}/settings/generic/`, ); -} \ No newline at end of file +} diff --git a/frontend/src/app/features/reporting/openproject-reporting.module.ts b/frontend/src/app/features/reporting/openproject-reporting.module.ts index 612da4637b..c1ceda2462 100644 --- a/frontend/src/app/features/reporting/openproject-reporting.module.ts +++ b/frontend/src/app/features/reporting/openproject-reporting.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,23 +27,22 @@ //++ import { NgModule } from '@angular/core'; -import { UIRouterModule } from "@uirouter/angular"; +import { UIRouterModule } from '@uirouter/angular'; import { REPORTING_ROUTES, -} from "core-app/features/reporting/openproject-reporting.routes"; -import { ReportingPageComponent } from "core-app/features/reporting/reporting-page/reporting-page.component"; +} from 'core-app/features/reporting/openproject-reporting.routes'; +import { ReportingPageComponent } from 'core-app/features/reporting/reporting-page/reporting-page.component'; @NgModule({ imports: [ // Routes for /cost_reports UIRouterModule.forChild({ - states: REPORTING_ROUTES + states: REPORTING_ROUTES, }), ], declarations: [ - ReportingPageComponent - ] + ReportingPageComponent, + ], }) export class OpenprojectReportingModule { } - diff --git a/frontend/src/app/features/reporting/openproject-reporting.routes.ts b/frontend/src/app/features/reporting/openproject-reporting.routes.ts index 5e97ce4342..bea4ea30c5 100644 --- a/frontend/src/app/features/reporting/openproject-reporting.routes.ts +++ b/frontend/src/app/features/reporting/openproject-reporting.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration, UIRouter } from "@uirouter/angular"; -import { ReportingPageComponent } from "core-app/features/reporting/reporting-page/reporting-page.component"; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { ReportingPageComponent } from 'core-app/features/reporting/reporting-page/reporting-page.component'; export const REPORTING_ROUTES:Ng2StateDeclaration[] = [ { name: 'reporting', parent: 'root', url: '/cost_reports', - component: ReportingPageComponent + component: ReportingPageComponent, }, { name: 'reporting.show', url: '/:id', - component: ReportingPageComponent + component: ReportingPageComponent, }, ]; diff --git a/frontend/src/app/features/reporting/reporting-page/functionality/tablesorter.ts b/frontend/src/app/features/reporting/reporting-page/functionality/tablesorter.ts index 51eb2f45c8..3ed64eff6d 100644 --- a/frontend/src/app/features/reporting/reporting-page/functionality/tablesorter.ts +++ b/frontend/src/app/features/reporting/reporting-page/functionality/tablesorter.ts @@ -19,7 +19,7 @@ function initTableSorter() { sortDisabled: I18n.t('js.sort.sorting_disabled'), nextAsc: I18n.t('js.sort.activate_asc'), nextDesc: I18n.t('js.sort.activate_dsc'), - nextNone: I18n.t('js.sort.activate_no') + nextNone: I18n.t('js.sort.activate_no'), }; jQuery('#sortable-table') @@ -28,20 +28,20 @@ function initTableSorter() { sortList: [[0, 0]], widgets: ['saveSort'], widgetOptions: { - storage_storageType: 's' + storage_storageType: 's', }, - textExtraction: function (node:HTMLElement, table:any, cellIndex:any) { + textExtraction(node:HTMLElement) { return node.getAttribute('raw-data'); - } + }, }); } export function registerTableSorter() { - jQuery(document).ready(function () { + jQuery(document).ready(() => { initTableSorter(); }); - jQuery(document).ajaxComplete(function () { + jQuery(document).ajaxComplete(() => { initTableSorter(); }); } diff --git a/frontend/src/app/features/reporting/reporting-page/reporting-page.component.ts b/frontend/src/app/features/reporting/reporting-page/reporting-page.component.ts index 8b5b791b01..33891a4887 100644 --- a/frontend/src/app/features/reporting/reporting-page/reporting-page.component.ts +++ b/frontend/src/app/features/reporting/reporting-page/reporting-page.component.ts @@ -1,7 +1,5 @@ -import { Component, OnInit, ViewEncapsulation } from "@angular/core"; -import { registerTableSorter } from "core-app/features/reporting/reporting-page/functionality/tablesorter"; - -export const reportingPageComponentSelector = 'op-reporting-page'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { registerTableSorter } from 'core-app/features/reporting/reporting-page/functionality/tablesorter'; import './functionality/reporting_engine'; import './functionality/reporting_engine/filters'; @@ -9,6 +7,7 @@ import './functionality/reporting_engine/group_bys'; import './functionality/reporting_engine/restore_query'; import './functionality/reporting_engine/controls'; +export const reportingPageComponentSelector = 'op-reporting-page'; @Component({ selector: reportingPageComponentSelector, @@ -17,8 +16,8 @@ import './functionality/reporting_engine/controls'; encapsulation: ViewEncapsulation.None, template: '', styleUrls: [ - './styles/reporting.sass' - ] + './styles/reporting.sass', + ], }) export class ReportingPageComponent implements OnInit { ngOnInit() { @@ -27,4 +26,4 @@ export class ReportingPageComponent implements OnInit { // Register table sorting functionality after reporting engine loaded registerTableSorter(); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts index f3144935e8..6baa16f6af 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts @@ -1,10 +1,12 @@ -import { EventEmitter, Component, OnInit, ChangeDetectionStrategy, Output, Input } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Observable, of } from "rxjs"; -import { map, tap } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { HalSourceLink } from "core-app/features/hal/resources/hal-resource"; +import { + EventEmitter, Component, ChangeDetectionStrategy, Output, Input, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { HalSourceLink } from 'core-app/features/hal/resources/hal-resource'; export interface NotificationSettingProjectOption { name:string; @@ -14,10 +16,11 @@ export interface NotificationSettingProjectOption { @Component({ selector: 'op-notification-setting-inline-create', templateUrl: './notification-setting-inline-create.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NotificationSettingInlineCreateComponent { @Input() userId:string; + @Output() onSelect = new EventEmitter(); /** Active inline-create mode */ @@ -29,9 +32,9 @@ export class NotificationSettingInlineCreateComponent { }; public autocompleterOptions = { - filters:[], - resource:'default', - getOptionsFn: (query:string):Observable => this.autocomplete(query) + filters: [], + resource: 'default', + getOptionsFn: (query:string):Observable => this.autocomplete(query), }; constructor( @@ -40,7 +43,6 @@ export class NotificationSettingInlineCreateComponent { ) { } - selectProject($event:NotificationSettingProjectOption) { this.onSelect.emit({ title: $event.name, href: $event.href }); this.active = false; @@ -61,9 +63,7 @@ export class NotificationSettingInlineCreateComponent { .filtered(filters) .get() .pipe( - map((collection) => { - return collection.elements.map(project => ({ href: project.href!, name: project.name })); - }) + map((collection) => collection.elements.map((project) => ({ href: project.href!, name: project.name }))), ); } } diff --git a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts index 0495bdc287..61121fae3a 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts @@ -1,11 +1,13 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { take } from "rxjs/internal/operators/take"; -import { UIRouterGlobals } from "@uirouter/core"; -import { UserPreferencesService } from "core-app/features/user-preferences/state/user-preferences.service"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; -import { UserPreferencesQuery } from "core-app/features/user-preferences/state/user-preferences.query"; +import { + ChangeDetectionStrategy, Component, Input, OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { take } from 'rxjs/internal/operators/take'; +import { UIRouterGlobals } from '@uirouter/core'; +import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; +import { UserPreferencesQuery } from 'core-app/features/user-preferences/state/user-preferences.query'; export const myNotificationsPageComponentSelector = 'op-notifications-page'; @@ -25,7 +27,7 @@ export class NotificationsSettingsPageComponent implements OnInit { inApp: this.I18n.t('js.notifications.in_app'), default_all_projects: this.I18n.t('js.notifications.settings.default_all_projects'), advanced_settings: this.I18n.t('js.forms.advanced_settings'), - self_notify: this.I18n.t('js.notifications.settings.self_notify') + self_notify: this.I18n.t('js.notifications.settings.self_notify'), }; constructor( @@ -34,7 +36,7 @@ export class NotificationsSettingsPageComponent implements OnInit { private store:UserPreferencesStore, private query:UserPreferencesQuery, private currentUserService:CurrentUserService, - private uiRouterGlobals:UIRouterGlobals + private uiRouterGlobals:UIRouterGlobals, ) { } @@ -44,7 +46,7 @@ export class NotificationsSettingsPageComponent implements OnInit { .currentUserService .user$ .pipe(take(1)) - .subscribe(user => { + .subscribe((user) => { this.userId = this.userId || user.id!; this.stateService.get(this.userId); }); diff --git a/frontend/src/app/features/user-preferences/notifications-settings/row/notification-setting-row.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/row/notification-setting-row.component.ts index 068d8ffd72..83db378c93 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/row/notification-setting-row.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/row/notification-setting-row.component.ts @@ -1,16 +1,19 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { arrayUpdate } from "@datorama/akita"; -import { NotificationSetting } from "core-app/features/user-preferences/state/notification-setting.model"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; +import { + ChangeDetectionStrategy, Component, Input, OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { arrayUpdate } from '@datorama/akita'; +import { NotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; @Component({ selector: '[op-notification-setting-row]', templateUrl: './notification-setting-row.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NotificationSettingRowComponent implements OnInit { @Input() first = false; + @Input() setting:NotificationSetting; /** Whether this setting is global */ @@ -28,7 +31,7 @@ export class NotificationSettingRowComponent implements OnInit { any_event_header: 'All events', default_all_projects: 'Default for all projects', add_setting: 'Add settings for project', - channel: (channel:string):string => this.I18n.t('js.notifications.channels.' + channel) + channel: (channel:string):string => this.I18n.t(`js.notifications.channels.${channel}`), }; constructor( @@ -44,9 +47,8 @@ export class NotificationSettingRowComponent implements OnInit { remove():void { this.store.update( ({ notifications }) => ({ - notifications: notifications.filter(notification => - notification._links.project.href !== this.setting._links.project.href) - }) + notifications: notifications.filter((notification) => notification._links.project.href !== this.setting._links.project.href), + }), ); } @@ -54,14 +56,14 @@ export class NotificationSettingRowComponent implements OnInit { this.store.update( ({ notifications }) => ({ notifications: arrayUpdate( - notifications, this.matcherFn.bind(this), delta - ) - }) + notifications, this.matcherFn.bind(this), delta, + ), + }), ); } private matcherFn(notification:NotificationSetting) { - return notification._links.project.href === this.setting._links.project.href && - notification.channel === this.setting.channel; + return notification._links.project.href === this.setting._links.project.href + && notification.channel === this.setting.channel; } } diff --git a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts index 4935cb598a..c743875962 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts @@ -1,23 +1,27 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; -import { KeyValue } from "@angular/common"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UserPreferencesService } from "core-app/features/user-preferences/state/user-preferences.service"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; -import { UserPreferencesQuery } from "core-app/features/user-preferences/state/user-preferences.query"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { UIRouterGlobals } from "@uirouter/core"; -import { HalSourceLink } from "core-app/features/hal/resources/hal-resource"; +// noinspection ES6UnusedImports + +import { + Component, OnInit, ChangeDetectionStrategy, Input, +} from '@angular/core'; +import { KeyValue } from '@angular/common'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; +import { UserPreferencesQuery } from 'core-app/features/user-preferences/state/user-preferences.query'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { UIRouterGlobals } from '@uirouter/core'; +import { HalSourceLink } from 'core-app/features/hal/resources/hal-resource'; import { buildNotificationSetting, - NotificationSetting -} from "core-app/features/user-preferences/state/notification-setting.model"; -import { arrayAdd } from "@datorama/akita"; + NotificationSetting, +} from 'core-app/features/user-preferences/state/notification-setting.model'; +import { arrayAdd } from '@datorama/akita'; @Component({ selector: 'op-notification-settings-table', templateUrl: './notification-settings-table.component.html', styleUrls: ['./notification-settings-table.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NotificationSettingsTableComponent { @Input() userId:string; @@ -52,7 +56,7 @@ export class NotificationSettingsTableComponent { private I18n:I18nService, private stateService:UserPreferencesService, private store:UserPreferencesStore, - private query:UserPreferencesQuery + private query:UserPreferencesQuery, ) { } @@ -64,8 +68,8 @@ export class NotificationSettingsTableComponent { this.store.update( ({ notifications }) => ({ - notifications: arrayAdd(notifications, added) - }) + notifications: arrayAdd(notifications, added), + }), ); } } diff --git a/frontend/src/app/features/user-preferences/notifications-settings/toolbar/notifications-settings-toolbar.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/toolbar/notifications-settings-toolbar.component.ts index 5c395335ad..127d875cdd 100644 --- a/frontend/src/app/features/user-preferences/notifications-settings/toolbar/notifications-settings-toolbar.component.ts +++ b/frontend/src/app/features/user-preferences/notifications-settings/toolbar/notifications-settings-toolbar.component.ts @@ -1,12 +1,12 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { UserPreferencesQuery } from "core-app/features/user-preferences/state/user-preferences.query"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; +import { UserPreferencesQuery } from 'core-app/features/user-preferences/state/user-preferences.query'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; @Component({ selector: 'op-notifications-settings-toolbar', templateUrl: './notifications-settings-toolbar.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NotificationsSettingsToolbarComponent { projectSettings$ = this.query.projectNotifications$; @@ -26,8 +26,8 @@ export class NotificationsSettingsToolbarComponent { removeAll():void { this.store.update( ({ notifications }) => ({ - notifications: notifications.filter(notification => notification._links.project.href === null) - }) + notifications: notifications.filter((notification) => notification._links.project.href === null), + }), ); } } diff --git a/frontend/src/app/features/user-preferences/state/notification-setting.model.ts b/frontend/src/app/features/user-preferences/state/notification-setting.model.ts index 2f1d3a25cc..fc5be21c58 100644 --- a/frontend/src/app/features/user-preferences/state/notification-setting.model.ts +++ b/frontend/src/app/features/user-preferences/state/notification-setting.model.ts @@ -1,4 +1,4 @@ -import { HalSourceLink } from "core-app/features/hal/resources/hal-resource"; +import { HalSourceLink } from 'core-app/features/hal/resources/hal-resource'; export type NotificationSettingChannel = 'mail'|'in_app'; @@ -16,14 +16,14 @@ export function buildNotificationSetting(project:null|HalSourceLink, params:Part _links: { project: { href: project ? project.href : null, - title: project?.title - } + title: project?.title, + }, }, involved: true, mentioned: true, watched: false, all: false, - channel: "in_app", - ...params + channel: 'in_app', + ...params, }; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/user-preferences/state/user-preferences.model.ts b/frontend/src/app/features/user-preferences/state/user-preferences.model.ts index 2aefb0fe91..3921c9501f 100644 --- a/frontend/src/app/features/user-preferences/state/user-preferences.model.ts +++ b/frontend/src/app/features/user-preferences/state/user-preferences.model.ts @@ -1,4 +1,4 @@ -import { NotificationSetting } from "core-app/features/user-preferences/state/notification-setting.model"; +import { NotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; export interface UserPreferencesModel { autoHidePopups:boolean; @@ -8,4 +8,4 @@ export interface UserPreferencesModel { warnOnLeavingUnsaved:boolean; selfNotified:boolean; notifications:NotificationSetting[]; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/user-preferences/state/user-preferences.query.ts b/frontend/src/app/features/user-preferences/state/user-preferences.query.ts index 28bf0d145e..6c04e8f96b 100644 --- a/frontend/src/app/features/user-preferences/state/user-preferences.query.ts +++ b/frontend/src/app/features/user-preferences/state/user-preferences.query.ts @@ -1,11 +1,11 @@ import { Injectable } from '@angular/core'; -import { Query } from "@datorama/akita"; -import { map } from "rxjs/operators"; -import { Observable } from "rxjs"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; -import { UserPreferencesModel } from "core-app/features/user-preferences/state/user-preferences.model"; -import { NotificationSetting } from "core-app/features/user-preferences/state/notification-setting.model"; +import { Query } from '@datorama/akita'; +import { map } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; +import { UserPreferencesModel } from 'core-app/features/user-preferences/state/user-preferences.model'; +import { NotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; @Injectable() export class UserPreferencesQuery extends Query { @@ -16,13 +16,13 @@ export class UserPreferencesQuery extends Query { notificationsGroupedByProject$:Observable<{ [key:string]:NotificationSetting[] }> = this .notificationSettings$ .pipe( - map(notifications => _.groupBy(notifications, setting => setting._links.project.title || 'global')) + map((notifications) => _.groupBy(notifications, (setting) => setting._links.project.title || 'global')), ); projectNotifications$ = this .notificationSettings$ .pipe( - map(settings => settings.filter(notification => notification._links.project.href !== null)) + map((settings) => settings.filter((notification) => notification._links.project.href !== null)), ); preferences$ = this.select(); diff --git a/frontend/src/app/features/user-preferences/state/user-preferences.service.ts b/frontend/src/app/features/user-preferences/state/user-preferences.service.ts index 627a56413b..f184944f5c 100644 --- a/frontend/src/app/features/user-preferences/state/user-preferences.service.ts +++ b/frontend/src/app/features/user-preferences/state/user-preferences.service.ts @@ -1,15 +1,14 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { Apiv3UserPreferencesPaths } from "core-app/core/apiv3/endpoints/users/apiv3-user-preferences-paths"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UserPreferencesModel } from "core-app/features/user-preferences/state/user-preferences.model"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { Apiv3UserPreferencesPaths } from 'core-app/core/apiv3/endpoints/users/apiv3-user-preferences-paths'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UserPreferencesModel } from 'core-app/features/user-preferences/state/user-preferences.model'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; @Injectable({ providedIn: 'root' }) export class UserPreferencesService { - constructor( private store:UserPreferencesStore, private http:HttpClient, @@ -24,11 +23,11 @@ export class UserPreferencesService { this.preferenceAPI(user) .get() .subscribe( - prefs => this.store.update(prefs), - error => this.notifications.addError(error) + (prefs) => this.store.update(prefs), + (error) => this.notifications.addError(error), ) .add( - () => this.store.setLoading(false) + () => this.store.setLoading(false), ); } @@ -38,11 +37,11 @@ export class UserPreferencesService { .preferenceAPI(user) .patch(delta) .subscribe( - prefs => { + (prefs) => { this.store.update(prefs); this.notifications.addSuccess(this.I18n.t('js.notice_successful_update')); }, - error => this.notifications.addError(error), + (error) => this.notifications.addError(error), ) .add(() => this.store.setLoading(false)); } diff --git a/frontend/src/app/features/user-preferences/state/user-preferences.store.ts b/frontend/src/app/features/user-preferences/state/user-preferences.store.ts index 96e5009f50..46f1806a72 100644 --- a/frontend/src/app/features/user-preferences/state/user-preferences.store.ts +++ b/frontend/src/app/features/user-preferences/state/user-preferences.store.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; +import { Injectable } from '@angular/core'; import { Store, StoreConfig } from '@datorama/akita'; -import { UserPreferencesModel } from "core-app/features/user-preferences/state/user-preferences.model"; +import { UserPreferencesModel } from 'core-app/features/user-preferences/state/user-preferences.model'; function createInitialState():UserPreferencesModel { return { @@ -38,7 +38,7 @@ function createInitialState():UserPreferencesModel { timeZone: null, warnOnLeavingUnsaved: true, selfNotified: false, - notifications: [] + notifications: [], }; } diff --git a/frontend/src/app/features/user-preferences/user-preferences.lazy-routes.ts b/frontend/src/app/features/user-preferences/user-preferences.lazy-routes.ts index 809a83191c..a6c29f3808 100644 --- a/frontend/src/app/features/user-preferences/user-preferences.lazy-routes.ts +++ b/frontend/src/app/features/user-preferences/user-preferences.lazy-routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration } from "@uirouter/angular"; +import { Ng2StateDeclaration } from '@uirouter/angular'; export const MY_ACCOUNT_LAZY_ROUTES:Ng2StateDeclaration[] = [ { name: 'my_notifications.**', parent: 'root', url: '/my/notifications', - loadChildren: () => import('./user-preferences.module').then(m => m.OpenProjectMyAccountModule) + loadChildren: () => import('./user-preferences.module').then((m) => m.OpenProjectMyAccountModule), }, { name: 'user_notifications.**', parent: 'root', url: '/users/:userId/edit/notifications', - loadChildren: () => import('./user-preferences.module').then(m => m.OpenProjectMyAccountModule) + loadChildren: () => import('./user-preferences.module').then((m) => m.OpenProjectMyAccountModule), }, ]; diff --git a/frontend/src/app/features/user-preferences/user-preferences.module.ts b/frontend/src/app/features/user-preferences/user-preferences.module.ts index b1fdedce62..3b31d787a9 100644 --- a/frontend/src/app/features/user-preferences/user-preferences.module.ts +++ b/frontend/src/app/features/user-preferences/user-preferences.module.ts @@ -1,16 +1,16 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { UIRouterModule } from "@uirouter/angular"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; -import { UserPreferencesStore } from "core-app/features/user-preferences/state/user-preferences.store"; -import { UserPreferencesQuery } from "core-app/features/user-preferences/state/user-preferences.query"; -import { UserPreferencesService } from "core-app/features/user-preferences/state/user-preferences.service"; -import { NotificationsSettingsPageComponent } from "core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component"; -import { NotificationSettingRowComponent } from "core-app/features/user-preferences/notifications-settings/row/notification-setting-row.component"; -import { NotificationSettingInlineCreateComponent } from "core-app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component"; -import { MY_ACCOUNT_ROUTES } from "core-app/features/user-preferences/user-preferences.routes"; +import { UIRouterModule } from '@uirouter/angular'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; +import { UserPreferencesStore } from 'core-app/features/user-preferences/state/user-preferences.store'; +import { UserPreferencesQuery } from 'core-app/features/user-preferences/state/user-preferences.query'; +import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; +import { NotificationsSettingsPageComponent } from 'core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component'; +import { NotificationSettingRowComponent } from 'core-app/features/user-preferences/notifications-settings/row/notification-setting-row.component'; +import { NotificationSettingInlineCreateComponent } from 'core-app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component'; +import { MY_ACCOUNT_ROUTES } from 'core-app/features/user-preferences/user-preferences.routes'; import { NotificationsSettingsToolbarComponent } from './notifications-settings/toolbar/notifications-settings-toolbar.component'; import { NotificationSettingsTableComponent } from './notifications-settings/table/notification-settings-table.component'; @@ -35,8 +35,8 @@ import { NotificationSettingsTableComponent } from './notifications-settings/tab ReactiveFormsModule, // Routes for /my/* UIRouterModule.forChild({ - states: MY_ACCOUNT_ROUTES + states: MY_ACCOUNT_ROUTES, }), - ] + ], }) export class OpenProjectMyAccountModule { } diff --git a/frontend/src/app/features/user-preferences/user-preferences.routes.ts b/frontend/src/app/features/user-preferences/user-preferences.routes.ts index 07f8e54282..eea5608257 100644 --- a/frontend/src/app/features/user-preferences/user-preferences.routes.ts +++ b/frontend/src/app/features/user-preferences/user-preferences.routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Ng2StateDeclaration } from "@uirouter/angular"; -import { NotificationsSettingsPageComponent } from "core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component"; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { NotificationsSettingsPageComponent } from 'core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component'; export const MY_ACCOUNT_ROUTES:Ng2StateDeclaration[] = [ { name: 'my_notifications', parent: 'root', url: '/my/notifications', - component: NotificationsSettingsPageComponent + component: NotificationsSettingsPageComponent, }, { name: 'user_notifications', parent: 'root', url: '/users/:userId/edit/notifications', - component: NotificationsSettingsPageComponent + component: NotificationsSettingsPageComponent, }, ]; diff --git a/frontend/src/app/features/work-packages/components/back-routing/back-button.component.ts b/frontend/src/app/features/work-packages/components/back-routing/back-button.component.ts index c27c768f2c..0ea3fad64d 100644 --- a/frontend/src/app/features/work-packages/components/back-routing/back-button.component.ts +++ b/frontend/src/app/features/work-packages/components/back-routing/back-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,8 @@ //++ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { BackRoutingService } from "core-app/features/work-packages/components/back-routing/back-routing.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { BackRoutingService } from 'core-app/features/work-packages/components/back-routing/back-routing.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ templateUrl: './back-button.component.html', @@ -38,14 +38,15 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; }) export class BackButtonComponent { @Input() public linkClass:string; + @Input() public customBackMethod:() => unknown; public text = { - goBack: this.I18n.t('js.button_back') + goBack: this.I18n.t('js.button_back'), }; constructor(readonly backRoutingService:BackRoutingService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { } public goBack():void { @@ -55,4 +56,4 @@ export class BackButtonComponent { this.backRoutingService.goBack(); } } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/back-routing/back-routing.service.ts b/frontend/src/app/features/work-packages/components/back-routing/back-routing.service.ts index c2bafce755..48f795599e 100644 --- a/frontend/src/app/features/work-packages/components/back-routing/back-routing.service.ts +++ b/frontend/src/app/features/work-packages/components/back-routing/back-routing.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,9 +27,9 @@ //++ import { Injectable, Injector } from '@angular/core'; -import { StateService, Transition } from "@uirouter/core"; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { StateService, Transition } from '@uirouter/core'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; interface BackRouteOptions { name:string; @@ -41,6 +41,7 @@ interface BackRouteOptions { @Injectable({ providedIn: 'root' }) export class BackRoutingService { @InjectField() private $state:StateService; + @InjectField() private keepTab:KeepTabService; private _backRoute:BackRouteOptions; @@ -56,7 +57,7 @@ export class BackRoutingService { if (preferListOverSplit) { this.goToOtherState(baseRoute, this.backRoute.params); } else { - const state = baseRoute + '.details.tabs'; + const state = `${baseRoute}.details.tabs`; const params = { ...this.backRoute.params, tabIdentifier: this.keepTab.currentDetailsTab }; this.goToOtherState(state, params); } @@ -87,12 +88,10 @@ export class BackRoutingService { // if we are in the first state if (!this.backRoute && baseRoute.includes('show')) { this.$state.reload(); + } else if (!this.backRoute || this.backRoute.name.includes('new')) { + this.$state.go(baseRoute, this.$state.params); } else { - if (!this.backRoute || this.backRoute.name.includes('new')) { - this.$state.go(baseRoute, this.$state.params); - } else { - this.goBackToPreviousState(preferListOverSplit, baseRoute); - } + this.goBackToPreviousState(preferListOverSplit, baseRoute); } } @@ -106,15 +105,17 @@ export class BackRoutingService { const toState = transition.to(); // Set backRoute to know where we came from - if (fromState.name && - fromState.data && - toState.data && - fromState.data.parent !== toState.data.parent) { + if (fromState.name + && fromState.data + && toState.data + && fromState.data.parent !== toState.data.parent) { const paramsFromCopy = { ...transition.params('from') }; - this.backRoute = { name: fromState.name, + this.backRoute = { + name: fromState.name, params: paramsFromCopy, parent: fromState.data.parent, - baseRoute: fromState.data.baseRoute }; + baseRoute: fromState.data.baseRoute, + }; } } diff --git a/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts b/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts index 23c86b0b0e..0a3fe61e85 100644 --- a/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts +++ b/frontend/src/app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @Component({ templateUrl: './wp-edit-actions-bar.html', @@ -37,17 +39,19 @@ import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-f }) export class WorkPackageEditActionsBarComponent { @Output('onSave') public onSave = new EventEmitter(); + @Output('onCancel') public onCancel = new EventEmitter(); + public _saving = false; public text = { save: this.I18n.t('js.button_save'), - cancel: this.I18n.t('js.button_cancel') + cancel: this.I18n.t('js.button_cancel'), }; constructor(private I18n:I18nService, - private editForm:EditFormComponent, - private cdRef:ChangeDetectorRef) { + private editForm:EditFormComponent, + private cdRef:ChangeDetectorRef) { } public set saving(active:boolean) { diff --git a/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts b/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts index 75752bee3f..5e9a74ec24 100644 --- a/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts +++ b/frontend/src/app/features/work-packages/components/filters/abstract-filter-date-time-value/abstract-filter-date-time-value.controller.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,23 +27,23 @@ //++ import { Moment } from 'moment'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OnInit, Directive } from '@angular/core'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Directive() export abstract class AbstractDateTimeValueController extends UntilDestroyedMixin implements OnInit { public filter:QueryFilterInstanceResource; constructor(protected I18n:I18nService, - protected timezoneService:TimezoneService) { + protected timezoneService:TimezoneService) { super(); } ngOnInit() { - _.remove(this.filter.values as string[], value => !this.timezoneService.isValidISODateTime(value)); + _.remove(this.filter.values as string[], (value) => !this.timezoneService.isValidISODateTime(value)); } public abstract get lowerBoundary():Moment|null; @@ -54,7 +54,7 @@ export abstract class AbstractDateTimeValueController extends UntilDestroyedMixi if (!this.timezoneService.isValidISODate(data)) { return ''; } - var d = this.timezoneService.parseISODatetime(data); + const d = this.timezoneService.parseISODatetime(data); return this.timezoneService.formattedISODateTime(d); } @@ -62,7 +62,7 @@ export abstract class AbstractDateTimeValueController extends UntilDestroyedMixi if (!this.timezoneService.isValidISODateTime(data)) { return ''; } - var d = this.timezoneService.parseISODatetime(data); + const d = this.timezoneService.parseISODatetime(data); return this.timezoneService.formattedISODate(d); } @@ -71,9 +71,8 @@ export abstract class AbstractDateTimeValueController extends UntilDestroyedMixi if (!value) { return false; - } else { - return value.hours() !== 0 || value.minutes() !== 0; } + return value.hours() !== 0 || value.minutes() !== 0; } public get timeZoneText() { @@ -81,16 +80,14 @@ export abstract class AbstractDateTimeValueController extends UntilDestroyedMixi return this.I18n.t('js.filter.time_zone_converted.two_values', { from: this.lowerBoundary.format('YYYY-MM-DD HH:mm'), - to: this.upperBoundary.format('YYYY-MM-DD HH:mm') + to: this.upperBoundary.format('YYYY-MM-DD HH:mm'), }); - } else if (this.upperBoundary) { + } if (this.upperBoundary) { return this.I18n.t('js.filter.time_zone_converted.only_end', { to: this.upperBoundary.format('YYYY-MM-DD HH:mm') }); - - } else if (this.lowerBoundary) { + } if (this.lowerBoundary) { return this.I18n.t('js.filter.time_zone_converted.only_start', { from: this.lowerBoundary.format('YYYY-MM-DD HH:mm') }); - } return ''; diff --git a/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts index 3f71a76993..e18a31e75e 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, EventEmitter, Input, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Component({ selector: 'filter-boolean-value', - templateUrl: './filter-boolean-value.component.html' + templateUrl: './filter-boolean-value.component.html', }) export class FilterBooleanValueComponent { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new EventEmitter(); constructor(readonly I18n:I18nService) { @@ -51,5 +55,4 @@ export class FilterBooleanValueComponent { this.filter.values[0] = val; this.filterChanged.emit(this.filter); } - } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts b/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts index 1162e17069..c787f1e06f 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-container/filter-container.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,33 +34,37 @@ import { OnDestroy, OnInit, Output, - ViewEncapsulation + } from '@angular/core'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; -import { DebouncedEventEmitter } from "core-app/shared/helpers/rxjs/debounced-event-emitter"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { Observable } from "rxjs"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; +import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { Observable } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; @Component({ templateUrl: './filter-container.directive.html', changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'filter-container' + selector: 'filter-container', }) export class WorkPackageFilterContainerComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { @Input('showFilterButton') showFilterButton = false; + @Input('filterButtonText') filterButtonText:string = I18n.t('js.button_filter'); + @Output() public filtersChanged = new DebouncedEventEmitter(componentDestroyed(this)); public visible$:Observable; + public filters = this.wpTableFilters.current; + public loaded = false; constructor(readonly wpTableFilters:WorkPackageViewFiltersService, - readonly cdRef:ChangeDetectorRef, - readonly wpFiltersService:WorkPackageFiltersService) { + readonly cdRef:ChangeDetectorRef, + readonly wpFiltersService:WorkPackageFiltersService) { super(); this.visible$ = this.wpFiltersService.observeUntil(componentDestroyed(this)); } @@ -69,7 +73,7 @@ export class WorkPackageFilterContainerComponent extends UntilDestroyedMixin imp this.wpTableFilters .pristine$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.filters = this.wpTableFilters.current; @@ -79,7 +83,7 @@ export class WorkPackageFilterContainerComponent extends UntilDestroyedMixin imp } public replaceIfComplete(filters:QueryFilterInstanceResource[]) { - const available = filters.filter(el => this.wpTableFilters.isAvailable(el)); + const available = filters.filter((el) => this.wpTableFilters.isAvailable(el)); this.wpTableFilters.replaceIfComplete(available); this.filtersChanged.emit(available); } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts index a7666967a7..9ff5bcc3da 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,27 +26,31 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input, OnInit, Output } from '@angular/core'; +import { + Component, Input, OnInit, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { Moment } from 'moment'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { AbstractDateTimeValueController } from '../abstract-filter-date-time-value/abstract-filter-date-time-value.controller'; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; @Component({ selector: 'filter-date-time-value', - templateUrl: './filter-date-time-value.component.html' + templateUrl: './filter-date-time-value.component.html', }) export class FilterDateTimeValueComponent extends AbstractDateTimeValueController implements OnInit { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); constructor(readonly I18n:I18nService, - readonly timezoneService:TimezoneService) { + readonly timezoneService:TimezoneService) { super(I18n, timezoneService); } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts index 6dfbfee421..3e742ada56 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,31 +26,35 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { Moment } from 'moment'; -import { AbstractDateTimeValueController } from '../abstract-filter-date-time-value/abstract-filter-date-time-value.controller'; -import { Component, Input, OnInit, Output } from '@angular/core'; +import { + Component, Input, OnInit, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { AbstractDateTimeValueController } from '../abstract-filter-date-time-value/abstract-filter-date-time-value.controller'; @Component({ selector: 'filter-date-times-value', - templateUrl: './filter-date-times-value.component.html' + templateUrl: './filter-date-times-value.component.html', }) export class FilterDateTimesValueComponent extends AbstractDateTimeValueController implements OnInit { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); readonly text = { - spacer: this.I18n.t('js.filter.value_spacer') + spacer: this.I18n.t('js.filter.value_spacer'), }; constructor(readonly I18n:I18nService, - readonly timezoneService:TimezoneService) { + readonly timezoneService:TimezoneService) { super(I18n, timezoneService); } @@ -75,16 +79,14 @@ export class FilterDateTimesValueComponent extends AbstractDateTimeValueControll public get lowerBoundary():Moment|null { if (this.begin && this.timezoneService.isValidISODateTime(this.begin.toString())) { return this.timezoneService.parseDatetime(this.begin.toString()); - } else { - return null; } + return null; } public get upperBoundary():Moment|null { if (this.end && this.timezoneService.isValidISODateTime(this.end.toString())) { return this.timezoneService.parseDatetime(this.end.toString()); - } else { - return null; } + return null; } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts index 2e33910801..9c47ee6781 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,24 +28,26 @@ import { Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Component({ selector: 'filter-date-value', - templateUrl: './filter-date-value.component.html' + templateUrl: './filter-date-value.component.html', }) export class FilterDateValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); constructor(readonly timezoneService:TimezoneService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { super(); } @@ -61,17 +63,15 @@ export class FilterDateValueComponent extends UntilDestroyedMixin { public parser(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { return data; - } else { - return null; } + return null; } public formatter(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { - var d = this.timezoneService.parseDate(data); + const d = this.timezoneService.parseDate(data); return this.timezoneService.formattedISODate(d); - } else { - return null; } + return null; } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts index a23bf5169d..d5580417a7 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,31 +26,33 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import * as moment from 'moment'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Component({ selector: 'filter-dates-value', - templateUrl: './filter-dates-value.component.html' + templateUrl: './filter-dates-value.component.html', }) export class FilterDatesValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); readonly text = { - spacer: this.I18n.t('js.filter.value_spacer') + spacer: this.I18n.t('js.filter.value_spacer'), }; constructor(readonly timezoneService:TimezoneService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { super(); } @@ -75,17 +77,15 @@ export class FilterDatesValueComponent extends UntilDestroyedMixin { public parser(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { return data; - } else { - return null; } + return null; } public formatter(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { - var d = this.timezoneService.parseDate(data); + const d = this.timezoneService.parseDate(data); return this.timezoneService.formattedISODate(d); - } else { - return null; } + return null; } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts index 56e437ed1e..79945aef15 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,27 +26,28 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - import { Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; @Component({ selector: 'filter-integer-value', - templateUrl: './filter-integer-value.component.html' + templateUrl: './filter-integer-value.component.html', }) export class FilterIntegerValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); constructor(readonly I18n:I18nService, - readonly schemaCache:SchemaCacheService) { + readonly schemaCache:SchemaCacheService) { super(); } @@ -66,13 +67,13 @@ export class FilterIntegerValueComponent extends UntilDestroyedMixin { public get unit() { switch ((this.schema.filter.allowedValues as QueryFilterResource[])[0].id) { - case 'startDate': - case 'dueDate': - case 'updatedAt': - case 'createdAt': - return this.I18n.t('js.work_packages.time_relative.days'); - default: - return ''; + case 'startDate': + case 'dueDate': + case 'updatedAt': + case 'createdAt': + return this.I18n.t('js.work_packages.time_relative.days'); + default: + return ''; } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts index 5c26eb758e..0b0cb8ece0 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component.ts @@ -8,37 +8,46 @@ import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter import { map } from 'rxjs/operators'; import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { CachableAPIV3Resource } from "core-app/core/apiv3/cache/cachable-apiv3-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { HalResourceSortingService } from "core-app/features/hal/services/hal-resource-sorting.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { AfterViewInit, ChangeDetectionStrategy, +import { CachableAPIV3Resource } from 'core-app/core/apiv3/cache/cachable-apiv3-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { HalResourceSortingService } from 'core-app/features/hal/services/hal-resource-sorting.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, - Component, EventEmitter, Input, NgZone, OnInit, Output, ViewChild } from '@angular/core'; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { I18nService } from "core-app/core/i18n/i18n.service"; + Component, EventEmitter, Input, NgZone, OnInit, Output, ViewChild, +} from '@angular/core'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ selector: 'filter-searchable-multiselect-value', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl:'./filter-searchable-multiselect-value.component.html' + templateUrl: './filter-searchable-multiselect-value.component.html', }) export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { @Input() public filter:QueryFilterInstanceResource; + @Input() public shouldFocus = false; + @Output() public filterChanged = new EventEmitter(); private _isEmpty:boolean; + public _availableOptions:HalResource[] = []; + public compareByHrefOrString = AngularTrackingHelpers.compareByHrefOrString; + public active:Set; + public requests = new DebouncedRequestSwitchmap( (searchTerm:string) => this.loadAvailable(searchTerm), errorNotificationHandler(this.halNotification), - true + true, ); + readonly text = { placeholder: this.I18n.t('js.placeholders.selection'), }; @@ -46,6 +55,7 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix public get value() { return this.filter.values; } + public get availableOptions() { return this._availableOptions; } @@ -61,13 +71,13 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix @ViewChild('ngSelectInstance', { static: true }) ngSelectInstance:NgSelectComponent; constructor(readonly halResourceService:HalResourceService, - readonly halSorting:HalResourceSortingService, - readonly apiV3Service:APIV3Service, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - protected currentProject:CurrentProjectService, - readonly halNotification:HalResourceNotificationService, - readonly ngZone:NgZone) { + readonly halSorting:HalResourceSortingService, + readonly apiV3Service:APIV3Service, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + protected currentProject:CurrentProjectService, + readonly halNotification:HalResourceNotificationService, + readonly ngZone:NgZone) { super(); } @@ -87,7 +97,7 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix this .requests .output$.pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((values:HalResource[]) => { this.availableOptions = values; @@ -97,13 +107,13 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix public loadAvailable(matching:string):Observable { const filters:ApiV3FilterBuilder = this.createFilters(matching); - const href = (this.filter.currentSchema!.values!.allowedValues as any).href; + const { href } = this.filter.currentSchema!.values!.allowedValues as any; const filteredData = (this.apiV3Service.collectionFromString(href) as APIv3ResourceCollection) .filtered(filters) .get() - .pipe(map(collection => collection.elements)); + .pipe(map((collection) => collection.elements)); return filteredData; } @@ -131,11 +141,9 @@ export class FilterSearchableMultiselectValueComponent extends UntilDestroyedMix if (component && component.dropdownPanel) { this.ngZone.runOutsideAngular(() => { setTimeout(() => { - component.dropdownPanel._updatePosition(); + component.dropdownPanel._updatePosition(); }, 25); - }); - } } } diff --git a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts index 61c7214041..b03bd2a222 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,23 +28,25 @@ import { Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; @Component({ selector: 'filter-string-value', - templateUrl: './filter-string-value.component.html' + templateUrl: './filter-string-value.component.html', }) export class FilterStringValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new DebouncedEventEmitter(componentDestroyed(this)); readonly text = { - enter_text: this.I18n.t('js.work_packages.description_enter_text') + enter_text: this.I18n.t('js.work_packages.description_enter_text'), }; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts index 2e54965910..076aaeb7e4 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; import { AfterViewInit, ChangeDetectionStrategy, @@ -37,33 +37,36 @@ import { Input, OnInit, Output, - ViewChild + ViewChild, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; -import { HalResourceSortingService } from "core-app/features/hal/services/hal-resource-sorting.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { RootResource } from "core-app/features/hal/resources/root-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResourceSortingService } from 'core-app/features/hal/services/hal-resource-sorting.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { RootResource } from 'core-app/features/hal/resources/root-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; @Component({ selector: 'filter-toggled-multiselect-value', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './filter-toggled-multiselect-value.component.html' + templateUrl: './filter-toggled-multiselect-value.component.html', }) export class FilterToggledMultiselectValueComponent implements OnInit, AfterViewInit { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new EventEmitter(); @ViewChild('ngSelectInstance', { static: true }) ngSelectInstance:NgSelectComponent; public _availableOptions:HalResource[] = []; + public compareByHrefOrString = AngularTrackingHelpers.compareByHrefOrString; private _isEmpty:boolean; @@ -73,12 +76,12 @@ export class FilterToggledMultiselectValueComponent implements OnInit, AfterView }; constructor(readonly halResourceService:HalResourceService, - readonly halSorting:HalResourceSortingService, - readonly PathHelper:PathHelperService, - readonly apiV3Service:APIV3Service, - readonly currentUser:CurrentUserService, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService) { + readonly halSorting:HalResourceSortingService, + readonly PathHelper:PathHelperService, + readonly apiV3Service:APIV3Service, + readonly currentUser:CurrentUserService, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService) { } ngOnInit() { @@ -130,7 +133,7 @@ export class FilterToggledMultiselectValueComponent implements OnInit, AfterView } private fetchAllowedValues() { - if ((this.filter.currentSchema!.values!.allowedValues as CollectionResource)['$load']) { + if ((this.filter.currentSchema!.values!.allowedValues as CollectionResource).$load) { this.loadAllowedValues(); } else { this.availableOptions = (this.filter.currentSchema!.values!.allowedValues as HalResource[]); @@ -172,10 +175,10 @@ export class FilterToggledMultiselectValueComponent implements OnInit, AfterView _links: { self: { href: this.apiV3Service.users.me.path, - title: this.I18n.t('js.label_me') - } - } - }, true + title: this.I18n.t('js.label_me'), + }, + }, + }, true, ); this._availableOptions.unshift(me); diff --git a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts index 63dcea48a2..9b796ce810 100644 --- a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,30 +26,39 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { + Component, EventEmitter, Input, OnInit, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; @Component({ selector: '[query-filter]', - templateUrl: './query-filter.component.html' + templateUrl: './query-filter.component.html', }) export class QueryFilterComponent implements OnInit { @Input() public shouldFocus = false; + @Input() public filter:QueryFilterInstanceResource; + @Output() public filterChanged = new EventEmitter(); + @Output() public deactivateFilter = new EventEmitter(); public availableOperators:any; + public showValuesInput = false; + public eeShowBanners = false; + public trackByHref = AngularTrackingHelpers.halHref; + public compareByHref = AngularTrackingHelpers.compareByHref; public text = { @@ -62,16 +71,16 @@ export class QueryFilterComponent implements OnInit { }; constructor(readonly wpTableFilters:WorkPackageViewFiltersService, - readonly schemaCache:SchemaCacheService, - readonly I18n:I18nService, - readonly currentProject:CurrentProjectService, - readonly bannerService:BannersService) { + readonly schemaCache:SchemaCacheService, + readonly I18n:I18nService, + readonly currentProject:CurrentProjectService, + readonly bannerService:BannersService) { } public onFilterUpdated(filter:QueryFilterInstanceResource) { this.filter = filter; - this.showValuesInput = this.showValues(this.filter); - this.filterChanged.emit(this.filter); + this.showValuesInput = this.showValues(); + this.filterChanged.emit(); } public removeThisFilter() { @@ -89,10 +98,10 @@ export class QueryFilterComponent implements OnInit { ngOnInit() { this.eeShowBanners = this.bannerService.eeShowBanners; this.availableOperators = this.schemaCache.of(this.filter).availableOperators; - this.showValuesInput = this.showValues(this.filter); + this.showValuesInput = this.showValues(); } - private showValues(filter:QueryFilterInstanceResource) { - return this.filter.currentSchema!.isValueRequired() && this.filter.currentSchema!.values!.type !== '[1]Boolean'; + private showValues() { + return this.filter.currentSchema!.isValueRequired() && this.filter.currentSchema!.values!.type !== '[1]Boolean'; } } diff --git a/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts b/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts index 4a02fb389e..bd45ba8643 100644 --- a/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/query-filters/query-filters.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,38 +26,43 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'; +import { + ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, Output, ViewChild, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; const ADD_FILTER_SELECT_INDEX = -1; - @Component({ selector: 'query-filters', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './query-filters.component.html' + templateUrl: './query-filters.component.html', }) export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit, OnChanges { - @ViewChild(NgSelectComponent) public ngSelectComponent:NgSelectComponent; + @Input() public filters:QueryFilterInstanceResource[]; + @Input() public showCloseFilter = false; - @Output() public filtersChanged = new DebouncedEventEmitter(componentDestroyed(this)); + @Output() public filtersChanged = new DebouncedEventEmitter(componentDestroyed(this)); public remainingFilters:any[] = []; + public eeShowBanners = false; + public focusElementIndex = 0; + public trackByName = AngularTrackingHelpers.trackByName; public text = { @@ -70,13 +75,13 @@ export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit selected_filter_list: this.I18n.t('js.label_selected_filter_list'), button_delete: this.I18n.t('js.button_delete'), please_select: this.I18n.t('js.placeholders.selection'), - filter_by_text: this.I18n.t('js.work_packages.label_filter_by_text') + filter_by_text: this.I18n.t('js.work_packages.label_filter_by_text'), }; constructor(readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly I18n:I18nService, - readonly bannerService:BannersService) { + readonly wpFiltersService:WorkPackageFiltersService, + readonly I18n:I18nService, + readonly bannerService:BannersService) { super(); } @@ -108,7 +113,7 @@ export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit public deactivateFilter(removedFilter:QueryFilterInstanceResource) { const index = this.filters.indexOf(removedFilter); - _.remove(this.filters, f => f.id === removedFilter.id); + _.remove(this.filters, (f) => f.id === removedFilter.id); this.filtersChanged.emit(this.filters); @@ -148,8 +153,8 @@ export class QueryFiltersComponent extends UntilDestroyedMixin implements OnInit } public isFilterAvailable(filter:QueryFilterResource):boolean { - return (this.wpTableFilters.availableFilters.some(availableFilter => availableFilter.id === filter.id) && - !(this.wpTableFilters.hidden.includes(filter.id) || filter.isTemplated())); + return (this.wpTableFilters.availableFilters.some((availableFilter) => availableFilter.id === filter.id) + && !(this.wpTableFilters.hidden.includes(filter.id) || filter.isTemplated())); } public onOpen() { diff --git a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts index 52e4797b01..2e177a1ad9 100644 --- a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,19 +27,20 @@ //++ import { Component, EventEmitter, Output } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { Subject } from "rxjs"; -import { debounceTime, distinctUntilChanged, map, tap } from "rxjs/operators"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { input } from "reactivestates"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { Subject } from 'rxjs'; +import { + debounceTime, distinctUntilChanged, map, tap, +} from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { input } from 'reactivestates'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; @Component({ selector: 'wp-filter-by-text-input', - templateUrl: './quick-filter-by-text-input.html' + templateUrl: './quick-filter-by-text-input.html', }) export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { @@ -49,7 +50,7 @@ export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { createWithDropdown: this.I18n.t('js.work_packages.create.button'), createButton: this.I18n.t('js.label_work_package'), explanation: this.I18n.t('js.label_create_work_package'), - placeholder: this.I18n.t('js.work_packages.placeholder_filter_by_text') + placeholder: this.I18n.t('js.work_packages.placeholder_filter_by_text'), }; /** Observable to the current search filter term */ @@ -59,8 +60,8 @@ export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { public searchTermChanged:Subject = new Subject(); constructor(readonly I18n:I18nService, - readonly querySpace:IsolatedQuerySpace, - readonly wpTableFilters:WorkPackageViewFiltersService) { + readonly querySpace:IsolatedQuerySpace, + readonly wpTableFilters:WorkPackageViewFiltersService) { super(); this.wpTableFilters @@ -73,9 +74,9 @@ export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { }), ) .subscribe((upstreamTerm:string) => { - console.log("upstream " + upstreamTerm + " " + (this.searchTerm as any).timestampOfLastValue); + console.log(`upstream ${upstreamTerm} ${(this.searchTerm as any).timestampOfLastValue}`); if (!this.searchTerm.value || this.searchTerm.isValueOlderThan(500)) { - console.log("Upstream value setting to " + upstreamTerm); + console.log(`Upstream value setting to ${upstreamTerm}`); this.searchTerm.putValue(upstreamTerm); } }); @@ -87,9 +88,9 @@ export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { tap((val) => this.searchTerm.putValue(val)), debounceTime(500), ) - .subscribe(term => { + .subscribe((term) => { if (term.length > 0) { - this.wpTableFilters.replace('search', filter => { + this.wpTableFilters.replace('search', (filter) => { filter.operator = filter.findOperator('**')!; filter.values = [term]; }); diff --git a/frontend/src/app/features/work-packages/components/filters/wp-filters/wp-filters.service.ts b/frontend/src/app/features/work-packages/components/filters/wp-filters/wp-filters.service.ts index 1ede48c6a8..369b4b50ba 100644 --- a/frontend/src/app/features/work-packages/components/filters/wp-filters/wp-filters.service.ts +++ b/frontend/src/app/features/work-packages/components/filters/wp-filters/wp-filters.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Injectable } from "@angular/core"; -import { input } from "reactivestates"; -import { Observable } from "rxjs"; -import { takeUntil } from "rxjs/operators"; +import { Injectable } from '@angular/core'; +import { input } from 'reactivestates'; +import { Observable } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Injectable() export class WorkPackageFiltersService { diff --git a/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment-field-handler.ts b/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment-field-handler.ts index 960a704d2c..6ccde23811 100644 --- a/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment-field-handler.ts +++ b/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment-field-handler.ts @@ -1,16 +1,22 @@ -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { ElementRef, Injector, OnInit, Directive } from "@angular/core"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { Subject } from "rxjs"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { + ElementRef, Injector, OnInit, Directive, +} from '@angular/core'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { Subject } from 'rxjs'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Directive() export abstract class WorkPackageCommentFieldHandler extends EditFieldHandler implements OnInit { public fieldName = 'comment'; + public handler = this; + public active = false; + public inEditMode = false; + public inFlight = false; public change:WorkPackageChangeset; @@ -19,7 +25,7 @@ export abstract class WorkPackageCommentFieldHandler extends EditFieldHandler im public onDestroy = new Subject(); constructor(protected elementRef:ElementRef, - protected injector:Injector) { + protected injector:Injector) { super(); } @@ -39,7 +45,7 @@ export abstract class WorkPackageCommentFieldHandler extends EditFieldHandler im withText += '\n'; } - this.change.setValue('comment' , { raw: withText }); + this.change.setValue('comment', { raw: withText }); } public get schema():IFieldSchema { @@ -48,7 +54,7 @@ export abstract class WorkPackageCommentFieldHandler extends EditFieldHandler im writable: true, required: false, type: '_comment', - hasDefault: false + hasDefault: false, }; } diff --git a/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment.component.ts b/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment.component.ts index 8aee98b490..9bc48c1fd4 100644 --- a/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment.component.ts +++ b/frontend/src/app/features/work-packages/components/work-package-comment/work-package-comment.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -38,55 +38,59 @@ import { OnDestroy, OnInit, TemplateRef, - ViewChild -} from "@angular/core"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageCommentFieldHandler } from "core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler"; -import { CommentService } from "core-app/features/work-packages/components/wp-activity/comment-service"; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; + ViewChild, +} from '@angular/core'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageCommentFieldHandler } from 'core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler'; +import { CommentService } from 'core-app/features/work-packages/components/wp-activity/comment-service'; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; @Component({ selector: 'work-package-comment', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './work-package-comment.component.html' + templateUrl: './work-package-comment.component.html', }) export class WorkPackageCommentComponent extends WorkPackageCommentFieldHandler implements OnInit, OnDestroy { @Input() public workPackage:WorkPackageResource; @ContentChild(TemplateRef) template:TemplateRef; + @ViewChild('commentContainer') public commentContainer:ElementRef; public text = { editTitle: this.I18n.t('js.label_add_comment_title'), addComment: this.I18n.t('js.label_add_comment'), cancelTitle: this.I18n.t('js.label_cancel_comment'), - placeholder: this.I18n.t('js.label_add_comment_title') + placeholder: this.I18n.t('js.label_add_comment_title'), }; + public fieldLabel:string = this.text.editTitle; public inFlight = false; + public canAddComment:boolean; + public showAbove:boolean; public htmlId = 'wp-comment-field'; constructor(protected elementRef:ElementRef, - protected injector:Injector, - protected commentService:CommentService, - protected wpLinkedActivities:WorkPackagesActivityService, - protected ConfigurationService:ConfigurationService, - protected loadingIndicator:LoadingIndicatorService, - protected apiV3Service:APIV3Service, - protected workPackageNotificationService:WorkPackageNotificationService, - protected NotificationsService:NotificationsService, - protected cdRef:ChangeDetectorRef, - protected I18n:I18nService) { + protected injector:Injector, + protected commentService:CommentService, + protected wpLinkedActivities:WorkPackagesActivityService, + protected ConfigurationService:ConfigurationService, + protected loadingIndicator:LoadingIndicatorService, + protected apiV3Service:APIV3Service, + protected workPackageNotificationService:WorkPackageNotificationService, + protected NotificationsService:NotificationsService, + protected cdRef:ChangeDetectorRef, + protected I18n:I18nService) { super(elementRef, injector); } @@ -98,7 +102,7 @@ export class WorkPackageCommentComponent extends WorkPackageCommentFieldHandler this.commentService.quoteEvents .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((quote:string) => { this.activate(quote); diff --git a/frontend/src/app/features/work-packages/components/work-package-comment/wp-comment-field.component.ts b/frontend/src/app/features/work-packages/components/work-package-comment/wp-comment-field.component.ts index 186f440612..309aee721a 100644 --- a/frontend/src/app/features/work-packages/components/work-package-comment/wp-comment-field.component.ts +++ b/frontend/src/app/features/work-packages/components/work-package-comment/wp-comment-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,17 +27,18 @@ //++ import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit } from '@angular/core'; import { FormattableEditFieldComponent, -} from "core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +} from 'core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Component({ - templateUrl: "../../../../shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.html", + templateUrl: '../../../../shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.html', }) export class WorkPackageCommentFieldComponent extends FormattableEditFieldComponent implements OnInit { public isBusy = false; + public name = 'comment'; @InjectField() public ConfigurationService:ConfigurationService; diff --git a/frontend/src/app/features/work-packages/components/wp-activity/activity-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-activity/activity-entry.component.ts index a48936246b..1b64c7d0b1 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/activity-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/activity-entry.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,35 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input, OnInit } from "@angular/core"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; - +import { Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ selector: 'activity-entry', - templateUrl: './activity-entry.component.html' + templateUrl: './activity-entry.component.html', }) export class ActivityEntryComponent implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public activity:any; + @Input() public activityNo:number; + @Input() public isInitial:boolean; public projectId:string; + public activityType:string; constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService) { + readonly I18n:I18nService) { } - ngOnInit() { this.projectId = this.workPackage.project.idFromLink; this.activityType = this.activity._type; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-activity/activity-link.component.ts b/frontend/src/app/features/work-packages/components/wp-activity/activity-link.component.ts index 2d04b9fdb6..f03302840f 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/activity-link.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/activity-link.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, OnInit } from "@angular/core"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ selector: 'activity-link', @@ -9,13 +9,15 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-packag uiSref="work-packages.show" [uiParams]="{workPackageId: workPackage.id!, '#': activityHtmlId }"> - ` + `, }) export class ActivityLinkComponent implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public activityNo:number; public activityHtmlId:string; + public activityLabel:string; ngOnInit() { @@ -31,9 +33,9 @@ function activityLink() { `, scope: { }, - link: function(scope:any) { + link(scope:any) { scope.workPackageId = scope.workPackage.id!; - scope.activityHtmlId = 'activity-' + scope.activityNo; - } + scope.activityHtmlId = `activity-${scope.activityNo}`; + }, }; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-activity/comment-service.ts b/frontend/src/app/features/work-packages/components/wp-activity/comment-service.ts index d4cdc02baf..3092d54205 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/comment-service.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/comment-service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,14 +29,13 @@ import { Injectable } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { Subject } from "rxjs"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Subject } from 'rxjs'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Injectable() export class CommentService { - // Replacement for ng1 $scope.$emit on activty-entry to mark comments to be quoted. // Should be generalized if needed for more than that. public quoteEvents = new Subject(); @@ -44,13 +43,14 @@ export class CommentService { constructor( readonly I18n:I18nService, private workPackageNotificationService:WorkPackageNotificationService, - private NotificationsService:NotificationsService) { + private NotificationsService:NotificationsService, + ) { } public createComment(workPackage:WorkPackageResource, comment:{ raw:string }) { return workPackage.addComment( - { comment: comment }, - { 'Content-Type': 'application/json; charset=UTF-8' } + { comment }, + { 'Content-Type': 'application/json; charset=UTF-8' }, ) .catch((error:any) => this.errorAndReject(error, workPackage)); } @@ -59,17 +59,17 @@ export class CommentService { const options = { ajax: { method: 'PATCH', - data: JSON.stringify({ comment: comment }), - contentType: 'application/json; charset=utf-8' - } + data: JSON.stringify({ comment }), + contentType: 'application/json; charset=utf-8', + }, }; return activity.update( - { comment: comment }, - { 'Content-Type': 'application/json; charset=UTF-8' } + { comment }, + { 'Content-Type': 'application/json; charset=UTF-8' }, ).then((activity:HalResource) => { this.NotificationsService.addSuccess( - this.I18n.t('js.work_packages.comment_updated') + this.I18n.t('js.work_packages.comment_updated'), ); return activity; diff --git a/frontend/src/app/features/work-packages/components/wp-activity/revision/revision-activity.component.ts b/frontend/src/app/features/work-packages/components/wp-activity/revision/revision-activity.component.ts index ea85c3bdd8..7fb08944f2 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/revision/revision-activity.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/revision/revision-activity.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,41 +25,52 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ selector: 'revision-activity', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './revision-activity.component.html' + templateUrl: './revision-activity.component.html', }) export class RevisionActivityComponent implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public activity:any; + @Input() public activityNo:number; public userId:string | number; + public userName:string; + public userActive:boolean; + public userPath:string | null; + public userLabel:string; + public userAvatar:string; public project:ProjectResource; + public revision:string; + public message:string; public revisionLink:string; constructor(readonly I18n:I18nService, - readonly timezoneService:TimezoneService, - readonly cdRef:ChangeDetectorRef, - readonly apiV3Service:APIV3Service) { + readonly timezoneService:TimezoneService, + readonly cdRef:ChangeDetectorRef, + readonly apiV3Service:APIV3Service) { } ngOnInit() { @@ -76,14 +87,14 @@ export class RevisionActivityComponent implements OnInit { link.href = revisionPath; link.title = this.revision; link.textContent = this.I18n.t( - "js.label_committed_link", - { revision_identifier: formattedRevision } + 'js.label_committed_link', + { revision_identifier: formattedRevision }, ); - this.revisionLink = this.I18n.t("js.label_committed_at", + this.revisionLink = this.I18n.t('js.label_committed_at', { committed_revision_link: link.outerHTML, - date: this.timezoneService.formattedDatetime(this.activity.createdAt) + date: this.timezoneService.formattedDatetime(this.activity.createdAt), }); } diff --git a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts index 0dd09ae075..8cf68a9a9b 100644 --- a/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-activity/user/user-activity.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -36,40 +36,51 @@ import { ElementRef, Injector, Input, NgZone, - OnInit -} from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageCommentFieldHandler } from "core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler"; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { CommentService } from "core-app/features/work-packages/components/wp-activity/comment-service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; + OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageCommentFieldHandler } from 'core-app/features/work-packages/components/work-package-comment/work-package-comment-field-handler'; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { CommentService } from 'core-app/features/work-packages/components/wp-activity/comment-service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ selector: 'user-activity', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './user-activity.component.html', - styleUrls: ['./user-activity.component.sass'] + styleUrls: ['./user-activity.component.sass'], }) export class UserActivityComponent extends WorkPackageCommentFieldHandler implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public activity:HalResource; + @Input() public activityNo:number; + @Input() public isInitial:boolean; public userCanEdit = false; + public userCanQuote = false; public userId:string | number; + public user:UserResource; + public userName:string; + public userAvatar:string; + public details:any[] = []; + public isComment:boolean; + public isBcfComment:boolean; + public postedComment:SafeHtml; public focused = false; @@ -84,24 +95,23 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem private $element:JQuery; constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly sanitization:DomSanitizer, - readonly PathHelper:PathHelperService, - readonly wpLinkedActivities:WorkPackagesActivityService, - readonly commentService:CommentService, - readonly ConfigurationService:ConfigurationService, - readonly apiV3Service:APIV3Service, - readonly cdRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly ngZone:NgZone, - protected appRef:ApplicationRef) { + readonly injector:Injector, + readonly sanitization:DomSanitizer, + readonly PathHelper:PathHelperService, + readonly wpLinkedActivities:WorkPackagesActivityService, + readonly commentService:CommentService, + readonly ConfigurationService:ConfigurationService, + readonly apiV3Service:APIV3Service, + readonly cdRef:ChangeDetectorRef, + readonly I18n:I18nService, + readonly ngZone:NgZone, + protected appRef:ApplicationRef) { super(elementRef, injector); } public ngOnInit() { super.ngOnInit(); - this.htmlId = `user_activity_edit_field_${this.activityNo}`; this.updateCommentText(); this.isComment = this.activity._type === 'Activity::Comment'; @@ -164,9 +174,8 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem public get bcfSnapshotUrl() { if (_.get(this.activity, 'bcfViewpoints[0]')) { return `${_.get(this.activity, 'bcfViewpoints[0]').href}/snapshot`; - } else { - return null; } + return null; } public async updateComment() { @@ -191,7 +200,7 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem public focusEditIcon() { // Find the according edit icon and focus it - jQuery('.edit-activity--' + this.activityNo + ' a').focus(); + jQuery(`.edit-activity--${this.activityNo} a`).focus(); } public focus() { @@ -214,11 +223,9 @@ export class UserActivityComponent extends WorkPackageCommentFieldHandler implem public quotedText(rawComment:string) { const quoted = rawComment.split('\n') - .map(function(line:string) { - return '\n> ' + line; - }) + .map((line:string) => `\n> ${line}`) .join(''); - return this.userName + ' wrote:\n' + quoted; + return `${this.userName} wrote:\n${quoted}`; } deactivate(focus:boolean):void { diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts index b12c2ec355..478526d926 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input, EventEmitter, Output } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { + Component, Input, EventEmitter, Output, +} from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; @Component({ templateUrl: './wp-breadcrumb-parent.html', @@ -38,9 +40,11 @@ import { WorkPackageNotificationService } from "core-app/features/work-packages/ }) export class WorkPackageBreadcrumbParentComponent { @Input('workPackage') workPackage:WorkPackageResource; + @Output('onSwitch') onSwitch = new EventEmitter(); public isSaving = false; + public text = { edit_parent: this.I18n.t('js.relation_buttons.change_parent'), set_or_remove_parent: this.I18n.t('js.relations_autocomplete.parent_placeholder'), @@ -53,7 +57,7 @@ export class WorkPackageBreadcrumbParentComponent { public constructor( protected readonly I18n:I18nService, protected readonly wpRelationsHierarchy:WorkPackageRelationsHierarchyService, - protected readonly notificationService:WorkPackageNotificationService + protected readonly notificationService:WorkPackageNotificationService, ) { } @@ -99,5 +103,3 @@ export class WorkPackageBreadcrumbParentComponent { } } } - - diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts index 26121278ae..7e3b7a187f 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,7 +28,7 @@ import { Component, Input } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ templateUrl: './wp-breadcrumb.html', @@ -60,5 +60,3 @@ export class WorkPackageBreadcrumbComponent { this.inputActive = val; } } - - diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-buttons.module.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-buttons.module.ts index 845069c089..55c50adaee 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-buttons.module.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-buttons.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; export interface ButtonControllerText { activate:string; @@ -38,10 +38,13 @@ export interface ButtonControllerText { export abstract class AbstractWorkPackageButtonComponent extends UntilDestroyedMixin { public disabled:boolean; + public buttonId:string; + public iconClass:string; public accessKey:number; + public isActive = false; protected text:ButtonControllerText; @@ -53,7 +56,7 @@ export abstract class AbstractWorkPackageButtonComponent extends UntilDestroyedM activate: this.I18n.t('js.label_activate'), deactivate: this.I18n.t('js.label_deactivate'), label: this.labelKey ? this.I18n.t(this.labelKey) : '', - buttonText: this.textKey ? this.I18n.t(this.textKey) : '' + buttonText: this.textKey ? this.I18n.t(this.textKey) : '', }; } @@ -74,11 +77,11 @@ export abstract class AbstractWorkPackageButtonComponent extends UntilDestroyedM } protected get activationPrefix():string { - return !this.isActive ? this.text.activate + ' ' : ''; + return !this.isActive ? `${this.text.activate} ` : ''; } protected get deactivationPrefix():string { - return this.isActive ? this.text.deactivate + ' ' : ''; + return this.isActive ? `${this.text.deactivate} ` : ''; } protected get prefix():string { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts index 2f4ed09141..c226ddb25c 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,41 +27,48 @@ //++ import { StateService, TransitionService } from '@uirouter/core'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { Observable } from "rxjs"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { Observable } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @Component({ selector: 'wp-create-button', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './wp-create-button.html' + templateUrl: './wp-create-button.html', }) export class WorkPackageCreateButtonComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { @Input('allowed') allowedWhen:string[]; + @Input('stateName$') stateName$:Observable; allowed:boolean; + disabled:boolean; + projectIdentifier:string|null; + types:any; + transitionUnregisterFn:Function; text = { createWithDropdown: this.I18n.t('js.work_packages.create.button'), createButton: this.I18n.t('js.label_work_package'), - explanation: this.I18n.t('js.label_create_work_package') + explanation: this.I18n.t('js.label_create_work_package'), }; constructor(readonly $state:StateService, - readonly currentProject:CurrentProjectService, - readonly authorisationService:AuthorisationService, - readonly transition:TransitionService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef) { + readonly currentProject:CurrentProjectService, + readonly authorisationService:AuthorisationService, + readonly transition:TransitionService, + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef) { super(); } @@ -74,7 +81,7 @@ export class WorkPackageCreateButtonComponent extends UntilDestroyedMixin implem .subscribe(() => { this.allowed = !!this .allowedWhen - .find(combined => { + .find((combined) => { const [module, permission] = combined.split('.'); return this.authorisationService.can(module, permission); }); @@ -82,7 +89,6 @@ export class WorkPackageCreateButtonComponent extends UntilDestroyedMixin implem this.updateDisabledState(); }); - this.transitionUnregisterFn = this.transition.onSuccess({}, this.updateDisabledState.bind(this)); } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts index 38bc5e88dc..2b7442395d 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { KeepTabService } from '../../wp-single-view-tabs/keep-tab/keep-tab.service'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { StateService, TransitionService } from '@uirouter/core'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, +} from '@angular/core'; import { AbstractWorkPackageButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-buttons.module'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { States } from "core-app/core/states/states.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { States } from 'core-app/core/states/states.service'; +import { KeepTabService } from '../../wp-single-view-tabs/keep-tab/keep-tab.service'; @Component({ templateUrl: '../wp-button.template.html', @@ -41,14 +43,21 @@ import { States } from "core-app/core/states/states.service"; }) export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageButtonComponent implements OnDestroy { public projectIdentifier:string; + public accessKey = 8; + public activeState = 'work-packages.partitioned.list.details'; + public listState = 'work-packages.partitioned.list'; + public buttonId = 'work-packages-details-view-button'; + public buttonClass = 'toolbar-icon'; + public iconClass = 'icon-info2'; public activateLabel:string; + public deactivateLabel:string; private transitionListener:Function; @@ -60,7 +69,8 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu readonly cdRef:ChangeDetectorRef, public states:States, public wpTableFocus:WorkPackageViewFocusService, - public keepTab:KeepTabService) { + public keepTab:KeepTabService, + ) { super(I18n); this.activateLabel = I18n.t('js.button_open_details'); @@ -80,9 +90,8 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu public get label():string { if (this.isActive) { return this.deactivateLabel; - } else { - return this.activateLabel; } + return this.activateLabel; } public isToggle():boolean { @@ -102,7 +111,7 @@ export class WorkPackageDetailsViewButtonComponent extends AbstractWorkPackageBu public openDetailsView():void { const params = { - workPackageId: this.wpTableFocus.focusedWorkPackage + workPackageId: this.wpTableFocus.focusedWorkPackage, }; this.keepTab.goCurrentDetailsState(params); diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts index e25dc5583a..3254165b85 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,27 +28,31 @@ import { AbstractWorkPackageButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-buttons.module'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, +} from '@angular/core'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; @Component({ selector: 'wp-filter-button', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './wp-filter-button.html' + templateUrl: './wp-filter-button.html', }) export class WorkPackageFilterButtonComponent extends AbstractWorkPackageButtonComponent implements OnInit { public count:number; + public initialized = false; public buttonId = 'work-packages-filter-toggle-button'; + public iconClass = 'icon-filter'; constructor(readonly I18n:I18nService, - protected cdRef:ChangeDetectorRef, - protected wpFiltersService:WorkPackageFiltersService, - protected wpTableFilters:WorkPackageViewFiltersService) { + protected cdRef:ChangeDetectorRef, + protected wpFiltersService:WorkPackageFiltersService, + protected wpTableFilters:WorkPackageViewFiltersService) { super(I18n); } @@ -84,7 +88,7 @@ export class WorkPackageFilterButtonComponent extends AbstractWorkPackageButtonC this.wpTableFilters .live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.count = this.wpTableFilters.currentlyVisibleFilters.length; diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component.ts index d7f7af01cd..ad0c3a8091 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,7 +27,6 @@ //++ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; @Component({ template: ` @@ -40,7 +39,7 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; `, changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'wp-fold-toggle-view-button' + selector: 'wp-fold-toggle-view-button', }) export class WorkPackageFoldToggleButtonComponent { } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts index 379c6b85c4..4824f7aaab 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,7 +27,7 @@ //++ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ templateUrl: './wp-settings-button.component.html', @@ -35,7 +35,7 @@ import { I18nService } from "core-app/core/i18n/i18n.service"; }) export class WorkPackageSettingsButtonComponent { public text = { - 'button_settings': this.I18n.t('js.button_settings') + button_settings: this.I18n.t('js.button_settings'), }; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts index f40c9709b5..8f7e172c0b 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,35 +26,37 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { + ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { ISchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; @Component({ selector: 'wp-status-button', styleUrls: ['./wp-status-button.component.sass'], - templateUrl: './wp-status-button.html' + templateUrl: './wp-status-button.html', }) export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implements OnInit { @Input('workPackage') public workPackage:WorkPackageResource; + @Input('containerClass') public containerClass:string; public text = { explanation: this.I18n.t('js.label_edit_status'), workPackageReadOnly: this.I18n.t('js.work_packages.message_work_package_read_only'), - workPackageStatusBlocked: this.I18n.t('js.work_packages.message_work_package_status_blocked') + workPackageStatusBlocked: this.I18n.t('js.work_packages.message_work_package_status_blocked'), }; constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly schemaCache:SchemaCacheService, - readonly halEditing:HalResourceEditingService) { + readonly cdRef:ChangeDetectorRef, + readonly schemaCache:SchemaCacheService, + readonly halEditing:HalResourceEditingService) { super(); } @@ -63,7 +65,7 @@ export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implem .temporaryEditResource(this.workPackage) .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp) => { this.workPackage = wp; @@ -79,15 +81,14 @@ export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implem public get buttonTitle() { if (this.schema.isReadonly) { return this.text.workPackageReadOnly; - } else if (this.schema.isEditable && !this.allowed) { + } if (this.schema.isEditable && !this.allowed) { return this.text.workPackageStatusBlocked; - } else { - return ''; } + return ''; } public get statusHighlightClass() { - const status = this.status; + const { status } = this; if (!status) { return; } @@ -109,8 +110,7 @@ export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implem private get schema() { if (this.halEditing.typedState(this.workPackage).hasValue()) { return this.halEditing.typedState(this.workPackage).value!.schema; - } else { - return this.schemaCache.of(this.workPackage) as ISchemaProxy; } + return this.schemaCache.of(this.workPackage); } } diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts index a57d1ef073..3dc83d0052 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AbstractWorkPackageButtonComponent, ButtonControllerText } from '../wp-buttons.module'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; +import { AbstractWorkPackageButtonComponent, ButtonControllerText } from '../wp-buttons.module'; export interface TimelineButtonText extends ButtonControllerText { zoomOut:string; @@ -42,28 +44,32 @@ export interface TimelineButtonText extends ButtonControllerText { templateUrl: './wp-timeline-toggle-button.html', styleUrls: ['./wp-timeline-toggle-button.sass'], selector: 'wp-timeline-toggle-button', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButtonComponent implements OnInit { public buttonId = 'work-packages-timeline-toggle-button'; + public iconClass = 'icon-view-timeline'; private activateLabel:string; + private deactivateLabel:string; public text:TimelineButtonText; public minZoomLevel:TimelineZoomLevel = 'days'; + public maxZoomLevel:TimelineZoomLevel = 'years'; public isAutoZoom = false; public isMaxLevel = false; + public isMinLevel = false; constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - public wpTableTimeline:WorkPackageViewTimelineService) { + readonly cdRef:ChangeDetectorRef, + public wpTableTimeline:WorkPackageViewTimelineService) { super(I18n); this.activateLabel = I18n.t('js.timelines.button_activate'); @@ -78,7 +84,7 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto this.wpTableTimeline .live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.isAutoZoom = this.wpTableTimeline.isAutoZoom(); @@ -90,7 +96,7 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto .appliedZoomLevel$ .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((current) => { this.isMaxLevel = current === this.maxZoomLevel; @@ -102,9 +108,8 @@ export class WorkPackageTimelineButtonComponent extends AbstractWorkPackageButto public get label():string { if (this.isActive) { return this.deactivateLabel; - } else { - return this.activateLabel; } + return this.activateLabel; } public isToggle():boolean { diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts index 6579dcca57..8f1e7d10d8 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewDisplayRepresentationService, wpDisplayCardRepresentation, - wpDisplayListRepresentation -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { combineLatest } from "rxjs"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; - + wpDisplayListRepresentation, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { combineLatest } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Component({ template: ` @@ -54,7 +55,7 @@ import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destr changeDetection: ChangeDetectionStrategy.OnPush, selector: 'wp-view-toggle-button', }) -export class WorkPackageViewToggleButton extends UntilDestroyedMixin implements OnInit { +export class WorkPackageViewToggleButtonComponent extends UntilDestroyedMixin implements OnInit { public view:string; public text:any = { @@ -64,9 +65,9 @@ export class WorkPackageViewToggleButton extends UntilDestroyedMixin implements }; constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService, - readonly wpTableTimeline:WorkPackageViewTimelineService) { + readonly cdRef:ChangeDetectorRef, + readonly wpDisplayRepresentationService:WorkPackageViewDisplayRepresentationService, + readonly wpTableTimeline:WorkPackageViewTimelineService) { super(); } @@ -77,7 +78,7 @@ export class WorkPackageViewToggleButton extends UntilDestroyedMixin implements ]); statesCombined.pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe(([display, timelines]) => { this.detectView(display, timelines.visible); this.cdRef.detectChanges(); diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts index 9316dbfc4c..1a4b4208b5 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AbstractWorkPackageButtonComponent } from '../wp-buttons.module'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import * as sfimport from "screenfull"; -import { Screenfull } from "screenfull"; +import * as sfimport from 'screenfull'; +import { Screenfull } from 'screenfull'; +import { AbstractWorkPackageButtonComponent } from '../wp-buttons.module'; const screenfull:Screenfull = sfimport as any; export const zenModeComponentSelector = 'zen-mode-toggle-button'; @@ -43,25 +43,27 @@ export const zenModeComponentSelector = 'zen-mode-toggle-button'; }) export class ZenModeButtonComponent extends AbstractWorkPackageButtonComponent { public buttonId = 'work-packages-zen-mode-toggle-button'; + public buttonClass = 'toolbar-icon'; + public iconClass = 'icon-zen-mode'; static inZenMode = false; private activateLabel:string; + private deactivateLabel:string; constructor(readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef) { + readonly cdRef:ChangeDetectorRef) { super(I18n); this.activateLabel = I18n.t('js.zen_mode.button_activate'); this.deactivateLabel = I18n.t('js.zen_mode.button_deactivate'); const self = this; - if (screenfull.enabled) { - screenfull.onchange(function() { + screenfull.onchange(() => { // This event might get triggered several times for once leaving // fullscreen mode. if (!screenfull.isFullscreen) { @@ -74,9 +76,8 @@ export class ZenModeButtonComponent extends AbstractWorkPackageButtonComponent { public get label():string { if (this.isActive) { return this.deactivateLabel; - } else { - return this.activateLabel; } + return this.activateLabel; } public isToggle():boolean { diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry.ts b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry.ts index 41f7f21f05..7a8d1afda4 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry.ts @@ -1,23 +1,21 @@ -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { CardClickHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/click-handler"; -import { CardDblClickHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/double-click-handler"; -import { CardRightClickHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/right-click-handler"; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { CardClickHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/click-handler'; +import { CardDblClickHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/double-click-handler'; +import { CardRightClickHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/right-click-handler'; import { WorkPackageViewEventHandler, - WorkPackageViewHandlerRegistry -} from "core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry"; - + WorkPackageViewHandlerRegistry, +} from 'core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry'; export type CardEventHandler = WorkPackageViewEventHandler; export class CardViewHandlerRegistry extends WorkPackageViewHandlerRegistry { - protected eventHandlers:((c:WorkPackageCardViewComponent) => CardEventHandler)[] = [ // Clicking on the card (not within a cell) - c => new CardClickHandler(this.injector, c), + (c) => new CardClickHandler(this.injector, c), // Double Clicking on the row (not within a cell) - c => new CardDblClickHandler(this.injector, c), + (c) => new CardDblClickHandler(this.injector, c), // Right clicking on cards - t => new CardRightClickHandler(this.injector, t), + (t) => new CardRightClickHandler(this.injector, t), ]; } diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/click-handler.ts b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/click-handler.ts index 06bb4cf294..cae27eb391 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/click-handler.ts @@ -1,20 +1,23 @@ import { Injector } from '@angular/core'; -import { CardEventHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; -import { WorkPackageCardViewService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service"; -import { StateService } from "@uirouter/core"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { CardEventHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; +import { WorkPackageCardViewService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service'; +import { StateService } from '@uirouter/core'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class CardClickHandler implements CardEventHandler { - // Injections @InjectField() deviceService:DeviceService; + @InjectField() $state:StateService; + @InjectField() wpTableSelection:WorkPackageViewSelectionService; + @InjectField() wpTableFocus:WorkPackageViewFocusService; + @InjectField() wpCardView:WorkPackageCardViewService; constructor(public readonly injector:Injector, @@ -54,7 +57,6 @@ export class CardClickHandler implements CardEventHandler { return false; } - protected handleWorkPackage(card:WorkPackageCardViewComponent, wpId:any, element:JQuery, evt:JQuery.TriggeredEvent) { this.setSelection(card, wpId, element, evt); @@ -87,5 +89,4 @@ export class CardClickHandler implements CardEventHandler { // Thus save that card for the details view button. this.wpTableFocus.updateFocus(wpId); } - } diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/double-click-handler.ts b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/double-click-handler.ts index 3dafd7227f..0bb7d58906 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/double-click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/double-click-handler.ts @@ -1,12 +1,13 @@ import { Injector } from '@angular/core'; -import { CardEventHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { StateService } from "@uirouter/core"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { CardEventHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { StateService } from '@uirouter/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class CardDblClickHandler implements CardEventHandler { @InjectField() $state:StateService; + @InjectField() wpTableSelection:WorkPackageViewSelectionService; constructor(public readonly injector:Injector, @@ -45,4 +46,3 @@ export class CardDblClickHandler implements CardEventHandler { return false; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/right-click-handler.ts b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/right-click-handler.ts index 1709722e55..d5e651b4cc 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/right-click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/event-handler/right-click-handler.ts @@ -1,19 +1,20 @@ import { Injector } from '@angular/core'; -import { CardEventHandler } from "core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { uiStateLinkClass } from "core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder"; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { WorkPackageCardViewService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service"; -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; -import { WorkPackageViewContextMenu } from "core-app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { CardEventHandler } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { uiStateLinkClass } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { WorkPackageCardViewService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service'; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { WorkPackageViewContextMenu } from 'core-app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class CardRightClickHandler implements CardEventHandler { - // Injections @InjectField() wpTableSelection:WorkPackageViewSelectionService; + @InjectField() wpCardView:WorkPackageCardViewService; + @InjectField() opContextMenu:OPContextMenuService; constructor(public readonly injector:Injector, @@ -51,19 +52,17 @@ export class CardRightClickHandler implements CardEventHandler { if (!wpId) { return true; - } else { - const classIdentifier = element.data('classIdentifier'); - const index = this.wpCardView.findRenderedCard(classIdentifier); - - if (!this.wpTableSelection.isSelected(wpId)) { - this.wpTableSelection.setSelection(wpId, index); - } + } + const classIdentifier = element.data('classIdentifier'); + const index = this.wpCardView.findRenderedCard(classIdentifier); - const handler = new WorkPackageViewContextMenu(this.injector, wpId, jQuery(evt.target) as JQuery, {}, card.showInfoButton); - this.opContextMenu.show(handler, evt); + if (!this.wpTableSelection.isSelected(wpId)) { + this.wpTableSelection.setSelection(wpId, index); } + const handler = new WorkPackageViewContextMenu(this.injector, wpId, jQuery(evt.target) as JQuery, {}, card.showInfoButton); + this.opContextMenu.show(handler, evt); + return false; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts index 7b93512a89..7bf5f946bd 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service.ts @@ -1,20 +1,19 @@ import { Injectable, Injector, Optional } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { DragAndDropHelpers } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.helpers"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { DragAndDropHelpers } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.helpers'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; @Injectable() export class WorkPackageCardDragAndDropService { - private _workPackages:WorkPackageResource[]; /** Whether the card view has an active inline created wp */ @@ -23,16 +22,15 @@ export class WorkPackageCardDragAndDropService { /** A reference to the component in use, to have access to the current input variables */ public cardView:WorkPackageCardViewComponent; - public constructor(readonly states:States, - readonly injector:Injector, - readonly reorderService:WorkPackageViewOrderService, - readonly wpCreate:WorkPackageCreateService, - readonly notificationService:WorkPackageNotificationService, - readonly apiV3Service:APIV3Service, - readonly currentProject:CurrentProjectService, - @Optional() readonly dragService:DragAndDropService, - readonly wpInlineCreate:WorkPackageInlineCreateService) { + readonly injector:Injector, + readonly reorderService:WorkPackageViewOrderService, + readonly wpCreate:WorkPackageCreateService, + readonly notificationService:WorkPackageNotificationService, + readonly apiV3Service:APIV3Service, + readonly currentProject:CurrentProjectService, + @Optional() readonly dragService:DragAndDropService, + readonly wpInlineCreate:WorkPackageInlineCreateService) { } @@ -95,7 +93,7 @@ export class WorkPackageCardDragAndDropService { } return result; - } + }, }); } @@ -112,7 +110,7 @@ export class WorkPackageCardDragAndDropService { */ public set workPackages(workPackages:WorkPackageResource[]) { if (this.activeInlineCreateWp) { - const existingNewWp = this._workPackages.find(o => o.isNew); + const existingNewWp = this._workPackages.find((o) => o.isNew); // If there is already a card for a new WP, // we have to replace this one by the new activeInlineCreateWp @@ -132,8 +130,8 @@ export class WorkPackageCardDragAndDropService { */ private get currentOrder():string[] { return this.workPackages - .filter(wp => wp && !wp.isNew) - .map(el => el.id!); + .filter((wp) => wp && !wp.isNew) + .map((el) => el.id!); } /** @@ -143,14 +141,12 @@ export class WorkPackageCardDragAndDropService { newOrder = _.uniq(newOrder); Promise - .all(newOrder.map(id => - this - .apiV3Service - .work_packages - .id(id) - .get() - .toPromise() - )) + .all(newOrder.map((id) => this + .apiV3Service + .work_packages + .id(id) + .get() + .toPromise())) .then((workPackages:WorkPackageResource[]) => { this.workPackages = workPackages; this.cardView.cdRef.detectChanges(); diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts index 824fa862de..bbbb69483b 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/services/wp-card-view.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @Injectable() export class WorkPackageCardViewService { @@ -24,13 +24,11 @@ export class WorkPackageCardViewService { public updateRenderedCardsValues(workPackages:WorkPackageResource[]) { this.querySpace.tableRendered.putValue( - workPackages.map((wp) => { - return { - classIdentifier: this.classIdentifier(wp), - workPackageId: wp.id, - hidden: false - }; - }) + workPackages.map((wp) => ({ + classIdentifier: this.classIdentifier(wp), + workPackageId: wp.id, + hidden: false, + })), ); } } diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts b/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts index 2a6f4921e9..db9ceba37d 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/wp-card-view.component.ts @@ -9,37 +9,37 @@ import { Input, OnInit, Output, - ViewChild -} from "@angular/core"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { CardHighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { StateService } from "@uirouter/core"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; + ViewChild, +} from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { CardHighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { StateService } from '@uirouter/core'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { filter, map, withLatestFrom } from 'rxjs/operators'; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { CardViewHandlerRegistry } from "core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry"; -import { WorkPackageCardViewService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service"; -import { WorkPackageCardDragAndDropService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { DeviceService } from "core-app/core/browser/device.service"; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { CardViewHandlerRegistry } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; +import { WorkPackageCardViewService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service'; +import { WorkPackageCardDragAndDropService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { DeviceService } from 'core-app/core/browser/device.service'; import { WorkPackageViewHandlerToken, - WorkPackageViewOutputs -} from "core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; + WorkPackageViewOutputs, +} from 'core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; export type CardViewOrientation = 'horizontal'|'vertical'; @@ -47,20 +47,29 @@ export type CardViewOrientation = 'horizontal'|'vertical'; selector: 'wp-card-view', styleUrls: ['./styles/wp-card-view.component.sass', './styles/wp-card-view-horizontal.sass', './styles/wp-card-view-vertical.sass'], templateUrl: './wp-card-view.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit, WorkPackageViewOutputs { @Input('dragOutOfHandler') public canDragOutOf:(wp:WorkPackageResource) => boolean; + @Input() public dragInto:boolean; + @Input() public highlightingMode:CardHighlightingMode; + @Input() public workPackageAddedHandler:(wp:WorkPackageResource) => Promise; + @Input() public showStatusButton = true; + @Input() public showInfoButton = false; + @Input() public orientation:CardViewOrientation = 'vertical'; + /** Whether cards are removable */ @Input() public cardsRemovable = false; + /** Whether a notification box shall be shown when there are no WP to display */ @Input() public showEmptyResultsBox = false; + /** Whether on special mobile version of the cards shall be shown */ @Input() public shrinkOnMobile = false; @@ -68,53 +77,64 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements @ViewChild('container', { static: true }) public container:ElementRef; @Output() public onMoved = new EventEmitter(); + @Output() selectionChanged = new EventEmitter(); + @Output() itemClicked = new EventEmitter<{ workPackageId:string, double:boolean }>(); + @Output() stateLinkClicked = new EventEmitter<{ workPackageId:string, requestedState:string }>(); public trackByHref = AngularTrackingHelpers.trackByHrefAndProperty('lockVersion'); + public query:QueryResource; + public isResultEmpty = false; + public columns:QueryColumn[]; + public text = { removeCard: this.I18n.t('js.card.remove_from_list'), addNewCard: this.I18n.t('js.card.add_new'), noResults: { title: this.I18n.t('js.work_packages.no_results.title'), - description: this.I18n.t('js.work_packages.no_results.description') + description: this.I18n.t('js.work_packages.no_results.description'), }, }; /** Inline create / reference properties */ public canAdd = false; + public canReference = false; + public inReference = false; + public referenceClass = this.wpInlineCreate.referenceComponentClass; + // We need to mount a dynamic component into the view // but map the following output public referenceOutputs = { onCancel: () => this.setReferenceMode(false), - onReferenced: (wp:WorkPackageResource) => this.cardDragDrop.addWorkPackageToQuery(wp, 0) + onReferenced: (wp:WorkPackageResource) => this.cardDragDrop.addWorkPackageToQuery(wp, 0), }; constructor(readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly injector:Injector, - readonly $state:StateService, - readonly I18n:I18nService, - readonly wpCreate:WorkPackageCreateService, - readonly wpInlineCreate:WorkPackageInlineCreateService, - readonly notificationService:WorkPackageNotificationService, - readonly halEvents:HalEventsService, - readonly authorisationService:AuthorisationService, - readonly causedUpdates:CausedUpdatesService, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - readonly wpTableSelection:WorkPackageViewSelectionService, - readonly wpViewOrder:WorkPackageViewOrderService, - readonly cardView:WorkPackageCardViewService, - readonly cardDragDrop:WorkPackageCardDragAndDropService, - readonly deviceService:DeviceService) { + readonly states:States, + readonly injector:Injector, + readonly $state:StateService, + readonly I18n:I18nService, + readonly wpCreate:WorkPackageCreateService, + readonly wpInlineCreate:WorkPackageInlineCreateService, + readonly notificationService:WorkPackageNotificationService, + readonly halEvents:HalEventsService, + readonly authorisationService:AuthorisationService, + readonly causedUpdates:CausedUpdatesService, + readonly cdRef:ChangeDetectorRef, + readonly pathHelper:PathHelperService, + readonly wpTableSelection:WorkPackageViewSelectionService, + readonly wpViewOrder:WorkPackageViewOrderService, + readonly cardView:WorkPackageCardViewService, + readonly cardDragDrop:WorkPackageCardDragAndDropService, + readonly deviceService:DeviceService) { super(); } @@ -134,13 +154,13 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements this.halEvents .aggregated$('WorkPackage') .pipe( - map(events => events.filter(event => event.eventType === 'updated')), - filter(events => { - const wpIds:string[] = this.workPackages.map(el => el.id!.toString()); - return !!events.find(event => wpIds.indexOf(event.id) !== -1); - }) + map((events) => events.filter((event) => event.eventType === 'updated')), + filter((events) => { + const wpIds:string[] = this.workPackages.map((el) => el.id!.toString()); + return !!events.find((event) => wpIds.indexOf(event.id) !== -1); + }), ).subscribe(() => { - this.workPackages = this.workPackages.map(wp => this.states.workPackages.get(wp.id!).getValueOr(wp)); + this.workPackages = this.workPackages.map((wp) => this.states.workPackages.get(wp.id!).getValueOr(wp)); this.cdRef.detectChanges(); }); @@ -173,9 +193,7 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements } else { new registry(this.injector).attachTo(this); } - this.wpTableSelection.registerSelectAllListener(() => { - return this.cardView.renderedCards; - }); + this.wpTableSelection.registerSelectAllListener(() => this.cardView.renderedCards); this.wpTableSelection.registerDeselectAllListener(); } @@ -211,7 +229,7 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements public classes() { let classes = 'wp-cards-container '; - classes += '-' + this.orientation; + classes += `-${this.orientation}`; classes += this.shrinkOnMobile ? ' -shrink' : ''; return classes; @@ -225,7 +243,7 @@ export class WorkPackageCardViewComponent extends UntilDestroyedMixin implements this.wpCreate .onNewWorkPackage() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(async (wp:WorkPackageResource) => { this.onCardSaved(wp); diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts b/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts index 8711b11daf..b2e8913550 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts @@ -5,55 +5,63 @@ import { EventEmitter, Input, OnInit, - Output -} from "@angular/core"; -import { checkedClassName, uiStateLinkClass } from "core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { StateService } from "@uirouter/core"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageCardViewService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { CardHighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { CardViewOrientation } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { LinkHandling } from "core-app/shared/helpers/link-handling/link-handling"; + Output, +} from '@angular/core'; +import { checkedClassName, uiStateLinkClass } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { StateService } from '@uirouter/core'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageCardViewService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CardHighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { CardViewOrientation } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; @Component({ selector: 'wp-single-card', styleUrls: ['./wp-single-card.component.sass'], templateUrl: './wp-single-card.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public showInfoButton = false; + @Input() public showStatusButton = true; + @Input() public showRemoveButton = false; + @Input() public highlightingMode:CardHighlightingMode = 'inline'; + @Input() public draggable = false; + @Input() public orientation:CardViewOrientation = 'vertical'; + @Input() public shrinkOnMobile = false; @Output() onRemove = new EventEmitter(); + @Output() stateLinkClicked = new EventEmitter<{ workPackageId:string, requestedState:string }>(); public uiStateLinkClass:string = uiStateLinkClass; public text = { removeCard: this.I18n.t('js.card.remove_from_list'), - detailsView: this.I18n.t('js.button_open_details') + detailsView: this.I18n.t('js.button_open_details'), }; constructor(readonly pathHelper:PathHelperService, - readonly I18n:I18nService, - readonly $state:StateService, - readonly wpTableSelection:WorkPackageViewSelectionService, - readonly wpTableFocus:WorkPackageViewFocusService, - readonly cardView:WorkPackageCardViewService, - readonly cdRef:ChangeDetectorRef) { + readonly I18n:I18nService, + readonly $state:StateService, + readonly wpTableSelection:WorkPackageViewSelectionService, + readonly wpTableFocus:WorkPackageViewFocusService, + readonly cardView:WorkPackageCardViewService, + readonly cdRef:ChangeDetectorRef) { super(); } @@ -61,7 +69,7 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen // Update selection state this.wpTableSelection.live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.cdRef.detectChanges(); @@ -73,7 +81,7 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen } public emitStateLinkClicked(event:MouseEvent, wp:WorkPackageResource, detail?:boolean):void { - if (LinkHandling.isClickedWithModifier(event)) { + if (isClickedWithModifier(event)) { return; } @@ -82,15 +90,15 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen this.wpTableSelection.setSelection(wp.id!, this.cardView.findRenderedCard(classIdentifier)); this.wpTableFocus.updateFocus(wp.id!); - this.stateLinkClicked.emit({ workPackageId:wp.id!, requestedState: stateToEmit }); + this.stateLinkClicked.emit({ workPackageId: wp.id!, requestedState: stateToEmit }); } public cardClasses() { let classes = this.isSelected(this.workPackage) ? checkedClassName : ''; classes += this.draggable ? ' -draggable' : ''; classes += this.workPackage.isNew ? ' -new' : ''; - classes += ' wp-card-' + this.workPackage.id; - classes += ' -' + this.orientation; + classes += ` wp-card-${this.workPackage.id}`; + classes += ` -${this.orientation}`; classes += this.shrinkOnMobile ? ' -shrink' : ''; return classes; } @@ -128,7 +136,7 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen } public bcfSnapshotPath(wp:WorkPackageResource) { - return wp.bcfViewpoints && wp.bcfViewpoints.length > 0 ? wp.bcfViewpoints[0].href + '/snapshot' : null; + return wp.bcfViewpoints && wp.bcfViewpoints.length > 0 ? `${wp.bcfViewpoints[0].href}/snapshot` : null; } private isSelected(wp:WorkPackageResource):boolean { diff --git a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-full-view.component.ts b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-full-view.component.ts index 02a39ae8d8..b1a7f34c51 100644 --- a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-full-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-full-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -31,11 +31,10 @@ import { WorkPackageCopyController } from 'core-app/features/work-packages/compo @Component({ selector: 'wp-copy-full-view', - host: { 'class': 'work-packages-page--ui-view' }, + host: { class: 'work-packages-page--ui-view' }, changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: '../wp-new/wp-new-full-view.html' + templateUrl: '../wp-new/wp-new-full-view.html', }) export class WorkPackageCopyFullViewComponent extends WorkPackageCopyController { public successState = 'work-packages.show'; } - diff --git a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-split-view.component.ts b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-split-view.component.ts index 4217855606..5ca78f98f1 100644 --- a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-split-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy-split-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,13 +27,12 @@ //++ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { WorkPackageCopyController } from "core-app/features/work-packages/components/wp-copy/wp-copy.controller"; +import { WorkPackageCopyController } from 'core-app/features/work-packages/components/wp-copy/wp-copy.controller'; @Component({ selector: 'wp-copy-split-view', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: '../wp-new/wp-new-split-view.html' + templateUrl: '../wp-new/wp-new-split-view.html', }) export class WorkPackageCopySplitViewComponent extends WorkPackageCopyController { } - diff --git a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy.controller.ts b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy.controller.ts index 4bcbb119a0..8cfeaf8b69 100644 --- a/frontend/src/app/features/work-packages/components/wp-copy/wp-copy.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-copy/wp-copy.controller.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,24 +27,26 @@ //++ import { take } from 'rxjs/operators'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageCreateComponent } from 'core-app/features/work-packages/components/wp-new/wp-create.component'; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { Directive } from "@angular/core"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { Directive } from '@angular/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Directive() export class WorkPackageCopyController extends WorkPackageCreateComponent { private __initialized_at:number; + private copiedWorkPackageId:string; /** Are we in the copying substates ? */ public copying = true; @InjectField() wpRelations:WorkPackageRelationsService; + @InjectField() halEditing:HalResourceEditingService; ngOnInit() { @@ -52,7 +54,7 @@ export class WorkPackageCopyController extends WorkPackageCreateComponent { this.wpCreate.onNewWorkPackage() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { if (wp.__initialized_at === this.__initialized_at) { @@ -70,7 +72,7 @@ export class WorkPackageCopyController extends WorkPackageCreateComponent { .id(this.copiedWorkPackageId) .get() .pipe( - take(1) + take(1), ) .subscribe((wp:WorkPackageResource) => { this.createCopyFrom(wp).then(resolve, reject); @@ -83,11 +85,11 @@ export class WorkPackageCopyController extends WorkPackageCreateComponent { } private createCopyFrom(wp:WorkPackageResource) { - const sourceChangeset = this.halEditing.changeFor(wp) as WorkPackageChangeset; + const sourceChangeset:WorkPackageChangeset = this.halEditing.changeFor(wp); return this.wpCreate .copyWorkPackage(sourceChangeset) - .then((copyChangeset:WorkPackageChangeset) => { + .then((copyChangeset) => { this.__initialized_at = copyChangeset.pristineResource.__initialized_at; this diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts index 261233ec6f..778698952d 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,36 +26,43 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ApplicationRef, ChangeDetectorRef, Component, ElementRef, OnInit } from '@angular/core'; +import { + ApplicationRef, ChangeDetectorRef, Component, ElementRef, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; - export const customDateActionAdminSelector = 'custom-date-action-admin'; @Component({ selector: customDateActionAdminSelector, - templateUrl: './custom-date-action-admin.html' + templateUrl: './custom-date-action-admin.html', }) export class CustomDateActionAdminComponent implements OnInit { public valueVisible = false; + public fieldName:string; + public fieldValue:string; + public visibleValue:string; + public selectedOperator:any; private onKey = 'on'; + private currentKey = 'current'; + private currentFieldValue = '%CURRENT_DATE%'; public operators = [ { key: this.onKey, label: this.I18n.t('js.custom_actions.date.specific') }, - { key: this.currentKey, label: this.I18n.t('js.custom_actions.date.current_date') } + { key: this.currentKey, label: this.I18n.t('js.custom_actions.date.current_date') }, ]; constructor(private elementRef:ElementRef, - private cdRef:ChangeDetectorRef, - public appRef:ApplicationRef, - private I18n:I18nService) { + private cdRef:ChangeDetectorRef, + public appRef:ApplicationRef, + private I18n:I18nService) { } // cannot use $onInit as it would be called before the operators gets filled @@ -103,5 +110,3 @@ export class CustomDateActionAdminComponent implements OnInit { this.cdRef.detectChanges(); } } - - diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts index f18a3535fd..9d3e0e7d9b 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CustomActionResource } from "core-app/features/hal/resources/custom-action-resource"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CustomActionResource } from 'core-app/features/hal/resources/custom-action-resource'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Component({ selector: 'wp-custom-actions', templateUrl: './wp-custom-actions.component.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WpCustomActionsComponent extends UntilDestroyedMixin implements OnInit { - @Input() workPackage:WorkPackageResource; actions:CustomActionResource[] = []; constructor(readonly apiV3Service:APIV3Service, - readonly cdRef:ChangeDetectorRef) { + readonly cdRef:ChangeDetectorRef) { super(); } @@ -55,12 +56,11 @@ export class WpCustomActionsComponent extends UntilDestroyedMixin implements OnI .id(this.workPackage.id!) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp) => { this.actions = wp.customActions ? [...wp.customActions] : []; this.cdRef.detectChanges(); }); } - } diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts index efbb9c16d7..7bd74578c5 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/wp-custom-actions/wp-custom-action.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,33 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - import { Component, HostListener, Input } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CustomActionResource } from "core-app/features/hal/resources/custom-action-resource"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CustomActionResource } from 'core-app/features/hal/resources/custom-action-resource'; @Component({ selector: 'wp-custom-action', - templateUrl: './wp-custom-action.component.html' + templateUrl: './wp-custom-action.component.html', }) export class WpCustomActionComponent { - @Input() workPackage:WorkPackageResource; + @Input() action:CustomActionResource; constructor(private halResourceService:HalResourceService, - private apiV3Service:APIV3Service, - private wpSchemaCacheService:SchemaCacheService, - private wpActivity:WorkPackagesActivityService, - private notificationService:WorkPackageNotificationService, - private halEditing:HalResourceEditingService, - private halEvents:HalEventsService) { + private apiV3Service:APIV3Service, + private wpSchemaCacheService:SchemaCacheService, + private wpActivity:WorkPackagesActivityService, + private notificationService:WorkPackageNotificationService, + private halEditing:HalResourceEditingService, + private halEvents:HalEventsService) { } private fetchAction() { @@ -69,26 +68,26 @@ export class WpCustomActionComponent { lockVersion: this.workPackage.lockVersion, _links: { workPackage: { - href: this.workPackage.href - } - } + href: this.workPackage.href, + }, + }, }; this.halResourceService - .post(this.action.href + '/execute', payload) + .post(`${this.action.href}/execute`, payload) .subscribe( (savedWp:WorkPackageResource) => { this.notificationService.showSave(savedWp, false); this.workPackage = savedWp; - this.wpActivity.clear(this.workPackage.id!); + this.wpActivity.clear(this.workPackage.id); // Loading the schema might be necessary in cases where the button switches // project or type. this.apiV3Service.work_packages.cache.updateWorkPackage(savedWp).then(() => { this.halEditing.stopEditing(savedWp); - this.halEvents.push(savedWp, { eventType: "updated" }); + this.halEvents.push(savedWp, { eventType: 'updated' }); }); }, - (errorResource:any) => this.notificationService.handleRawError(errorResource, this.workPackage) + (errorResource:any) => this.notificationService.handleRawError(errorResource, this.workPackage), ); } @@ -96,4 +95,3 @@ export class WpCustomActionComponent { this.fetchAction(); } } - diff --git a/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts b/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts index b03994baa0..97454edf3d 100644 --- a/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-details/wp-details-toolbar.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,22 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Component, Input } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ selector: 'wp-details-toolbar', - templateUrl: './wp-details-toolbar.html' + templateUrl: './wp-details-toolbar.html', }) export class WorkPackageSplitViewToolbarComponent { @Input('workPackage') workPackage:WorkPackageResource; public text = { - button_more: this.I18n.t('js.button_more') + button_more: this.I18n.t('js.button_more'), }; constructor(readonly I18n:I18nService, - readonly halEditing:HalResourceEditingService) {} + readonly halEditing:HalResourceEditingService) {} } diff --git a/frontend/src/app/features/work-packages/components/wp-edit-form/table-edit-form.ts b/frontend/src/app/features/work-packages/components/wp-edit-form/table-edit-form.ts index 9f763e0518..ee76641264 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit-form/table-edit-form.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit-form/table-edit-form.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,28 +29,32 @@ import { Injector } from '@angular/core'; import { Subscription } from 'rxjs'; import { States } from 'core-app/core/states/states.service'; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; - -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { FocusHelperService } from "core-app/shared/directives/focus/focus-helper"; -import { EditingPortalService } from "core-app/shared/components/fields/edit/editing-portal/editing-portal-service"; -import { CellBuilder, editCellContainer, tdClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/cell-builder"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { editModeClassName } from "core-app/shared/components/fields/edit/edit-field.component"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; + +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; +import { EditingPortalService } from 'core-app/shared/components/fields/edit/editing-portal/editing-portal-service'; +import { CellBuilder, editCellContainer, tdClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/cell-builder'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { editModeClassName } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export const activeFieldContainerClassName = 'inline-edit--active-field'; export const activeFieldClassName = 'inline-edit--field'; export class TableEditForm extends EditForm { @InjectField() public wpTableColumns:WorkPackageViewColumnsService; + @InjectField() public apiV3Service!:APIV3Service; + @InjectField() public states:States; + @InjectField() public FocusHelper:FocusHelperService; + @InjectField() public editingPortalService:EditingPortalService; // Use cell builder to reset edit fields @@ -65,9 +69,9 @@ export class TableEditForm extends EditForm { .subscribe((wp) => this.resource = wp); constructor(public injector:Injector, - public table:WorkPackageTable, - public workPackageId:string, - public classIdentifier:string) { + public table:WorkPackageTable, + public workPackageId:string, + public classIdentifier:string) { super(injector); } @@ -86,7 +90,6 @@ export class TableEditForm extends EditForm { public activateField(form:EditForm, schema:IFieldSchema, fieldName:string, errors:string[]):Promise { return this.waitForContainer(fieldName) .then((cell) => { - // Forcibly set the width since the edit field may otherwise // be given more width. Thereby preserve a minimum width of 150. // To avoid flickering content, the padding is removed, too. @@ -94,8 +97,8 @@ export class TableEditForm extends EditForm { td.addClass(editModeClassName); let width = parseInt(td.css('width')); width = width > 150 ? width - 10 : 150; - td.css('max-width', width + 'px'); - td.css('width', width + 'px'); + td.css('max-width', `${width}px`); + td.css('width', `${width}px`); return this.editingPortalService.create( cell, @@ -103,7 +106,7 @@ export class TableEditForm extends EditForm { form, schema, fieldName, - errors + errors, ); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.spec.ts b/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.spec.ts index dda23ca03b..68feca96c6 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,34 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TestBed } from "@angular/core/testing"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { Injector } from "@angular/core"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { WorkPackageFilterValues } from "core-app/features/work-packages/components/wp-edit-form/work-package-filter-values"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackagesActivityService } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { TypeResource } from "core-app/features/hal/resources/type-resource"; -import { HttpClientModule } from "@angular/common/http"; -import { States } from "core-app/core/states/states.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { UIRouterModule } from "@uirouter/angular"; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; -import { HookService } from "core-app/features/plugins/hook-service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { OpenProjectFileUploadService } from "core-app/core/file-upload/op-file-upload.service"; -import { OpenProjectDirectFileUploadService } from "core-app/core/file-upload/op-direct-file-upload.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { TestBed } from '@angular/core/testing'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { Injector } from '@angular/core'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { WorkPackageFilterValues } from 'core-app/features/work-packages/components/wp-edit-form/work-package-filter-values'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { TypeResource } from 'core-app/features/hal/resources/type-resource'; +import { HttpClientModule } from '@angular/common/http'; +import { States } from 'core-app/core/states/states.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { UIRouterModule } from '@uirouter/angular'; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { OpenProjectFileUploadService } from 'core-app/core/file-upload/op-file-upload.service'; +import { OpenProjectDirectFileUploadService } from 'core-app/core/file-upload/op-direct-file-upload.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; describe('WorkPackageFilterValues', () => { let resource:WorkPackageResource; @@ -70,7 +70,7 @@ describe('WorkPackageFilterValues', () => { TestBed.configureTestingModule({ imports: [ UIRouterModule.forRoot({}), - HttpClientModule + HttpClientModule, ], providers: [ I18nService, @@ -93,7 +93,7 @@ describe('WorkPackageFilterValues', () => { WorkPackageCreateService, HalResourceEditingService, WorkPackagesActivityService, - ] + ], }).compileComponents(); injector = TestBed.inject(Injector); @@ -104,19 +104,19 @@ describe('WorkPackageFilterValues', () => { const type1 = halResourceService.createHalResourceOfClass( TypeResource, - { _type: 'Type', id: '1', _links: { self: { href: '/api/v3/types/1', name: 'Task' } } } + { _type: 'Type', id: '1', _links: { self: { href: '/api/v3/types/1', name: 'Task' } } }, ); const type2 = halResourceService.createHalResourceOfClass( TypeResource, - { _type: 'Type', id: '2', _links: { self: { href: '/api/v3/types/2', name: 'Bug' } } } + { _type: 'Type', id: '2', _links: { self: { href: '/api/v3/types/2', name: 'Bug' } } }, ); filters = [ { id: 'type', operator: { id: '=' }, - values: [type1, type2] - } + values: [type1, type2], + }, ]; subject = new WorkPackageFilterValues(injector, filters); @@ -131,9 +131,9 @@ describe('WorkPackageFilterValues', () => { _links: { type: { href: '/api/v3/types/1', - name: 'Task' - } - } + name: 'Task', + }, + }, }; setupTestBed(); @@ -154,9 +154,9 @@ describe('WorkPackageFilterValues', () => { _links: { type: { href: '/api/v3/types/2', - name: 'Bug' - } - } + name: 'Bug', + }, + }, }; setupTestBed(); @@ -169,9 +169,5 @@ describe('WorkPackageFilterValues', () => { expect(changeset.value('type').href).toEqual('/api/v3/types/2'); })); }); - }); }); - - - diff --git a/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.ts b/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.ts index 12af59c996..e883f1ad30 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit-form/work-package-filter-values.ts @@ -1,32 +1,32 @@ -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { Injector } from '@angular/core'; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import compareByHrefOrString = AngularTrackingHelpers.compareByHrefOrString; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; export class WorkPackageFilterValues { - @InjectField() currentUser:CurrentUserService; + @InjectField() halResourceService:HalResourceService; handlers:Partial void>> = { '=': this.applyFirstValue.bind(this), - '!*': this.setToNull.bind(this) + '!*': this.setToNull.bind(this), }; constructor(public injector:Injector, - private filters:QueryFilterInstanceResource[], - private excluded:string[] = []) { + private filters:QueryFilterInstanceResource[], + private excluded:string[] = []) { } public applyDefaultsFromFilters(change:WorkPackageChangeset|Object) { - _.each(this.filters, filter => { + _.each(this.filters, (filter) => { // Exclude filters specified in constructor if (this.excluded.indexOf(filter.id) !== -1) { return; @@ -47,7 +47,7 @@ export class WorkPackageFilterValues { * @param filter A positive '=' filter with at least one value * @private */ - private applyFirstValue(change:WorkPackageChangeset|{[id:string]:any}, filter:QueryFilterInstanceResource):void { + private applyFirstValue(change:WorkPackageChangeset|{ [id:string]:any }, filter:QueryFilterInstanceResource):void { // Avoid setting a value if current value is in filter list // and more than one value selected if (this.filterAlreadyApplied(change, filter)) { @@ -70,10 +70,10 @@ export class WorkPackageFilterValues { * @param filter A none '!*' filter * @private */ - private setToNull(change:WorkPackageChangeset|{[id:string]:any}, filter:QueryFilterInstanceResource):void { + private setToNull(change:WorkPackageChangeset|{ [id:string]:any }, filter:QueryFilterInstanceResource):void { const attributeName = this.mapFilterToAttribute(filter); - this.setValue(change, attributeName,{ href: null }); + this.setValue(change, attributeName, { href: null }); } private setValueFor(change:WorkPackageChangeset|Object, field:string, value:string|HalResource) { @@ -84,7 +84,7 @@ export class WorkPackageFilterValues { } } - private setValue(change:WorkPackageChangeset|{[id:string]:any}, field:string, value:any) { + private setValue(change:WorkPackageChangeset|{ [id:string]:any }, field:string, value:any) { if (change instanceof WorkPackageChangeset) { change.setValue(field, value); } else { @@ -113,7 +113,7 @@ export class WorkPackageFilterValues { * Avoid applying filter values when changeset already matches one of the selected values * @param filter */ - private filterAlreadyApplied(change:WorkPackageChangeset|{[id:string]:any}, filter:any):boolean { + private filterAlreadyApplied(change:WorkPackageChangeset|{ [id:string]:any }, filter:any):boolean { let current = change instanceof WorkPackageChangeset ? change.projectedResource[filter.id] : change[filter.id]; current = _.castArray(current); diff --git a/frontend/src/app/features/work-packages/components/wp-edit/work-package-changeset.ts b/frontend/src/app/features/work-packages/components/wp-edit/work-package-changeset.ts index 89fd89aa17..dbf558329f 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit/work-package-changeset.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit/work-package-changeset.ts @@ -1,10 +1,9 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { WorkPackageSchemaProxy } from "core-app/features/hal/schemas/work-package-schema-proxy"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { WorkPackageSchemaProxy } from 'core-app/features/hal/schemas/work-package-schema-proxy'; export class WorkPackageChangeset extends ResourceChangeset { - public setValue(key:string, val:any) { super.setValue(key, val); @@ -41,8 +40,7 @@ export class WorkPackageChangeset extends ResourceChangeset public get schema():SchemaResource { if (this.form$.hasValue()) { return WorkPackageSchemaProxy.create(super.schema, this.projectedResource); - } else { - return super.schema; } + return super.schema; } } diff --git a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts index 478834d7b2..b7daa34357 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Component, ElementRef, Input, OnInit } from '@angular/core'; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; +import { + Component, ElementRef, Input, OnInit, +} from '@angular/core'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @Component({ selector: 'wp-replacement-label', - templateUrl: './wp-replacement-label.html' + templateUrl: './wp-replacement-label.html', }) export class WorkPackageReplacementLabelComponent implements OnInit { @Input('fieldName') public fieldName:string; + private $element:JQuery; constructor(protected wpeditForm:EditFormComponent, - protected elementRef:ElementRef) { + protected elementRef:ElementRef) { } ngOnInit() { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/cell-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/cell-builder.ts index 30416b10ea..e4c4904912 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/cell-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/cell-builder.ts @@ -1,17 +1,17 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { DisplayFieldRenderer, - editFieldContainerClass -} from "core-app/shared/components/fields/display/display-field-renderer"; + editFieldContainerClass, +} from 'core-app/shared/components/fields/display/display-field-renderer'; import { Injector } from '@angular/core'; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; + export const tdClassName = 'wp-table--cell-td'; export const editCellContainer = 'wp-table--cell-container'; export class CellBuilder { - @InjectField(SchemaCacheService) schemaCache:SchemaCacheService; public fieldRenderer = new DisplayFieldRenderer(this.injector, 'table'); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder.ts index 8a40955f6b..a061749cc1 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder.ts @@ -1,15 +1,14 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { tdClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/cell-builder"; -import { Injector } from "@angular/core"; -import { TableDragActionsRegistryService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service"; -import { TableDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service"; -import { internalSortColumn } from "core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { tdClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/cell-builder'; +import { Injector } from '@angular/core'; +import { TableDragActionsRegistryService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service'; +import { TableDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service'; +import { internalSortColumn } from 'core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns'; /** Debug the render position */ const RENDER_DRAG_AND_DROP_POSITION = false; export class DragDropHandleBuilder { - // Injections private actionService:TableDragActionService; @@ -31,7 +30,7 @@ export class DragDropHandleBuilder { return td; } - td.classList.add('wp-table--sort-td', internalSortColumn.id, 'hide-when-print'); + td.classList.add('wp-table--sort-td', internalSortColumn.id, 'hide-when-print'); // Wrap handle as span const span = document.createElement('span'); @@ -40,7 +39,7 @@ export class DragDropHandleBuilder { if (RENDER_DRAG_AND_DROP_POSITION) { const text = document.createElement('span'); - text.textContent = '' + position; + text.textContent = `${position}`; td.appendChild(text); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass.ts index efd5e42b6b..a92d114c2a 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass.ts @@ -1,23 +1,23 @@ import { Injector } from '@angular/core'; +import { DragDropHandleBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { QueryOrder } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-order'; import { PrimaryRenderPass, RowRenderInfo } from '../primary-render-pass'; -import { DragDropHandleBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-builder"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { QueryOrder } from "core-app/core/apiv3/endpoints/queries/apiv3-query-order"; export class DragDropHandleRenderPass { - @InjectField() public wpTableColumns:WorkPackageViewColumnsService; + @InjectField() public wpTableOrder:WorkPackageViewOrderService; // Drag & Drop handle builder protected dragDropHandleBuilder = new DragDropHandleBuilder(this.injector); constructor(public readonly injector:Injector, - private table:WorkPackageTable, - private tablePass:PrimaryRenderPass) { + private table:WorkPackageTable, + private tablePass:PrimaryRenderPass) { } public render() { @@ -25,7 +25,6 @@ export class DragDropHandleRenderPass { return; } - this.wpTableOrder.withLoadedPositions().then((positions:QueryOrder) => { this.tablePass.renderedOrder.forEach((row:RowRenderInfo, position:number) => { // We only care for rows that are natural work packages and are not relation sub-rows @@ -33,7 +32,7 @@ export class DragDropHandleRenderPass { return; } - const handle = this.dragDropHandleBuilder.build(row.workPackage!, positions[row.workPackage!.id!]); + const handle = this.dragDropHandleBuilder.build(row.workPackage, positions[row.workPackage.id!]); if (handle) { row.element.replaceChild(handle, row.element.firstElementChild!); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const.ts index ec08dfd651..cea438c53f 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const.ts @@ -1,3 +1,3 @@ export type HighlightingMode = 'status'|'priority'|'type'|'inline'|'none'; -export type CardHighlightingMode = 'priority'|'type'|'none'|'inline'; +export type CardHighlightingMode = 'priority'|'type'|'none'|'inline'; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions.ts index b2484dd209..91167eb217 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions.ts @@ -10,9 +10,8 @@ export namespace Highlighting { export function colorClass(highlightColorTextInline:boolean, id:string|number) { if (highlightColorTextInline) { return `__hl_inline_color_${id}_text`; - } else { - return `__hl_inline_color_${id}_dot`; } + return `__hl_inline_color_${id}_dot`; } /** diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts index 1a69c9d389..9b6e884e8f 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass.ts @@ -1,20 +1,20 @@ import { Injector } from '@angular/core'; -import { PrimaryRenderPass, RowRenderInfo } from "core-app/features/work-packages/components/wp-fast-table/builders/primary-render-pass"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { PrimaryRenderPass, RowRenderInfo } from 'core-app/features/work-packages/components/wp-fast-table/builders/primary-render-pass'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class HighlightingRenderPass { - @InjectField() wpTableHighlighting:WorkPackageViewHighlightingService; + @InjectField() querySpace:IsolatedQuerySpace; constructor(public readonly injector:Injector, - private table:WorkPackageTable, - private tablePass:PrimaryRenderPass) { + private table:WorkPackageTable, + private tablePass:PrimaryRenderPass) { } @@ -31,7 +31,6 @@ export class HighlightingRenderPass { // Render for each original row, clone it since we're modifying the tablepass this.tablePass.renderedOrder.forEach((row:RowRenderInfo, position:number) => { - // We only care for rows that are natural work packages if (!row.workPackage) { return; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns.ts index 7bfce53308..7f3e206726 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns.ts @@ -1,10 +1,9 @@ -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; export const internalSortColumn = { - id: '__internal-sorthandle' + id: '__internal-sorthandle', } as QueryColumn; export const internalContextMenuColumn = { - id: '__internal-contextMenu' + id: '__internal-contextMenu', } as QueryColumn; - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-header-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-header-builder.ts index 225cf65192..7bef6b14c8 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-header-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-header-builder.ts @@ -1,17 +1,17 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { rowGroupClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; import { groupName } from './grouped-rows-helpers'; -import { rowGroupClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; export function groupClassNameFor(group:GroupObject) { return `group-${group.identifier}`; } export class GroupHeaderBuilder { - @InjectField() public I18n:I18nService; + public text:{ collapse:string, expand:string }; constructor(public readonly injector:Injector) { @@ -23,7 +23,8 @@ export class GroupHeaderBuilder { public buildGroupRow(group:GroupObject, colspan:number) { const row = document.createElement('tr'); - let togglerIconClass, text; + let togglerIconClass; let + text; if (group.collapsed) { text = this.text.expand; @@ -35,8 +36,8 @@ export class GroupHeaderBuilder { row.classList.add(rowGroupClassName, groupClassNameFor(group)); row.id = `wp-table-rowgroup-${group.index}`; - row.dataset['groupIndex'] = (group.index).toString(); - row.dataset['groupIdentifier'] = group.identifier; + row.dataset.groupIndex = (group.index).toString(); + row.dataset.groupIdentifier = group.identifier; row.innerHTML = `
diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder.ts index 82e403d07a..0356ea7153 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder.ts @@ -1,22 +1,22 @@ - -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SingleRowBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { groupedRowClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SingleRowBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { groupedRowClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; export class GroupSumsBuilder extends SingleRowBuilder { - @InjectField() readonly querySpace:IsolatedQuerySpace; + @InjectField() readonly schemaCache:SchemaCacheService; + @InjectField() readonly displayFieldService:DisplayFieldService; public text = { - sum: this.I18n.t('js.label_sum') + sum: this.I18n.t('js.label_sum'), }; public buildSumsRow(group:GroupObject) { @@ -28,7 +28,7 @@ export class GroupSumsBuilder extends SingleRowBuilder { return tr; } - public renderColumns(sums:{[key:string]:any}, tr:HTMLTableRowElement) { + public renderColumns(sums:{ [key:string]:any }, tr:HTMLTableRowElement) { this.augmentedColumns.forEach((column, i:number) => { const td = document.createElement('td'); const div = this.renderContent(sums, column.id, this.sumsSchema[column.id]); @@ -71,7 +71,7 @@ export class GroupSumsBuilder extends SingleRowBuilder { sums, name, fieldSchema, - { injector: this.injector, container: 'table', options: {} } + { injector: this.injector, container: 'table', options: {} }, ); if (!field.isEmpty()) { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-render-pass.ts index 035f5ceaef..e608d99812 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-render-pass.ts @@ -1,28 +1,26 @@ import { Injector } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { collapsedRowClass } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants'; +import { GroupSumsBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder'; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; import { WorkPackageTable } from '../../../wp-fast-table'; import { WorkPackageTableRow } from '../../../wp-table.interfaces'; import { SingleRowBuilder } from '../../rows/single-row-builder'; import { PlainRenderPass } from '../plain/plain-render-pass'; import { groupClassNameFor, GroupHeaderBuilder } from './group-header-builder'; import { groupByProperty, groupedRowClassName } from './grouped-rows-helpers'; -import { collapsedRowClass } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants"; -import { GroupSumsBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder"; -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; export const groupRowClass = '-group-row'; export class GroupedRenderPass extends PlainRenderPass { - private sumsBuilder = new GroupSumsBuilder(this.injector, this.workPackageTable); constructor(public readonly injector:Injector, - public workPackageTable:WorkPackageTable, - public groups:GroupObject[], - public headerBuilder:GroupHeaderBuilder, - public colspan:number) { - + public workPackageTable:WorkPackageTable, + public groups:GroupObject[], + public headerBuilder:GroupHeaderBuilder, + public colspan:number) { super(injector, workPackageTable, new SingleRowBuilder(injector, workPackageTable)); } @@ -31,7 +29,7 @@ export class GroupedRenderPass extends PlainRenderPass { */ protected doRender() { let currentGroup:GroupObject | null = null; - const length = this.workPackageTable.originalRows.length; + const { length } = this.workPackageTable.originalRows; this.workPackageTable.originalRows.forEach((wpId:string, index:number) => { const row = this.workPackageTable.originalRowIndex[wpId]; const nextGroup = this.matchingGroup(row.object); @@ -78,8 +76,8 @@ export class GroupedRenderPass extends PlainRenderPass { return this.matchesMultiValue(property as HalResource[], group); } - //// If its a linked resource, compare the href, - //// which is an array of links the resource offers + /// / If its a linked resource, compare the href, + /// / which is an array of links the resource offers if (property && property.href) { return !!_.find(group._links.valueLink, (l:any):any => property.href === l.href); } @@ -104,14 +102,12 @@ export class GroupedRenderPass extends PlainRenderPass { return false; } - const joinedOrderedHrefs = (objects:any[]) => { - return _.map(objects, object => object.href).sort().join(', '); - }; + const joinedOrderedHrefs = (objects:any[]) => _.map(objects, (object) => object.href).sort().join(', '); return _.isEqualWith( property, group.href, - (a, b) => joinedOrderedHrefs(a) === joinedOrderedHrefs(b) + (a, b) => joinedOrderedHrefs(a) === joinedOrderedHrefs(b), ); } @@ -119,7 +115,7 @@ export class GroupedRenderPass extends PlainRenderPass { * Enhance a row from the rowBuilder with group information. */ private buildSingleRow(row:WorkPackageTableRow):void { - const group = row.group; + const { group } = row; if (!group) { console.warn("All rows should have a group, but this one doesn't %O", row); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-builder.ts index ae5ae3a796..e1c5caed63 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-builder.ts @@ -1,27 +1,29 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { + collapsedRowClass, + rowGroupClassName, +} from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; import { WorkPackageTable } from '../../../wp-fast-table'; import { tableRowClassName } from '../../rows/single-row-builder'; import { RowsBuilder } from '../rows-builder'; import { GroupHeaderBuilder } from './group-header-builder'; import { GroupedRenderPass } from './grouped-render-pass'; import { groupedRowClassName, groupIdentifier } from './grouped-rows-helpers'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { - collapsedRowClass, - rowGroupClassName -} from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; export class GroupedRowsBuilder extends RowsBuilder { - // Injections @InjectField() private readonly querySpace:IsolatedQuerySpace; + @InjectField() public states:States; + @InjectField() public wpTableColumns:WorkPackageViewColumnsService; + @InjectField() public I18n:I18nService; constructor(public readonly injector:Injector, workPackageTable:WorkPackageTable) { @@ -61,7 +63,7 @@ export class GroupedRowsBuilder extends RowsBuilder { this.workPackageTable, this.getGroupData(), builder, - this.colspan + this.colspan, ).render(); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers.ts index 9a11bb3d54..cae7ebb269 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers.ts @@ -1,13 +1,13 @@ -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; export function groupIdentifier(group:GroupObject) { let value = group.value || 'nullValue'; if (group.href) { try { - value += group.href.map(el => el.href).join('-'); + value += group.href.map((el) => el.href).join('-'); } catch (e) { - console.error('Failed to extract group identifier for ' + group.value); + console.error(`Failed to extract group identifier for ${group.value}`); } } @@ -16,12 +16,11 @@ export function groupIdentifier(group:GroupObject) { } export function groupName(group:GroupObject) { - const value = group.value; + const { value } = group; if (value === null) { return '-'; - } else { - return value; } + return value; } export function groupByProperty(group:GroupObject):string { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass.ts index 64309aec81..ceb4c73ec9 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass.ts @@ -1,25 +1,27 @@ import { Injector } from '@angular/core'; -import { additionalHierarchyRowClassName, SingleHierarchyRowBuilder } from './single-hierarchy-row-builder'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { PrimaryRenderPass, RowRenderInfo } from "core-app/features/work-packages/components/wp-fast-table/builders/primary-render-pass"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { WorkPackageTableRow } from "core-app/features/work-packages/components/wp-fast-table/wp-table.interfaces"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { PrimaryRenderPass, RowRenderInfo } from 'core-app/features/work-packages/components/wp-fast-table/builders/primary-render-pass'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { WorkPackageTableRow } from 'core-app/features/work-packages/components/wp-fast-table/wp-table.interfaces'; import { ancestorClassIdentifier, - hierarchyGroupClass -} from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; -import { WorkPackageViewHierarchies } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; + hierarchyGroupClass, +} from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; +import { WorkPackageViewHierarchies } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { additionalHierarchyRowClassName, SingleHierarchyRowBuilder } from './single-hierarchy-row-builder'; export class HierarchyRenderPass extends PrimaryRenderPass { - @InjectField() querySpace:IsolatedQuerySpace; + @InjectField() states:States; + @InjectField() apiV3Service:APIV3Service; + @InjectField() wpTableHierarchies:WorkPackageViewHierarchiesService; // Remember which rows were already rendered @@ -39,8 +41,8 @@ export class HierarchyRenderPass extends PrimaryRenderPass { public parentsWithVisibleChildren:{ [id:string]:boolean } = {}; constructor(public readonly injector:Injector, - public workPackageTable:WorkPackageTable, - public rowBuilder:SingleHierarchyRowBuilder) { + public workPackageTable:WorkPackageTable, + public rowBuilder:SingleHierarchyRowBuilder) { super(injector, workPackageTable, rowBuilder); } @@ -49,7 +51,7 @@ export class HierarchyRenderPass extends PrimaryRenderPass { this.hierarchies = this.wpTableHierarchies.current; - _.each(this.workPackageTable.originalRowIndex, (row, ) => { + _.each(this.workPackageTable.originalRowIndex, (row) => { row.object.ancestors.forEach((ancestor:WorkPackageResource) => { this.parentsWithVisibleChildren[ancestor.id!] = true; }); @@ -94,7 +96,7 @@ export class HierarchyRenderPass extends PrimaryRenderPass { * @returns {boolean} */ public deferInsertion(workPackage:WorkPackageResource):boolean { - const ancestors = workPackage.ancestors; + const { ancestors } = workPackage; // Will only defer if at least one ancestor exists if (ancestors.length === 0) { @@ -126,11 +128,11 @@ export class HierarchyRenderPass extends PrimaryRenderPass { let elements = this.deferred[parent.id!] || []; // Append to them the child and all children below let newElements:WorkPackageResource[] = ancestorChain.slice(i + 1, ancestorChain.length); - newElements = newElements.map(child => this.apiV3Service.work_packages.cache.state(child.id!).value!); + newElements = newElements.map((child) => this.apiV3Service.work_packages.cache.state(child.id!).value!); // Append all new elements elements = elements.concat(newElements); // Remove duplicates (Regression #29652) - this.deferred[parent.id!] = _.uniqBy(elements, el => el.id!); + this.deferred[parent.id!] = _.uniqBy(elements, (el) => el.id!); return true; } // Otherwise, continue the chain upwards @@ -139,7 +141,6 @@ export class HierarchyRenderPass extends PrimaryRenderPass { return false; } - /** * Render any deferred children of the given work package. If recursive children were * deferred, each of them will be passed through renderCallback. @@ -171,7 +172,7 @@ export class HierarchyRenderPass extends PrimaryRenderPass { private buildWithHierarchy(row:WorkPackageTableRow) { // Ancestor data [root, med, thisrow] - const ancestors = row.object.ancestors; + const { ancestors } = row.object; const ancestorGroups:string[] = []; // Iterate ancestors @@ -250,7 +251,7 @@ export class HierarchyRenderPass extends PrimaryRenderPass { this.spliceRow( el, `${hierarchyRoot},${hierarchyGroup}`, - this.buildRenderInfo(el, workPackage, hidden, isAncestor) + this.buildRenderInfo(el, workPackage, hidden, isAncestor), ); this.rendered[workPackage.id!] = true; @@ -261,9 +262,9 @@ export class HierarchyRenderPass extends PrimaryRenderPass { element: row, classIdentifier: '', additionalClasses: [], - workPackage: workPackage, + workPackage, renderType: 'primary', - hidden: hidden + hidden, }; const [ancestorClasses, _] = this.rowBuilder.ancestorRowData(workPackage); @@ -276,6 +277,6 @@ export class HierarchyRenderPass extends PrimaryRenderPass { info.classIdentifier = this.rowBuilder.classIdentifier(workPackage); } - return info as RowRenderInfo; + return info; } } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-rows-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-rows-builder.ts index 1f5e7bd01b..604ffe1630 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-rows-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-rows-builder.ts @@ -1,18 +1,19 @@ import { Injector } from '@angular/core'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; import { WorkPackageTable } from '../../../wp-fast-table'; import { RowsBuilder } from '../rows-builder'; import { HierarchyRenderPass } from './hierarchy-render-pass'; import { SingleHierarchyRowBuilder } from './single-hierarchy-row-builder'; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; export class HierarchyRowsBuilder extends RowsBuilder { - // Injections @InjectField() states:States; + @InjectField() wpTableColumns:WorkPackageViewColumnsService; + @InjectField() wpTableHierarchies:WorkPackageViewHierarchiesService; // The group expansion state diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder.ts index f243dd73fc..7f023687a5 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder.ts @@ -1,15 +1,15 @@ import { Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { SingleRowBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { States } from "core-app/core/states/states.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { SingleRowBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { States } from 'core-app/core/states/states.service'; import { collapsedGroupClass, hierarchyGroupClass, - hierarchyRootClass -} from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; + hierarchyRootClass, +} from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export const indicatorCollapsedClass = '-hierarchy-collapsed'; export const hierarchyCellClassName = 'wp-table--hierarchy-span'; @@ -20,6 +20,7 @@ export const hierarchyBaseIndentation = 25; export class SingleHierarchyRowBuilder extends SingleRowBuilder { // Injected @InjectField() public wpTableHierarchies:WorkPackageViewHierarchiesService; + @InjectField() public states:States; // Retain a map of hierarchy elements present in the table @@ -33,16 +34,15 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { }; constructor(public readonly injector:Injector, - protected workPackageTable:WorkPackageTable) { - + protected workPackageTable:WorkPackageTable) { super(injector, workPackageTable); this.text = { - leaf: (level:number) => this.I18n.t('js.work_packages.hierarchy.leaf', { level: level }), + leaf: (level:number) => this.I18n.t('js.work_packages.hierarchy.leaf', { level }), expanded: (level:number) => this.I18n.t('js.work_packages.hierarchy.children_expanded', - { level: level }), + { level }), collapsed: (level:number) => this.I18n.t('js.work_packages.hierarchy.children_collapsed', - { level: level }), + { level }), }; } @@ -53,7 +53,7 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { public refreshRow(workPackage:WorkPackageResource, jRow:JQuery):JQuery { // Remove any old hierarchy const newRow = super.refreshRow(workPackage, jRow); - newRow.find(`.wp-table--hierarchy-span`).remove(); + newRow.find('.wp-table--hierarchy-span').remove(); this.appendHierarchyIndicator(workPackage, newRow); return newRow; @@ -92,7 +92,6 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { hidden = true; rowClasses.push(collapsedGroupClass(ancestor.id!)); } - }); } @@ -105,7 +104,6 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { public buildAncestorRow(ancestor:WorkPackageResource, ancestorGroups:string[], index:number):[HTMLTableRowElement, boolean] { - const workPackage = this.states.workPackages.get(ancestor.id!).value!; const [tr, hidden] = this.buildEmpty(workPackage); tr.classList.add(additionalHierarchyRowClassName); @@ -127,9 +125,9 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { .prepend(hierarchyElement); // Assure that the content is still visible when the hierarchy indentation is very large - jRow.find('td.subject').css('minWidth', 125 + (hierarchyIndentation * hierarchyLevel) + 'px'); + jRow.find('td.subject').css('minWidth', `${125 + (hierarchyIndentation * hierarchyLevel)}px`); jRow.find('td.subject .wp-table--cell-container') - .css('width', 'calc(100% - ' + hierarchyBaseIndentation + 'px - ' + (hierarchyIndentation * hierarchyLevel) + 'px)'); + .css('width', `calc(100% - ${hierarchyBaseIndentation}px - ${hierarchyIndentation * hierarchyLevel}px)`); } /** @@ -138,7 +136,7 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { private buildHierarchyIndicator(workPackage:WorkPackageResource, jRow:JQuery|null, level:number):HTMLElement { const hierarchyIndicator = document.createElement('span'); const collapsed = this.wpTableHierarchies.collapsed(workPackage.id!); - const indicatorWidth = hierarchyBaseIndentation + (hierarchyIndentation * level) + 'px'; + const indicatorWidth = `${hierarchyBaseIndentation + (hierarchyIndentation * level)}px`; hierarchyIndicator.classList.add(hierarchyCellClassName); hierarchyIndicator.style.width = indicatorWidth; hierarchyIndicator.dataset.indentation = indicatorWidth; @@ -149,9 +147,11 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { ${this.text.expanded( - level)} + level, + )} ${this.text.collapsed( - level)} + level, + )} `; } else { @@ -164,5 +164,4 @@ export class SingleHierarchyRowBuilder extends SingleRowBuilder { return hierarchyIndicator; } - } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-render-pass.ts index 0a36f63558..7547a795d1 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-render-pass.ts @@ -4,11 +4,9 @@ import { PrimaryRenderPass } from '../../primary-render-pass'; import { SingleRowBuilder } from '../../rows/single-row-builder'; export class PlainRenderPass extends PrimaryRenderPass { - constructor(public readonly injector:Injector, - public workPackageTable:WorkPackageTable, - public rowBuilder:SingleRowBuilder) { - + public workPackageTable:WorkPackageTable, + public rowBuilder:SingleRowBuilder) { super(injector, workPackageTable, rowBuilder); } @@ -18,7 +16,7 @@ export class PlainRenderPass extends PrimaryRenderPass { protected doRender():void { this.workPackageTable.originalRows.forEach((wpId:string) => { const row = this.workPackageTable.originalRowIndex[wpId]; - const [tr,] = this.rowBuilder.buildEmpty(row.object); + const [tr] = this.rowBuilder.buildEmpty(row.object); row.element = tr; this.appendRow(row.object, tr); this.tableBody.appendChild(tr); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-rows-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-rows-builder.ts index cd5ff7dae1..12632f3692 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-rows-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/plain/plain-rows-builder.ts @@ -1,14 +1,13 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageTable } from '../../../wp-fast-table'; import { PrimaryRenderPass } from '../../primary-render-pass'; import { SingleRowBuilder } from '../../rows/single-row-builder'; import { RowsBuilder } from '../rows-builder'; import { PlainRenderPass } from './plain-render-pass'; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class PlainRowsBuilder extends RowsBuilder { - // Injections @InjectField() public I18n:I18nService; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/rows-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/rows-builder.ts index 81dbd1a410..b6776bf152 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/rows-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/modes/rows-builder.ts @@ -1,11 +1,10 @@ import { Injector } from '@angular/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; import { WorkPackageTable } from '../../wp-fast-table'; import { PrimaryRenderPass } from '../primary-render-pass'; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; export abstract class RowsBuilder { - @InjectField() public states:States; constructor(public readonly injector:Injector, public workPackageTable:WorkPackageTable) { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/primary-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/primary-render-pass.ts index fa878d618c..22d8a12912 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/primary-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/primary-render-pass.ts @@ -1,16 +1,16 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageTable } from '../wp-fast-table'; -import { RelationRenderInfo, RelationsRenderPass } from './relations/relations-render-pass'; -import { SingleRowBuilder } from './rows/single-row-builder'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { HighlightingRenderPass } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass'; +import { DragDropHandleRenderPass } from 'core-app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { timeOutput } from 'core-app/shared/helpers/debug_output'; import { TimelineRenderPass } from './timeline/timeline-render-pass'; -import { HighlightingRenderPass } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/row-highlight-render-pass"; -import { DragDropHandleRenderPass } from "core-app/features/work-packages/components/wp-fast-table/builders/drag-and-drop/drag-drop-handle-render-pass"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { timeOutput } from "core-app/shared/helpers/debug_output"; +import { SingleRowBuilder } from './rows/single-row-builder'; +import { RelationRenderInfo, RelationsRenderPass } from './relations/relations-render-pass'; +import { WorkPackageTable } from '../wp-fast-table'; export type RenderedRowType = 'primary'|'relations'; @@ -35,9 +35,10 @@ export interface RowRenderInfo { } export abstract class PrimaryRenderPass { - @InjectField() halEditing:HalResourceEditingService; + @InjectField() states:States; + @InjectField() I18n!:I18nService; /** The rendered order of rows of work package IDs or , if not a work package row */ @@ -59,8 +60,8 @@ export abstract class PrimaryRenderPass { public highlighting:HighlightingRenderPass; constructor(public readonly injector:Injector, - public workPackageTable:WorkPackageTable, - public rowBuilder:SingleRowBuilder) { + public workPackageTable:WorkPackageTable, + public rowBuilder:SingleRowBuilder) { } /** @@ -69,9 +70,7 @@ export abstract class PrimaryRenderPass { * @return {PrimaryRenderPass} */ public render():this { - timeOutput('Primary render pass', () => { - // Prepare and reset the render pass this.prepare(); @@ -111,11 +110,11 @@ export abstract class PrimaryRenderPass { let replacement:JQuery|null = null; switch (row.renderType) { - case 'primary': - replacement = this.rowBuilder.refreshRow(workPackage, oldRow); - break; - case 'relations': - replacement = this.relations.refreshRelationRow(row as RelationRenderInfo, workPackage, oldRow); + case 'primary': + replacement = this.rowBuilder.refreshRow(workPackage, oldRow); + break; + case 'relations': + replacement = this.relations.refreshRelationRow(row as RelationRenderInfo, workPackage, oldRow); } if (replacement !== null && oldRow.length) { @@ -124,13 +123,11 @@ export abstract class PrimaryRenderPass { } public get result():RenderedWorkPackage[] { - return this.renderedOrder.map((row) => { - return { - classIdentifier: row.classIdentifier, - workPackageId: row.workPackage ? row.workPackage.id : null, - hidden: row.hidden - }; - }); + return this.renderedOrder.map((row) => ({ + classIdentifier: row.classIdentifier, + workPackageId: row.workPackage ? row.workPackage.id : null, + hidden: row.hidden, + })); } /** @@ -187,16 +184,15 @@ export abstract class PrimaryRenderPass { row:HTMLTableRowElement, additionalClasses:string[] = [], hidden = false) { - this.tableBody.appendChild(row); this.renderedOrder.push({ classIdentifier: this.rowBuilder.classIdentifier(workPackage), - additionalClasses: additionalClasses, - workPackage: workPackage, + additionalClasses, + workPackage, renderType: 'primary', element: row, - hidden: hidden + hidden, }); } @@ -216,10 +212,10 @@ export abstract class PrimaryRenderPass { this.renderedOrder.push({ element: row, classIdentifier: classIdentifer, - additionalClasses: additionalClasses, + additionalClasses, workPackage: null, renderType: 'primary', - hidden: hidden + hidden, }); } } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relation-cell-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relation-cell-builder.ts index 656f5c2a64..999e715569 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relation-cell-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relation-cell-builder.ts @@ -1,20 +1,21 @@ import { Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { tdClassName } from './cell-builder'; -import { QueryColumn } from '../../wp-query/query-column'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; import { WorkPackageRelationsService } from '../../wp-relations/wp-relations.service'; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; +import { QueryColumn } from '../../wp-query/query-column'; +import { tdClassName } from './cell-builder'; export const relationCellTdClassName = 'wp-table--relation-cell-td'; export const relationCellIndicatorClassName = 'wp-table--relation-indicator'; export class RelationCellbuilder { - @InjectField() states:States; + @InjectField() wpRelations:WorkPackageRelationsService; + @InjectField() wpTableRelationColumns:WorkPackageViewRelationColumnsService; constructor(public readonly injector:Injector) { @@ -23,7 +24,7 @@ export class RelationCellbuilder { public build(workPackage:WorkPackageResource, column:QueryColumn) { const td = document.createElement('td'); td.classList.add(tdClassName, relationCellTdClassName, column.id); - td.dataset['columnId'] = column.id; + td.dataset.columnId = column.id; // Get current expansion and value state const expanded = this.wpTableRelationColumns.getExpandFor(workPackage.id!) === column.id; @@ -60,10 +61,9 @@ export class RelationCellbuilder { const badge = document.createElement('span'); badge.classList.add('wp-table--relation-count'); - badge.textContent = '' + relations.length; + badge.textContent = `${relations.length}`; badge.classList.add('badge', '-border-only'); return badge; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relation-row-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relation-row-builder.ts index 7c4fa66cbd..c860ccb629 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relation-row-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relation-row-builder.ts @@ -1,14 +1,14 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { isRelationColumn, QueryColumn } from '../../../wp-query/query-column'; -import { WorkPackageTable } from '../../wp-fast-table'; -import { tdClassName } from '../cell-builder'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { RelationColumnType } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { States } from 'core-app/core/states/states.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; import { commonRowClassName, SingleRowBuilder, tableRowClassName } from '../rows/single-row-builder'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { RelationColumnType } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { States } from "core-app/core/states/states.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; +import { tdClassName } from '../cell-builder'; +import { WorkPackageTable } from '../../wp-fast-table'; +import { isRelationColumn, QueryColumn } from '../../../wp-query/query-column'; export function relationGroupClass(workPackageId:string) { return `__relations-expanded-from-${workPackageId}`; @@ -21,13 +21,12 @@ export function relationIdentifier(targetId:string, workPackageId:string) { export const relationCellClassName = 'wp-table--relation-cell-td'; export class RelationRowBuilder extends SingleRowBuilder { - @InjectField() public states:States; + @InjectField() public I18n:I18nService; constructor(public readonly injector:Injector, - protected workPackageTable:WorkPackageTable) { - + protected workPackageTable:WorkPackageTable) { super(injector, workPackageTable); } @@ -39,7 +38,6 @@ export class RelationRowBuilder extends SingleRowBuilder { * @return {any} */ public buildCell(workPackage:WorkPackageResource, column:QueryColumn):HTMLElement|null { - // handle relation types if (isRelationColumn(column)) { return this.emptyRelationCell(column); @@ -71,15 +69,15 @@ export class RelationRowBuilder extends SingleRowBuilder { public createEmptyRelationRow(from:WorkPackageResource, to:WorkPackageResource) { const identifier = this.relationClassIdentifier(from, to); const tr = document.createElement('tr'); - tr.dataset['workPackageId'] = to.id!; - tr.dataset['classIdentifier'] = identifier; + tr.dataset.workPackageId = to.id!; + tr.dataset.classIdentifier = identifier; tr.classList.add( commonRowClassName, tableRowClassName, 'issue', - `wp-table--relations-aditional-row`, + 'wp-table--relations-aditional-row', identifier, `${identifier}-table`, - relationGroupClass(from.id!) + relationGroupClass(from.id!), ); return tr; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relations-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relations-render-pass.ts index a350415b40..953fbf6700 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relations-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/relations/relations-render-pass.ts @@ -1,17 +1,17 @@ import { Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { PrimaryRenderPass, RowRenderInfo } from '../primary-render-pass'; -import { relationGroupClass, RelationRowBuilder } from './relation-row-builder'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { RelationColumnType, - WorkPackageViewRelationColumnsService -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; + WorkPackageViewRelationColumnsService, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { relationGroupClass, RelationRowBuilder } from './relation-row-builder'; +import { PrimaryRenderPass, RowRenderInfo } from '../primary-render-pass'; export interface RelationRenderInfo extends RowRenderInfo { data:{ @@ -23,15 +23,16 @@ export interface RelationRenderInfo extends RowRenderInfo { export class RelationsRenderPass { @InjectField() wpRelations:WorkPackageRelationsService; + @InjectField() wpTableColumns:WorkPackageViewColumnsService; + @InjectField() wpTableRelationColumns:WorkPackageViewRelationColumnsService; public relationRowBuilder:RelationRowBuilder; constructor(public readonly injector:Injector, - private table:WorkPackageTable, - private tablePass:PrimaryRenderPass) { - + private table:WorkPackageTable, + private tablePass:PrimaryRenderPass) { this.relationRowBuilder = new RelationRowBuilder(injector, table); } @@ -44,14 +45,13 @@ export class RelationsRenderPass { // Render for each original row, clone it since we're modifying the tablepass const rendered = _.clone(this.tablePass.renderedOrder); rendered.forEach((row:RowRenderInfo, position:number) => { - // We only care for rows that are natural work packages if (!row.workPackage) { return; } // If the work package has no relations, ignore - const workPackage = row.workPackage; + const { workPackage } = row; const fromId = workPackage.id!; const state = this.wpRelations.state(fromId); if (!state.hasValue() || _.size(state.value) === 0) { @@ -61,12 +61,11 @@ export class RelationsRenderPass { this.wpTableRelationColumns.relationsToExtendFor(workPackage, state.value, (relation:RelationResource, column:QueryColumn, type:any) => { - // Build each relation row (currently sorted by order defined in API) const [relationRow, target] = this.relationRowBuilder.buildEmptyRelationRow( workPackage, relation, - type + type, ); // Augment any data for the belonging work package row to it @@ -92,11 +91,11 @@ export class RelationsRenderPass { renderType: 'relations', hidden: row.hidden, data: { - relation: relation, + relation, columnId: column.id, - relationType: type - } - } as RelationRenderInfo + relationType: type, + }, + } as RelationRenderInfo, ); }); }); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder.ts index cf404ba519..2c0ff99b8b 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder.ts @@ -1,21 +1,21 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { locateTableRowByIdentifier } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { isRelationColumn, QueryColumn } from '../../../wp-query/query-column'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; -import { WorkPackageTable } from '../../wp-fast-table'; -import { CellBuilder, tdClassName } from '../cell-builder'; -import { RelationCellbuilder } from '../relation-cell-builder'; -import { checkedClassName } from '../ui-state-link-builder'; import { TableActionRenderer } from 'core-app/features/work-packages/components/wp-fast-table/builders/table-action-renderer'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; import { internalContextMenuColumn, - internalSortColumn -} from "core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { debugLog } from "core-app/shared/helpers/debug_output"; + internalSortColumn, +} from 'core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { checkedClassName } from '../ui-state-link-builder'; +import { RelationCellbuilder } from '../relation-cell-builder'; +import { CellBuilder, tdClassName } from '../cell-builder'; +import { WorkPackageTable } from '../../wp-fast-table'; +import { isRelationColumn, QueryColumn } from '../../../wp-query/query-column'; // Work package table row entries export const tableRowClassName = 'wp-table--row'; @@ -23,14 +23,16 @@ export const tableRowClassName = 'wp-table--row'; export const commonRowClassName = 'wp--row'; export class SingleRowBuilder { - // Injections @InjectField() wpTableSelection:WorkPackageViewSelectionService; + @InjectField() wpTableColumns:WorkPackageViewColumnsService; + @InjectField() I18n!:I18nService; // Cell builder instance protected cellBuilder = new CellBuilder(this.injector); + // Relation cell builder instance protected relationCellBuilder = new RelationCellbuilder(this.injector); @@ -41,7 +43,7 @@ export class SingleRowBuilder { protected readonly augmentedColumns:QueryColumn[] = this.buildAugmentedColumns(); constructor(public readonly injector:Injector, - protected workPackageTable:WorkPackageTable) { + protected workPackageTable:WorkPackageTable) { } /** @@ -73,18 +75,18 @@ export class SingleRowBuilder { // Handle property types switch (column.id) { - case internalContextMenuColumn.id: - if (this.workPackageTable.configuration.actionsColumnEnabled) { - return this.contextLinkBuilder.build(workPackage); - } else if (this.workPackageTable.configuration.columnMenuEnabled) { - const td = document.createElement('td'); - td.classList.add('hide-when-print'); - return td; - } else { + case internalContextMenuColumn.id: + if (this.workPackageTable.configuration.actionsColumnEnabled) { + return this.contextLinkBuilder.build(workPackage); + } if (this.workPackageTable.configuration.columnMenuEnabled) { + const td = document.createElement('td'); + td.classList.add('hide-when-print'); + return td; + } return null; - } - default: - return this.cellBuilder.build(workPackage, column); + + default: + return this.cellBuilder.build(workPackage, column); } } @@ -105,14 +107,14 @@ export class SingleRowBuilder { const identifier = this.classIdentifier(workPackage); const tr = document.createElement('tr'); tr.setAttribute('tabindex', '0'); - tr.dataset['workPackageId'] = workPackage.id!; - tr.dataset['classIdentifier'] = identifier; + tr.dataset.workPackageId = workPackage.id!; + tr.dataset.classIdentifier = identifier; tr.classList.add( tableRowClassName, commonRowClassName, identifier, `${identifier}-table`, - 'issue' + 'issue', ); return tr; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/table-action-renderer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/table-action-renderer.ts index 02879555cb..8f3e7787a6 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/table-action-renderer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/table-action-renderer.ts @@ -1,13 +1,12 @@ import { Injector } from '@angular/core'; -import { tdClassName } from './cell-builder'; import { OpTableActionsService } from 'core-app/features/work-packages/components/wp-table/table-actions/table-actions.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { contextMenuSpanClassName, contextMenuTdClassName } from "core-app/features/work-packages/components/wp-table/table-actions/table-action"; -import { internalContextMenuColumn } from "core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { contextMenuSpanClassName, contextMenuTdClassName } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; +import { internalContextMenuColumn } from 'core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { tdClassName } from './cell-builder'; export class TableActionRenderer { - // Injections @InjectField() tableActionsService:OpTableActionsService; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-render-pass.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-render-pass.ts index 31cdfd4d8f..b754851579 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-render-pass.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-render-pass.ts @@ -4,7 +4,6 @@ import { TimelineRowBuilder } from './timeline-row-builder'; import { WorkPackageTable } from '../../wp-fast-table'; export class TimelineRenderPass { - /** Row builders */ protected timelineBuilder:TimelineRowBuilder; @@ -12,8 +11,8 @@ export class TimelineRenderPass { public timelineBody:DocumentFragment; constructor(public readonly injector:Injector, - private table:WorkPackageTable, - private tablePass:PrimaryRenderPass) { + private table:WorkPackageTable, + private tablePass:PrimaryRenderPass) { } public render() { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-row-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-row-builder.ts index 28952d229e..fa3aaa777d 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-row-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/timeline/timeline-row-builder.ts @@ -1,20 +1,19 @@ import { Injector } from '@angular/core'; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageTable } from '../../wp-fast-table'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { commonRowClassName } from '../rows/single-row-builder'; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageTable } from '../../wp-fast-table'; export const timelineCellClassName = 'wp-timeline-cell'; export class TimelineRowBuilder { - @InjectField() public states:States; + @InjectField() public wpTableTimeline:WorkPackageViewTimelineService; constructor(public readonly injector:Injector, - protected workPackageTable:WorkPackageTable) { + protected workPackageTable:WorkPackageTable) { } public build(workPackageId:string|null) { @@ -22,7 +21,7 @@ export class TimelineRowBuilder { cell.classList.add(timelineCellClassName, commonRowClassName); if (workPackageId) { - cell.dataset['workPackageId'] = workPackageId; + cell.dataset.workPackageId = workPackageId; } return cell; @@ -37,7 +36,6 @@ export class TimelineRowBuilder { public insert(workPackageId:string|null, timelineBody:DocumentFragment|HTMLElement, rowClasses:string[] = []) { - const cell = this.build(workPackageId); cell.classList.add(...rowClasses); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder.ts index e577554074..95e4abf453 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder.ts @@ -1,13 +1,12 @@ import { StateService } from '@uirouter/core'; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; export const uiStateLinkClass = '__ui-state-link'; export const checkedClassName = '-checked'; export class UiStateLinkBuilder { - constructor(public readonly $state:StateService, - public readonly keepTab:KeepTabService) { + public readonly keepTab:KeepTabService) { } public linkToDetails(workPackageId:string, title:string, content:string) { @@ -33,17 +32,17 @@ export class UiStateLinkBuilder { a.href = this.$state.href( tabState, { - workPackageId: workPackageId, - tabIdentifier: tabIdentifier - } + workPackageId, + tabIdentifier, + }, ); a.classList.add(uiStateLinkClass); - a.dataset['workPackageId'] = workPackageId; - a.dataset['wpState'] = state; + a.dataset.workPackageId = workPackageId; + a.dataset.wpState = state; a.setAttribute('title', title); a.textContent = content; return a; } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/edit-cell-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/edit-cell-handler.ts index 5c41bf456d..4f289f9f77 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/edit-cell-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/edit-cell-handler.ts @@ -1,20 +1,20 @@ import { Injector } from '@angular/core'; import { displayClassName, editableClassName, readOnlyClassName } from 'core-app/shared/components/fields/display/display-field-renderer'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { tableRowClassName } from '../../builders/rows/single-row-builder'; -import { WorkPackageTable } from '../../wp-fast-table'; -import { ClickOrEnterHandler } from '../click-or-enter-handler'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { getPosition } from 'core-app/shared/helpers/set-click-position/set-click-position'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { States } from 'core-app/core/states/states.service'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { ClickPositionMapper } from "core-app/shared/helpers/set-click-position/set-click-position"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { States } from "core-app/core/states/states.service"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { ClickOrEnterHandler } from '../click-or-enter-handler'; +import { WorkPackageTable } from '../../wp-fast-table'; +import { tableRowClassName } from '../../builders/rows/single-row-builder'; export class EditCellHandler extends ClickOrEnterHandler implements TableEventHandler { - // Injections @InjectField() public states:States; + @InjectField() public halEditing:HalResourceEditingService; // Keep a reference to all @@ -61,7 +61,7 @@ export class EditCellHandler extends ClickOrEnterHandler implements TableEventHa const form = table.editing.startEditing(workPackage, classIdentifier); // Get the position where the user clicked. - const positionOffset = ClickPositionMapper.getPosition(evt); + const positionOffset = getPosition(evt); // Activate the field form.activate(fieldName) diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/relations-cell-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/relations-cell-handler.ts index 24010a8375..6aa88a1bd9 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/relations-cell-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/cell/relations-cell-handler.ts @@ -1,15 +1,14 @@ import { Injector } from '@angular/core'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; import { relationCellIndicatorClassName, relationCellTdClassName } from '../../builders/relation-cell-builder'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { WorkPackageTable } from '../../wp-fast-table'; import { ClickOrEnterHandler } from '../click-or-enter-handler'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { debugLog } from "core-app/shared/helpers/debug_output"; export class RelationsCellHandler extends ClickOrEnterHandler implements TableEventHandler { - // Injections @InjectField() wpTableRelationColumns:WorkPackageViewRelationColumnsService; diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/click-or-enter-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/click-or-enter-handler.ts index 5906dde307..15afc2b5d4 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/click-or-enter-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/click-or-enter-handler.ts @@ -1,13 +1,12 @@ -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; -import { WorkPackageTable } from "../wp-fast-table"; -import { TableEventComponent } from "core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry"; - +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; +import { WorkPackageTable } from '../wp-fast-table'; /** * Execute the callback if the given JQuery Event is either an ENTER key or a click */ export function onClickOrEnter(evt:JQuery.TriggeredEvent, callback:() => void) { - if (evt.type === 'click' || (evt.type === 'keydown' && evt.which === keyCodes.ENTER)) { + if (evt.type === 'click' || (evt.type === 'keydown' && evt.which === KeyCodes.ENTER)) { callback(); return false; } @@ -15,7 +14,6 @@ export function onClickOrEnter(evt:JQuery.TriggeredEvent, callback:() => void) { return true; } - export abstract class ClickOrEnterHandler { public handleEvent(view:TableEventComponent, evt:JQuery.TriggeredEvent) { onClickOrEnter(evt, () => this.processEvent(view.workPackageTable, evt)); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-click-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-click-handler.ts index d47b451aab..e8bc59e63a 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-click-handler.ts @@ -1,13 +1,11 @@ import { Injector } from '@angular/core'; import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { contextMenuLinkClassName } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; +import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; import { uiStateLinkClass } from '../../builders/ui-state-link-builder'; -import { WorkPackageTable } from '../../wp-fast-table'; import { ContextMenuHandler } from './context-menu-handler'; -import { contextMenuLinkClassName } from "core-app/features/work-packages/components/wp-table/table-actions/table-action"; -import { TableEventComponent } from "core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry"; export class ContextMenuClickHandler extends ContextMenuHandler { - constructor(public readonly injector:Injector) { super(injector); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-handler.ts index 1ba8b17540..cb913ced64 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-handler.ts @@ -1,10 +1,10 @@ import { Injector } from '@angular/core'; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { WorkPackageTableContextMenu } from 'core-app/shared/components/op-context-menu/wp-context-menu/wp-table-context-menu.directive'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { WorkPackageTable } from '../../wp-fast-table'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; -import { WorkPackageTableContextMenu } from "core-app/shared/components/op-context-menu/wp-context-menu/wp-table-context-menu.directive"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export abstract class ContextMenuHandler implements TableEventHandler { // Injections diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-keyboard-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-keyboard-handler.ts index fb8c685de6..74aab5a566 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-keyboard-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-keyboard-handler.ts @@ -1,11 +1,9 @@ import { Injector } from '@angular/core'; -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; -import { WorkPackageTable } from '../../wp-fast-table'; +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; import { ContextMenuHandler } from './context-menu-handler'; -import { TableEventComponent } from "core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry"; export class ContextMenuKeyboardHandler extends ContextMenuHandler { - constructor(public readonly injector:Injector) { super(injector); } @@ -25,7 +23,7 @@ export class ContextMenuKeyboardHandler extends ContextMenuHandler { const target = jQuery(evt.target); - if (!(evt.keyCode === keyCodes.F10 && evt.shiftKey && evt.altKey)) { + if (!(evt.keyCode === KeyCodes.F10 && evt.shiftKey && evt.altKey)) { return true; } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-rightclick-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-rightclick-handler.ts index 0217ef353a..32b5e5f32b 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-rightclick-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/context-menu/context-menu-rightclick-handler.ts @@ -1,16 +1,14 @@ import { Injector } from '@angular/core'; import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { TableEventComponent } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { timelineCellClassName } from '../../builders/timeline/timeline-row-builder'; import { uiStateLinkClass } from '../../builders/ui-state-link-builder'; -import { WorkPackageTable } from '../../wp-fast-table'; import { ContextMenuHandler } from './context-menu-handler'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { TableEventComponent } from "core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry"; export class ContextMenuRightClickHandler extends ContextMenuHandler { - @InjectField() readonly wpTableSelection:WorkPackageViewSelectionService; constructor(public readonly injector:Injector) { @@ -50,7 +48,7 @@ export class ContextMenuRightClickHandler extends ContextMenuHandler { const wpId = element.data('workPackageId'); if (wpId) { - const [index,] = view.workPackageTable.findRenderedRow(wpId); + const [index] = view.workPackageTable.findRenderedRow(wpId); if (!this.wpTableSelection.isSelected(wpId)) { this.wpTableSelection.setSelection(wpId, index); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/click-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/click-handler.ts index e4e6950944..ca6db7eb44 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/click-handler.ts @@ -1,23 +1,26 @@ import { Injector } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { States } from "core-app/core/states/states.service"; -import { KeepTabService } from '../../../wp-single-view-tabs/keep-tab/keep-tab.service'; -import { tableRowClassName } from '../../builders/rows/single-row-builder'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { displayClassName } from 'core-app/shared/components/fields/display/display-field-renderer'; +import { activeFieldClassName } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { displayClassName } from "core-app/shared/components/fields/display/display-field-renderer"; -import { activeFieldClassName } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { tableRowClassName } from '../../builders/rows/single-row-builder'; +import { KeepTabService } from '../../../wp-single-view-tabs/keep-tab/keep-tab.service'; export class RowClickHandler implements TableEventHandler { - // Injections @InjectField() public $state:StateService; + @InjectField() public states:States; + @InjectField() public keepTab:KeepTabService; + @InjectField() public wpTableSelection:WorkPackageViewSelectionService; + @InjectField() public wpTableFocus:WorkPackageViewFocusService; constructor(public readonly injector:Injector) { @@ -86,4 +89,3 @@ export class RowClickHandler implements TableEventHandler { return false; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/double-click-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/double-click-handler.ts index a066cf2d58..3d4ec9f5ea 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/double-click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/double-click-handler.ts @@ -2,23 +2,24 @@ import { Injector } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { debugLog } from 'core-app/shared/helpers/debug_output'; -import { States } from "core-app/core/states/states.service"; -import { tdClassName } from '../../builders/cell-builder'; -import { tableRowClassName } from '../../builders/rows/single-row-builder'; -import { WorkPackageTable } from '../../wp-fast-table'; +import { States } from 'core-app/core/states/states.service'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { displayClassName } from 'core-app/shared/components/fields/display/display-field-renderer'; +import { activeFieldClassName } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { LinkHandling } from "core-app/shared/helpers/link-handling/link-handling"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { displayClassName } from "core-app/shared/components/fields/display/display-field-renderer"; -import { activeFieldClassName } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { tableRowClassName } from '../../builders/rows/single-row-builder'; +import { tdClassName } from '../../builders/cell-builder'; export class RowDoubleClickHandler implements TableEventHandler { - // Injections @InjectField() public $state:StateService; + @InjectField() public states:States; + @InjectField() public wpTableSelection:WorkPackageViewSelectionService; + @InjectField() public wpTableFocus:WorkPackageViewFocusService; constructor(public readonly injector:Injector) { @@ -40,7 +41,7 @@ export class RowDoubleClickHandler implements TableEventHandler { const target = jQuery(evt.target); // Skip clicks with modifiers - if (LinkHandling.isClickedWithModifier(evt)) { + if (isClickedWithModifier(evt)) { return true; } @@ -68,4 +69,3 @@ export class RowDoubleClickHandler implements TableEventHandler { return false; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/group-row-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/group-row-handler.ts index f9f63ce711..71031d0888 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/group-row-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/group-row-handler.ts @@ -1,14 +1,14 @@ import { Injector } from '@angular/core'; -import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { rowGroupClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; +import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; export class GroupRowHandler implements TableEventHandler { - // Injections @InjectField() public querySpace:IsolatedQuerySpace; + @InjectField() public workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService; constructor(public readonly injector:Injector) { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/hierarchy-click-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/hierarchy-click-handler.ts index 0e723c5171..6ad9f29942 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/hierarchy-click-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/hierarchy-click-handler.ts @@ -1,15 +1,16 @@ import { Injector } from '@angular/core'; -import { States } from "core-app/core/states/states.service"; +import { States } from 'core-app/core/states/states.service'; +import { TableEventComponent, TableEventHandler } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { WorkPackageTable } from '../../wp-fast-table'; import { ClickOrEnterHandler } from '../click-or-enter-handler'; -import { TableEventComponent, TableEventHandler } from "core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class HierarchyClickHandler extends ClickOrEnterHandler implements TableEventHandler { // Injections @InjectField() public states:States; + @InjectField() public wpTableHierarchies:WorkPackageViewHierarchiesService; constructor(public readonly injector:Injector) { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/wp-state-links-handler.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/wp-state-links-handler.ts index cb0da58786..3d6148629a 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/wp-state-links-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/row/wp-state-links-handler.ts @@ -1,23 +1,25 @@ import { Injector } from '@angular/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; +import { StateService } from '@uirouter/core'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { KeepTabService } from '../../../wp-single-view-tabs/keep-tab/keep-tab.service'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { uiStateLinkClass } from '../../builders/ui-state-link-builder'; -import { WorkPackageTable } from '../../wp-fast-table'; import { TableEventComponent, TableEventHandler } from '../table-handler-registry'; -import { StateService } from '@uirouter/core'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class WorkPackageStateLinksHandler implements TableEventHandler { - // Injections @InjectField() public $state:StateService; + @InjectField() public keepTab:KeepTabService; + @InjectField() public states:States; + @InjectField() public wpTableSelection:WorkPackageViewSelectionService; + @InjectField() public wpTableFocus:WorkPackageViewFocusService; constructor(public readonly injector:Injector) { @@ -63,7 +65,7 @@ export class WorkPackageStateLinksHandler implements TableEventHandler { // Update single selection if no modifier present this.wpTableSelection.setSelection(workPackageId, index); - view.stateLinkClicked.emit({ workPackageId: workPackageId, requestedState: state }); + view.stateLinkClicked.emit({ workPackageId, requestedState: state }); evt.preventDefault(); evt.stopPropagation(); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/columns-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/columns-transformer.ts index 2f44c64308..7bc3f2bd57 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/columns-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/columns-transformer.ts @@ -1,34 +1,32 @@ import { Injector } from '@angular/core'; import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { takeUntil } from 'rxjs/operators'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { takeUntil } from "rxjs/operators"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class ColumnsTransformer { - @InjectField() public querySpace:IsolatedQuerySpace; + @InjectField() public wpTableColumns:WorkPackageViewColumnsService; constructor(public readonly injector:Injector, - public table:WorkPackageTable) { - + public table:WorkPackageTable) { this.wpTableColumns .updates$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe(() => { if (table.originalRows.length > 0) { - - var t0 = performance.now(); + const t0 = performance.now(); // Redraw the table section, ignore timeline table.redrawTable(); - var t1 = performance.now(); + const t1 = performance.now(); - debugLog('column redraw took ' + (t1 - t0) + ' milliseconds.'); + debugLog(`column redraw took ${t1 - t0} milliseconds.`); } }); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer.ts index 98d46ac416..d4deeca2c9 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer.ts @@ -1,41 +1,49 @@ import { Injector } from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { take, takeUntil } from 'rxjs/operators'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { TableDragActionsRegistryService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service'; +import { TableDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service'; +import { States } from 'core-app/core/states/states.service'; +import { tableRowClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { DragAndDropHelpers } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.helpers'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { BrowserDetector } from 'core-app/core/browser/browser-detector.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { isInsideCollapsedGroup } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers'; +import { collapsedGroupClass } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { take, takeUntil } from "rxjs/operators"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { TableDragActionsRegistryService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service"; -import { TableDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service"; -import { States } from "core-app/core/states/states.service"; -import { tableRowClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { DragAndDropHelpers } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.helpers"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { BrowserDetector } from "core-app/core/browser/browser-detector.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { isInsideCollapsedGroup } from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers"; -import { collapsedGroupClass } from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; export class DragAndDropTransformer { - @InjectField() private readonly states:States; + @InjectField() private readonly querySpace:IsolatedQuerySpace; + @InjectField() private readonly inlineCreateService:WorkPackageInlineCreateService; + @InjectField() private readonly halNotification:HalResourceNotificationService; + @InjectField() private readonly wpTableSortBy:WorkPackageViewSortByService; + @InjectField() private readonly wpTableOrder:WorkPackageViewOrderService; + @InjectField() private readonly browserDetector:BrowserDetector; + @InjectField() private readonly apiV3Service:APIV3Service; + @InjectField() private readonly wpListService:WorkPackagesListService; + @InjectField() private readonly dragActionRegistry:TableDragActionsRegistryService; + @InjectField(DragAndDropService, null) private readonly dragService:DragAndDropService|null; constructor(public readonly injector:Injector, - public table:WorkPackageTable) { - + public table:WorkPackageTable) { // The DragService may not have been provided // in which case we do not provide drag and drop if (this.dragService === null) { @@ -76,7 +84,7 @@ export class DragAndDropTransformer { const workPackage = await this.apiV3Service.work_packages.id(wpId).get().toPromise(); if (isInsideCollapsedGroup(sibling)) { - const collapsedGroupCSSClass = Array.from(sibling!.classList).find(listClass => listClass.includes(collapsedGroupClass()))!; + const collapsedGroupCSSClass = Array.from(sibling!.classList).find((listClass) => listClass.includes(collapsedGroupClass()))!; const collapsedGroupId = collapsedGroupCSSClass.replace(collapsedGroupClass(), ''); const collapsedGroupElements = source.getElementsByClassName(collapsedGroupClass(collapsedGroupId)); const collapsedGroupLastChild = collapsedGroupElements[collapsedGroupElements.length - 1]; @@ -161,8 +169,8 @@ export class DragAndDropTransformer { const mappedOrder = await Promise.all( order.map( - wpId => this.apiV3Service.work_packages.id(wpId).get().toPromise() - ) + (wpId) => this.apiV3Service.work_packages.id(wpId).get().toPromise(), + ), ); /** Re-render the table */ diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer.ts index 606529e444..c208a4204d 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer.ts @@ -1,13 +1,13 @@ import { Injector } from '@angular/core'; import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { WorkPackageViewCollapsedGroupsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; export class GroupFoldTransformer { - @InjectField() public workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService; + @InjectField() public querySpace:IsolatedQuerySpace; constructor(public readonly injector:Injector, @@ -16,7 +16,7 @@ export class GroupFoldTransformer { .updates$() .pipe( takeUntil(this.querySpace.stopAllSubscriptions), - distinctUntilChanged() + distinctUntilChanged(), ) .subscribe((groupsCollapseEvent) => table.setGroupsCollapseState(groupsCollapseEvent.state)); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/hierarchy-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/hierarchy-transformer.ts index 09394a6015..365cec2355 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/hierarchy-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/hierarchy-transformer.ts @@ -1,33 +1,34 @@ import { Injector } from '@angular/core'; import { scrollTableRowIntoView } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers'; -import { distinctUntilChanged, filter, map, takeUntil } from 'rxjs/operators'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; +import { + distinctUntilChanged, filter, map, takeUntil, +} from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; import { collapsedGroupClass, hierarchyGroupClass, - hierarchyRootClass -} from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; -import { indicatorCollapsedClass } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder"; -import { tableRowClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder"; -import { WorkPackageViewHierarchies } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; + hierarchyRootClass, +} from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; +import { indicatorCollapsedClass } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/single-hierarchy-row-builder'; +import { tableRowClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/rows/single-row-builder'; +import { WorkPackageViewHierarchies } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class HierarchyTransformer { - @InjectField() public wpTableHierarchies:WorkPackageViewHierarchiesService; + @InjectField() public querySpace:IsolatedQuerySpace; constructor(public readonly injector:Injector, table:WorkPackageTable) { - this.wpTableHierarchies .updates$() .pipe( takeUntil(this.querySpace.stopAllSubscriptions), map((state) => state.isVisible), - distinctUntilChanged() + distinctUntilChanged(), ) .subscribe(() => { // We don't have to reload all results when _disabling_ the hierarchy mode. @@ -42,10 +43,9 @@ export class HierarchyTransformer { .updates$() .pipe( takeUntil(this.querySpace.stopAllSubscriptions), - filter(() => this.querySpace.tableRendered.hasValue()) + filter(() => this.querySpace.tableRendered.hasValue()), ) .subscribe((state:WorkPackageViewHierarchies) => { - if (state.isVisible === lastValue) { this.renderHierarchyState(state); } @@ -61,9 +61,7 @@ export class HierarchyTransformer { const rendered = this.querySpace.tableRendered.value!; // Show all hierarchies - jQuery('[class^="__hierarchy-group-"]').removeClass((i:number, classNames:string):string => { - return (classNames.match(/__collapsed-group-\d+/g) || []).join(' '); - }); + jQuery('[class^="__hierarchy-group-"]').removeClass((i:number, classNames:string):string => (classNames.match(/__collapsed-group-\d+/g) || []).join(' ')); // Mark which rows were hidden by some other hierarchy group // (e.g., by a collapsed parent) @@ -79,9 +77,9 @@ export class HierarchyTransformer { if (hierarchyRoot) { if (isCollapsed) { - hierarchyRoot.classList.add(`__hierarchy-root-collapsed`); + hierarchyRoot.classList.add('__hierarchy-root-collapsed'); } else { - hierarchyRoot.classList.remove(`__hierarchy-root-collapsed`); + hierarchyRoot.classList.remove('__hierarchy-root-collapsed'); } } @@ -110,7 +108,6 @@ export class HierarchyTransformer { scrollTableRowIntoView(state.last); } - this.querySpace.tableRendered.putValue(rendered, 'Updated hidden state of rows after hierarchy change.'); } } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer.ts index aa1dffca71..0ff628d3b5 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer.ts @@ -1,13 +1,13 @@ import { Injector } from '@angular/core'; import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; -import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { WorkPackageTable } from '../../wp-fast-table'; export class HighlightingTransformer { - @InjectField() public wpTableHighlighting:WorkPackageViewHighlightingService; + @InjectField() public querySpace:IsolatedQuerySpace; constructor(public readonly injector:Injector, @@ -16,7 +16,7 @@ export class HighlightingTransformer { .updates$() .pipe( takeUntil(this.querySpace.stopAllSubscriptions), - distinctUntilChanged() + distinctUntilChanged(), ) .subscribe(() => table.redrawTable()); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/relations-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/relations-transformer.ts index a39da7fe75..ed1de4dd52 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/relations-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/relations-transformer.ts @@ -1,22 +1,21 @@ import { Injector } from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { takeUntil } from 'rxjs/operators'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { takeUntil } from "rxjs/operators"; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class RelationsTransformer { - @InjectField() public wpTableRelationColumns:WorkPackageViewRelationColumnsService; + @InjectField() public querySpace:IsolatedQuerySpace; constructor(public readonly injector:Injector, table:WorkPackageTable) { - this.wpTableRelationColumns .updates$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe(() => { table.redrawTableAndTimeline(); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/rows-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/rows-transformer.ts index a8b131efdf..14d1d0c3e5 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/rows-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/rows-transformer.ts @@ -1,29 +1,30 @@ import { Injector } from '@angular/core'; import { filter, takeUntil } from 'rxjs/operators'; -import { WorkPackageTable } from '../../wp-fast-table'; import { States } from 'core-app/core/states/states.service'; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageTable } from '../../wp-fast-table'; export class RowsTransformer { - @InjectField() querySpace:IsolatedQuerySpace; + @InjectField() wpTableSortBy:WorkPackageViewSortByService; + @InjectField() wpTableOrder:WorkPackageViewOrderService; + @InjectField() states:States; constructor(public readonly injector:Injector, - public table:WorkPackageTable) { - + public table:WorkPackageTable) { // Redraw table if the current row state changed this.querySpace .initialized .values$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe(() => { let rows:WorkPackageResource[]; @@ -44,7 +45,7 @@ export class RowsTransformer { filter(() => { const rendered = this.querySpace.tableRendered.getValueOr([]); return rendered && rendered.length > 0; - }) + }), ) .subscribe(([changedId, wp]) => { if (wp === undefined) { diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/selection-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/selection-transformer.ts index 2dcc249a47..1fac244ade 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/selection-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/selection-transformer.ts @@ -1,35 +1,35 @@ import { Injector } from '@angular/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { takeUntil } from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; +import { + WorkPackageViewSelectionService, + WorkPackageViewSelectionState, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { tableRowClassName } from '../../builders/rows/single-row-builder'; import { checkedClassName } from '../../builders/ui-state-link-builder'; import { locateTableRow, scrollTableRowIntoView } from '../../helpers/wp-table-row-helpers'; import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; -import { - WorkPackageViewSelectionService, - WorkPackageViewSelectionState -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class SelectionTransformer { - @InjectField() public wpTableSelection:WorkPackageViewSelectionService; + @InjectField() public wpTableFocus:WorkPackageViewFocusService; + @InjectField() public querySpace:IsolatedQuerySpace; + @InjectField() public FocusHelper:FocusHelperService; constructor(public readonly injector:Injector, - public readonly table:WorkPackageTable) { - + public readonly table:WorkPackageTable) { // Focus a single selection when active this.querySpace.tableRendered.values$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe(() => { - this.wpTableFocus.ifShouldFocus((wpId:string) => { const element = locateTableRow(wpId); if (element.length) { @@ -39,20 +39,16 @@ export class SelectionTransformer { }); }); - // Update selection state this.wpTableSelection.live$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe((state:WorkPackageViewSelectionState) => { this.renderSelectionState(state); }); - - this.wpTableSelection.registerSelectAllListener(() => { - return table.renderedRows; - }); + this.wpTableSelection.registerSelectAllListener(() => table.renderedRows); this.wpTableSelection.registerDeselectAllListener(); } @@ -69,4 +65,3 @@ export class SelectionTransformer { }); } } - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/timeline-transformer.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/timeline-transformer.ts index f51f49691e..55bfb83dbe 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/timeline-transformer.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/state/timeline-transformer.ts @@ -1,23 +1,22 @@ import { Injector } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageTimelineState } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageTable } from '../../wp-fast-table'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageTimelineState } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export class TimelineTransformer { - @InjectField() public querySpace:IsolatedQuerySpace; + @InjectField() public wpTableTimeline:WorkPackageViewTimelineService; constructor(readonly injector:Injector, - readonly table:WorkPackageTable) { - + readonly table:WorkPackageTable) { this.wpTableTimeline .live$() .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe((state:WorkPackageTimelineState) => { this.renderVisibility(state.visible); diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry.ts index 0aae943167..ebe9e64f64 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry.ts @@ -1,4 +1,11 @@ -import { EventEmitter, Injector } from '@angular/core'; +import { Injector } from '@angular/core'; +import { HighlightingTransformer } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer'; +import { DragAndDropTransformer } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer'; +import { + WorkPackageViewEventHandler, WorkPackageViewOutputs, + WorkPackageViewHandlerRegistry, +} from 'core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry'; +import { GroupFoldTransformer } from 'core-app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer'; import { WorkPackageTable } from '../wp-fast-table'; import { EditCellHandler } from './cell/edit-cell-handler'; import { RelationsCellHandler } from './cell/relations-cell-handler'; @@ -16,14 +23,6 @@ import { RelationsTransformer } from './state/relations-transformer'; import { RowsTransformer } from './state/rows-transformer'; import { SelectionTransformer } from './state/selection-transformer'; import { TimelineTransformer } from './state/timeline-transformer'; -import { HighlightingTransformer } from "core-app/features/work-packages/components/wp-fast-table/handlers/state/highlighting-transformer"; -import { DragAndDropTransformer } from "core-app/features/work-packages/components/wp-fast-table/handlers/state/drag-and-drop-transformer"; -import { - WorkPackageViewEventHandler, WorkPackageViewOutputs, - WorkPackageViewHandlerRegistry -} from "core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry"; -import { WorkPackageFocusContext } from "core-app/features/work-packages/components/wp-table/wp-table.component"; -import { GroupFoldTransformer } from "core-app/features/work-packages/components/wp-fast-table/handlers/state/group-fold-transformer"; type StateTransformers = { // noinspection JSUnusedLocalSymbols @@ -38,7 +37,6 @@ export interface TableEventComponent extends WorkPackageViewOutputs { export type TableEventHandler = WorkPackageViewEventHandler; export class TableHandlerRegistry extends WorkPackageViewHandlerRegistry { - protected eventHandlers:((t:TableEventComponent) => TableEventHandler)[] = [ // Hierarchy expansion/collapsing () => new HierarchyClickHandler(this.injector), @@ -59,7 +57,7 @@ export class TableHandlerRegistry extends WorkPackageViewHandlerRegistry new ContextMenuKeyboardHandler(this.injector), // Clicking on relations cells - () => new RelationsCellHandler(this.injector) + () => new RelationsCellHandler(this.injector), ]; protected readonly stateTransformers:StateTransformers[] = [ @@ -71,13 +69,11 @@ export class TableHandlerRegistry extends WorkPackageViewHandlerRegistry { - return new cls(this.injector, viewRef.workPackageTable); - }); + this.stateTransformers.map((cls) => new cls(this.injector, viewRef.workPackageTable)); super.attachTo(viewRef); } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers.ts index b4f5a4ed16..85e4830dc1 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers.ts @@ -1,7 +1,7 @@ /** * Return the row html id attribute for the given work package ID. */ -import { collapsedGroupClass } from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; +import { collapsedGroupClass } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; export function rowId(workPackageId:string):string { return `wp-row-${workPackageId}-table`; @@ -12,7 +12,7 @@ export function relationRowClass():string { } export function locateTableRow(workPackageId:string):JQuery { - return jQuery('.' + rowId(workPackageId)); + return jQuery(`.${rowId(workPackageId)}`); } export function locateTableRowByIdentifier(identifier:string) { @@ -24,7 +24,7 @@ export function isInsideCollapsedGroup(el?:Element | null) { return false; } - return Array.from(el.classList).find(listClass => listClass.includes(collapsedGroupClass())) != null; + return Array.from(el.classList).find((listClass) => listClass.includes(collapsedGroupClass())) != null; } export function locatePredecessorBySelector(el:HTMLElement, selector:string):HTMLElement|null { @@ -33,9 +33,8 @@ export function locatePredecessorBySelector(el:HTMLElement, selector:string):HTM while (previous) { if (previous.matches(selector)) { return previous as HTMLElement; - } else { - previous = previous.previousElementSibling; } + previous = previous.previousElementSibling; } return null; @@ -57,6 +56,6 @@ export function scrollTableRowIntoView(workPackageId:string):void { container[0].scrollTop = elemBottom - container.height()!; } } catch (e) { - console.warn("Can't scroll row element into view: " + e); + console.warn(`Can't scroll row element into view: ${e}`); } } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-fast-table.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-fast-table.ts index 707c572040..c2077820c0 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-fast-table.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-fast-table.ts @@ -1,8 +1,13 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; +import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; import { WorkPackageTimelineTableController } from '../wp-table/timeline/container/wp-timeline-container.directive'; import { GroupedRowsBuilder } from './builders/modes/grouped/grouped-rows-builder'; import { HierarchyRowsBuilder } from './builders/modes/hierarchy/hierarchy-rows-builder'; @@ -11,24 +16,26 @@ import { RowsBuilder } from './builders/modes/rows-builder'; import { PrimaryRenderPass } from './builders/primary-render-pass'; import { WorkPackageTableEditingContext } from './wp-table-editing'; import { WorkPackageTableRow } from './wp-table.interfaces'; -import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; -import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; -import { WorkPackageTableConfiguration } from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; -import { debugLog } from "core-app/shared/helpers/debug_output"; export class WorkPackageTable { - @InjectField() querySpace:IsolatedQuerySpace; + @InjectField() apiV3Service:APIV3Service; + @InjectField() states:States; + @InjectField() I18n!:I18nService; + @InjectField() workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService; public originalRows:string[] = []; + public originalRowIndex:{ [id:string]:WorkPackageTableRow } = {}; + private hierarchyRowsBuilder = new HierarchyRowsBuilder(this.injector, this); + private groupedRowsBuilder = new GroupedRowsBuilder(this.injector, this); + private plainRowsBuilder = new PlainRowsBuilder(this.injector, this); // WP rows builder @@ -43,12 +50,12 @@ export class WorkPackageTable { public editing:WorkPackageTableEditingContext = new WorkPackageTableEditingContext(this, this.injector); constructor(public readonly injector:Injector, - public tableAndTimelineContainer:HTMLElement, - public scrollContainer:HTMLElement, - public tbody:HTMLElement, - public timelineBody:HTMLElement, - public timelineController:WorkPackageTimelineTableController, - public configuration:WorkPackageTableConfiguration) { + public tableAndTimelineContainer:HTMLElement, + public scrollContainer:HTMLElement, + public tbody:HTMLElement, + public timelineBody:HTMLElement, + public timelineController:WorkPackageTimelineTableController, + public configuration:WorkPackageTableConfiguration) { } public get renderedRows() { @@ -109,9 +116,7 @@ export class WorkPackageTable { this.timelineBody.appendChild(renderPass.timeline.timelineBody); // Mark rendering event in a timeout to let DOM process - setTimeout(() => - this.querySpace.tableRendered.putValue(renderPass.result) - ); + setTimeout(() => this.querySpace.tableRendered.putValue(renderPass.result)); }); } @@ -151,7 +156,6 @@ export class WorkPackageTable { return this.configuration.dragAndDropEnabled; } - /** * Perform the render pass * @param insert whether to insert the result (set to false for timeline) @@ -171,13 +175,13 @@ export class WorkPackageTable { return renderPass; } - setGroupsCollapseState(newState:{[key:string]:boolean}) { + setGroupsCollapseState(newState:{ [key:string]:boolean }) { this.querySpace.collapsedGroups.putValue(newState); const t0 = performance.now(); this.groupedRowsBuilder.refreshExpansionState(); const t1 = performance.now(); - debugLog('Group redraw took ' + (t1 - t0) + ' milliseconds.'); + debugLog(`Group redraw took ${t1 - t0} milliseconds.`); } } diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table-editing.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table-editing.ts index 228fa73f14..c19b46467b 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table-editing.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table-editing.ts @@ -1,18 +1,17 @@ import { Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { TableEditForm } from "core-app/features/work-packages/components/wp-edit-form/table-edit-form"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { TableEditForm } from 'core-app/features/work-packages/components/wp-edit-form/table-edit-form'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class WorkPackageTableEditingContext { - @InjectField() public halEditing:HalResourceEditingService; constructor(readonly table:WorkPackageTable, - readonly injector:Injector) { + readonly injector:Injector) { } public forms:{ [wpId:string]:TableEditForm } = {}; @@ -48,4 +47,3 @@ export class WorkPackageTableEditingContext { return this.forms[wpId] = new TableEditForm(this.injector, this.table, wpId, classIdentifier); } } - diff --git a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table.interfaces.ts b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table.interfaces.ts index d4c2edd917..35583ca719 100644 --- a/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table.interfaces.ts +++ b/frontend/src/app/features/work-packages/components/wp-fast-table/wp-table.interfaces.ts @@ -3,8 +3,8 @@ * May contain references to the current inserted row (if present) * or the group it belonged to when initially rendered. */ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { GroupObject } from "core-app/features/hal/resources/wp-collection-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { GroupObject } from 'core-app/features/hal/resources/wp-collection-resource'; export interface WorkPackageTableRow { object:WorkPackageResource; @@ -13,4 +13,3 @@ export interface WorkPackageTableRow { element?:HTMLElement; group:GroupObject|null; } - diff --git a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts index c6b1c426a2..6f890a5c99 100644 --- a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,29 +26,32 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Injector, Input, AfterViewInit } from '@angular/core'; +import { + Component, Injector, Input, AfterViewInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { fromEvent } from "rxjs"; -import { debounceTime } from "rxjs/operators"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; import { FieldDescriptor, GroupDescriptor, -} from "core-app/features/work-packages/components/wp-single-view/wp-single-view.component"; +} from 'core-app/features/work-packages/components/wp-single-view/wp-single-view.component'; @Component({ selector: 'wp-attribute-group', - templateUrl: './wp-attribute-group.template.html' + templateUrl: './wp-attribute-group.template.html', }) export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin implements AfterViewInit { @Input() public workPackage:WorkPackageResource; + @Input() public group:GroupDescriptor; constructor(readonly I18n:I18nService, - public wpEditForm:EditFormComponent, - protected injector:Injector) { + public wpEditForm:EditFormComponent, + protected injector:Injector) { super(); } @@ -59,7 +62,7 @@ export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin fromEvent(window, 'resize', { passive: true }) .pipe( this.untilDestroyed(), - debounceTime(250) + debounceTime(250), ) .subscribe(() => { this.fixColumns(); @@ -82,9 +85,8 @@ export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin public fieldName(name:string) { if (name === 'startDate') { return 'combinedDate'; - } else { - return name; } + return name; } /** @@ -94,9 +96,9 @@ export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin private fixColumns() { let lastOffset = 0; // Find corresponding HTML of attribute fields for each group - const htmlAttributes = jQuery('div.attributes-group:contains(' + this.group.name + ')').find('.attributes-key-value'); + const htmlAttributes = jQuery(`div.attributes-group:contains(${this.group.name})`).find('.attributes-key-value'); - htmlAttributes.each(function() { + htmlAttributes.each(function () { const offset = jQuery(this).position().top; if (offset < lastOffset) { @@ -106,4 +108,4 @@ export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin lastOffset = offset; }); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts b/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts index a1dd5597d0..fbbfb1892c 100644 --- a/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-grid/wp-grid.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from "@angular/core"; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { CardViewOrientation } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { distinctUntilChanged, takeUntil } from "rxjs/operators"; -import { HighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; -import { WorkPackageCardDragAndDropService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { WorkPackageTableConfiguration } from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; -import { WorkPackageViewOutputs } from "core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, +} from '@angular/core'; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { CardViewOrientation } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; +import { HighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; +import { WorkPackageCardDragAndDropService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-drag-and-drop.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; +import { WorkPackageViewOutputs } from 'core-app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry'; @Component({ selector: 'wp-grid', @@ -66,48 +68,53 @@ import { WorkPackageViewOutputs } from "core-app/features/work-packages/routing/ changeDetection: ChangeDetectionStrategy.OnPush, providers: [ DragAndDropService, - WorkPackageCardDragAndDropService - ] + WorkPackageCardDragAndDropService, + ], }) export class WorkPackagesGridComponent implements WorkPackageViewOutputs { @Input() public configuration:WorkPackageTableConfiguration; + @Input() public showResizer = false; + @Input() public resizerClass = ''; + @Input() public resizerStorageKey = ''; @Output() selectionChanged = new EventEmitter(); + @Output() itemClicked = new EventEmitter<{ workPackageId:string, double:boolean }>(); + @Output() stateLinkClicked = new EventEmitter<{ workPackageId:string, requestedState:string }>(); public canDragOutOf:() => boolean; + public dragInto:boolean; + public gridOrientation:CardViewOrientation = 'horizontal'; + public highlightingMode:HighlightingMode = 'none'; constructor(readonly wpTableHighlight:WorkPackageViewHighlightingService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly wpList:WorkPackagesListService, - readonly querySpace:IsolatedQuerySpace, - readonly cdRef:ChangeDetectorRef) { + readonly wpTableSortBy:WorkPackageViewSortByService, + readonly wpList:WorkPackagesListService, + readonly querySpace:IsolatedQuerySpace, + readonly cdRef:ChangeDetectorRef) { } ngOnInit() { this.dragInto = this.configuration.dragAndDropEnabled; - this.canDragOutOf = () => { - return this.configuration.dragAndDropEnabled; - }; + this.canDragOutOf = () => this.configuration.dragAndDropEnabled; this.wpTableHighlight .updates$() .pipe( takeUntil(this.querySpace.stopAllSubscriptions), - distinctUntilChanged() + distinctUntilChanged(), ) .subscribe(() => { this.highlightingMode = this.wpTableHighlight.current.mode; this.cdRef.detectChanges(); }); - } public switchToManualSorting() { diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/inline-create-row-builder.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/inline-create-row-builder.ts index e6f7b67c8d..58e0a32117 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/inline-create-row-builder.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/inline-create-row-builder.ts @@ -1,51 +1,52 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { tdClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/cell-builder'; +import { internalContextMenuColumn } from 'core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { WorkPackageTable } from '../wp-fast-table/wp-fast-table'; +import { rowId } from '../wp-fast-table/helpers/wp-table-row-helpers'; import { commonRowClassName, SingleRowBuilder, - tableRowClassName + tableRowClassName, } from '../wp-fast-table/builders/rows/single-row-builder'; -import { rowId } from '../wp-fast-table/helpers/wp-table-row-helpers'; -import { WorkPackageTable } from '../wp-fast-table/wp-fast-table'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { tdClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/cell-builder"; -import { internalContextMenuColumn } from "core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; export const inlineCreateRowClassName = 'wp-inline-create-row'; export const inlineCreateCancelClassName = 'wp-table--cancel-create-link'; export class InlineCreateRowBuilder extends SingleRowBuilder { - // Injections @InjectField() public states:States; + @InjectField() public wpTableSelection:WorkPackageViewSelectionService; + @InjectField() public wpTableColumns:WorkPackageViewColumnsService; + @InjectField() public I18n:I18nService; protected text:{ cancelButton:string }; constructor(public readonly injector:Injector, workPackageTable:WorkPackageTable) { - super(injector, workPackageTable); this.text = { - cancelButton: this.I18n.t('js.button_cancel') + cancelButton: this.I18n.t('js.button_cancel'), }; } public buildCell(workPackage:WorkPackageResource, column:QueryColumn):HTMLElement|null { switch (column.id) { - case internalContextMenuColumn.id: - return this.buildCancelButton(); - default: - return super.buildCell(workPackage, column); + case internalContextMenuColumn.id: + return this.buildCancelButton(); + default: + return super.buildCell(workPackage, column); } } @@ -53,7 +54,6 @@ export class InlineCreateRowBuilder extends SingleRowBuilder { // Get any existing edit state for this work package const [row, hidden] = this.buildEmpty(workPackage); - return [row, hidden]; } @@ -66,12 +66,12 @@ export class InlineCreateRowBuilder extends SingleRowBuilder { const identifier = this.classIdentifier(workPackage); const tr = document.createElement('tr'); tr.id = rowId(workPackage.id!); - tr.dataset['workPackageId'] = workPackage.id!; - tr.dataset['classIdentifier'] = identifier; + tr.dataset.workPackageId = workPackage.id!; + tr.dataset.classIdentifier = identifier; tr.classList.add( inlineCreateRowClassName, commonRowClassName, tableRowClassName, 'issue', identifier, - `${identifier}-table` + `${identifier}-table`, ); return tr; diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts index 72372bad1f..fd7f7a17a0 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -35,44 +35,45 @@ import { Injector, Input, EventEmitter, - OnInit, Output + OnInit, Output, } from '@angular/core'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; import { filter } from 'rxjs/operators'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { onClickOrEnter } from '../wp-fast-table/handlers/click-or-enter-handler'; -import { WorkPackageTable } from '../wp-fast-table/wp-fast-table'; -import { WorkPackageCreateService } from '../wp-new/wp-create.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { Subscription } from 'rxjs'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { inlineCreateCancelClassName, InlineCreateRowBuilder, - inlineCreateRowClassName + inlineCreateRowClassName, } from './inline-create-row-builder'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { Subscription } from 'rxjs'; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { WorkPackageCreateService } from '../wp-new/wp-create.service'; +import { WorkPackageTable } from '../wp-fast-table/wp-fast-table'; +import { onClickOrEnter } from '../wp-fast-table/handlers/click-or-enter-handler'; @Component({ selector: '[wpInlineCreate]', - templateUrl: './wp-inline-create.component.html' + templateUrl: './wp-inline-create.component.html', }) export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { - @Input('wp-inline-create--table') table:WorkPackageTable; + @Input('wp-inline-create--project-identifier') projectIdentifier:string; @Output('wp-inline-create--showing') showing = new EventEmitter(); // inner state public canAdd = false; + public canReference = false; // Inline create / reference row is active @@ -95,16 +96,16 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem } constructor(public readonly injector:Injector, - protected readonly elementRef:ElementRef, - protected readonly schemaCache:SchemaCacheService, - protected readonly I18n:I18nService, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly cdRef:ChangeDetectorRef, - protected readonly wpCreate:WorkPackageCreateService, - protected readonly wpInlineCreate:WorkPackageInlineCreateService, - protected readonly wpTableColumns:WorkPackageViewColumnsService, - protected readonly wpTableFocus:WorkPackageViewFocusService, - protected readonly authorisationService:AuthorisationService) { + protected readonly elementRef:ElementRef, + protected readonly schemaCache:SchemaCacheService, + protected readonly I18n:I18nService, + protected readonly querySpace:IsolatedQuerySpace, + protected readonly cdRef:ChangeDetectorRef, + protected readonly wpCreate:WorkPackageCreateService, + protected readonly wpInlineCreate:WorkPackageInlineCreateService, + protected readonly wpTableColumns:WorkPackageViewColumnsService, + protected readonly wpTableFocus:WorkPackageViewFocusService, + protected readonly authorisationService:AuthorisationService) { super(); } @@ -157,7 +158,7 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem .updates$() .pipe( filter(() => this.isActive), // Take only when row is inserted - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => this.refreshRow()); } @@ -170,7 +171,7 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem this.wpCreate .onNewWorkPackage() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { if (this.currentWorkPackage && this.currentWorkPackage.__initialized_at === wp.__initialized_at) { @@ -217,7 +218,6 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem this.wpCreate .createOrContinueWorkPackage(this.projectIdentifier) .then((change:WorkPackageChangeset) => { - const wp = this.currentWorkPackage = change.projectedResource; this.editingSubscription = this @@ -229,7 +229,7 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem if (!this.isActive) { this.insertRow(wp); } else { - this.schemaCache.update(this.currentWorkPackage!, form!.schema); + this.schemaCache.update(this.currentWorkPackage!, form.schema); this.refreshRow(); } }); @@ -269,7 +269,7 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem const builder = new InlineCreateRowBuilder(this.injector, this.table); const form = this.table.editing.startEditing(wp, builder.classIdentifier(wp)); - const [row,] = builder.buildNew(wp, form); + const [row] = builder.buildNew(wp, form); this.$element.append(row); return form; @@ -311,5 +311,4 @@ export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implem public get colspan():number { return this.wpTableColumns.columnCount + 1; } - } diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts index 241c7ae200..364d02295b 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,17 +27,17 @@ //++ import { Injectable, Injector, OnDestroy } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Subject } from "rxjs"; -import { ComponentType } from "@angular/cdk/portal"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Subject } from 'rxjs'; +import { ComponentType } from '@angular/cdk/portal'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Injectable() export class WorkPackageInlineCreateService implements OnDestroy { - @InjectField() I18n!:I18nService; + @InjectField() protected readonly authorisationService:AuthorisationService; constructor(readonly injector:Injector) { @@ -70,8 +70,8 @@ export class WorkPackageInlineCreateService implements OnDestroy { } public get canCreateWorkPackages() { - return this.authorisationService.can('work_packages', 'createWorkPackage') && - this.authorisationService.can('work_packages', 'editWorkPackage'); + return this.authorisationService.can('work_packages', 'createWorkPackage') + && this.authorisationService.can('work_packages', 'editWorkPackage'); } /** Allow callbacks to happen on newly created inline work packages */ diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts index 4ff93a3b03..42aff54383 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list-checksum.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,22 +29,23 @@ import { StateService, TransitionPromise } from '@uirouter/core'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { Injectable } from '@angular/core'; -import { WorkPackageViewPagination } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { WorkPackageViewPagination } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @Injectable() export class WorkPackagesListChecksumService { constructor(protected UrlParamsHelper:UrlParamsHelperService, - protected $state:StateService) { + protected $state:StateService) { } public id:string|null; + public checksum:string|null; + public visibleChecksum:string|null; public updateIfDifferent(query:QueryResource, pagination:WorkPackageViewPagination):Promise { - const newQueryChecksum = this.getNewChecksum(query, pagination); let routePromise:Promise = Promise.resolve(); @@ -54,7 +55,6 @@ export class WorkPackagesListChecksumService { routePromise = this.maintainUrlQueryState(query.id, null); this.clear(); - } else if (this.isChecksumDifferent(newQueryChecksum)) { routePromise = this.maintainUrlQueryState(query.id, newQueryChecksum); } @@ -129,14 +129,14 @@ export class WorkPackagesListChecksumService { return ( // Can only be outdated if either ID or props set - (hasCurrentQueryID || hasCurrentChecksum) && - ( + (hasCurrentQueryID || hasCurrentChecksum) + && ( // Query ID changed - idChanged || + idChanged // Query ID same + query props changed - (!idChanged && checksumChanged && (otherChecksum || this.visibleChecksum)) || + || (!idChanged && checksumChanged && (otherChecksum || this.visibleChecksum)) // No query ID set - (!hasCurrentQueryID && visibleChecksumChanged) + || (!hasCurrentQueryID && visibleChecksumChanged) ) ); } @@ -151,7 +151,7 @@ export class WorkPackagesListChecksumService { return this.$state.go( '.', { query_props: checksum, query_id: id }, - { custom: { notify: false } } + { custom: { notify: false } }, ); } } diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts index dd6f592f7e..a3671127b4 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list-invalid-query.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { QueryColumn } from '../wp-query/query-column'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Injectable } from '@angular/core'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; +import { QueryColumn } from '../wp-query/query-column'; @Injectable() export class WorkPackagesListInvalidQueryService { @@ -51,10 +51,8 @@ export class WorkPackagesListInvalidQueryService { } private restoreFilters(query:QueryResource, payload:QueryResource, querySchema:SchemaResource) { - let filters = _.map((payload.filters), filter => { - const filterInstanceSchema = _.find(querySchema.filtersSchemas.elements, (schema:QueryFilterInstanceSchemaResource) => { - return (schema.filter.allowedValues as QueryFilterResource[])[0].href === filter.filter.href; - }); + let filters = _.map((payload.filters), (filter) => { + const filterInstanceSchema = _.find(querySchema.filtersSchemas.elements, (schema:QueryFilterInstanceSchemaResource) => (schema.filter.allowedValues as QueryFilterResource[])[0].href === filter.filter.href); if (!filterInstanceSchema) { return null; @@ -62,16 +60,14 @@ export class WorkPackagesListInvalidQueryService { const recreatedFilter = filterInstanceSchema.getFilter(); - const operator = _.find(filterInstanceSchema.operator.allowedValues, operator => { - return operator.href === filter.operator.href; - }); + const operator = _.find(filterInstanceSchema.operator.allowedValues, (operator) => operator.href === filter.operator.href); if (operator) { recreatedFilter.operator = operator; } recreatedFilter.values.length = 0; - _.each(filter.values, value => recreatedFilter.values.push(value)); + _.each(filter.values, (value) => recreatedFilter.values.push(value)); return recreatedFilter; }); @@ -80,39 +76,29 @@ export class WorkPackagesListInvalidQueryService { // clear filters while keeping reference query.filters.length = 0; - _.each(filters, filter => query.filters.push(filter)); + _.each(filters, (filter) => query.filters.push(filter)); } private restoreColumns(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) { - let columns = _.map(stubQuery.columns, column => { - return _.find((schema.columns.allowedValues as QueryColumn[]), candidate => { - return candidate.href === column.href; - }); - }); + let columns = _.map(stubQuery.columns, (column) => _.find((schema.columns.allowedValues as QueryColumn[]), (candidate) => candidate.href === column.href)); columns = _.compact(columns); query.columns.length = 0; - _.each(columns, column => query.columns.push(column!)); + _.each(columns, (column) => query.columns.push(column!)); } private restoreSortBy(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) { - let sortBys = _.map((stubQuery.sortBy), sortBy => { - return _.find((schema.sortBy.allowedValues as QuerySortByResource[]), candidate => { - return candidate.href === sortBy.href; - })!; - }); + let sortBys = _.map((stubQuery.sortBy), (sortBy) => _.find((schema.sortBy.allowedValues as QuerySortByResource[]), (candidate) => candidate.href === sortBy.href)!); sortBys = _.compact(sortBys); query.sortBy.length = 0; - _.each(sortBys, sortBy => query.sortBy.push(sortBy)); + _.each(sortBys, (sortBy) => query.sortBy.push(sortBy)); } private restoreGroupBy(query:QueryResource, stubQuery:QueryResource, schema:SchemaResource) { - const groupBy = _.find((schema.groupBy.allowedValues as QueryGroupByResource[]), candidate => { - return stubQuery.groupBy && stubQuery.groupBy.href === candidate.href; - }) as any; + const groupBy = _.find((schema.groupBy.allowedValues as QueryGroupByResource[]), (candidate) => stubQuery.groupBy && stubQuery.groupBy.href === candidate.href) as any; query.groupBy = groupBy; } diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts index 1ea6400eec..c782f76a17 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-list.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,31 +26,32 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackagesListInvalidQueryService } from './wp-list-invalid-query.service'; -import { WorkPackageStatesInitializationService } from './wp-states-initialization.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { States } from 'core-app/core/states/states.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { StateService } from '@uirouter/core'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { from, Observable, of } from 'rxjs'; -import { input } from "reactivestates"; -import { catchError, mergeMap, share, switchMap, take } from "rxjs/operators"; +import { input } from 'reactivestates'; +import { + catchError, mergeMap, share, switchMap, take, +} from 'rxjs/operators'; import { - PaginationUpdateObject, - WorkPackageViewPaginationService -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { APIv3QueriesPaths } from "core-app/core/apiv3/endpoints/queries/apiv3-queries-paths"; -import { APIv3QueryPaths } from "core-app/core/apiv3/endpoints/queries/apiv3-query-paths"; -import { PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; + WorkPackageViewPaginationService, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { APIv3QueriesPaths } from 'core-app/core/apiv3/endpoints/queries/apiv3-queries-paths'; +import { APIv3QueryPaths } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-paths'; +import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { WorkPackageStatesInitializationService } from './wp-states-initialization.service'; +import { WorkPackagesListInvalidQueryService } from './wp-list-invalid-query.service'; export interface QueryDefinition { queryParams:{ query_id?:string, query_props?:string }; @@ -59,7 +60,6 @@ export interface QueryDefinition { @Injectable() export class WorkPackagesListService { - // We remember the query requests coming in so we can ensure only the latest request is being tended to private queryRequests = input(); @@ -67,13 +67,9 @@ export class WorkPackagesListService { private queryLoading = this.queryRequests .values$() .pipe( - switchMap((q:QueryDefinition) => { - return from(this.ensurePerPageKnown().then(() => q)); - }), + switchMap((q:QueryDefinition) => from(this.ensurePerPageKnown().then(() => q))), // Stream the query request, switchMap will call previous requests to be cancelled - switchMap((q:QueryDefinition) => - this.streamQueryRequest(q.queryParams, q.projectIdentifier) - ), + switchMap((q:QueryDefinition) => this.streamQueryRequest(q.queryParams, q.projectIdentifier)), // Map the observable from the stream to a new one that completes when states are loaded mergeMap((query:QueryResource) => { // load the form if needed @@ -85,22 +81,22 @@ export class WorkPackagesListService { }), // Share any consecutive requests to the same resource, this is due to switchMap // diverting observables to the LATEST emitted. - share() + share(), ); constructor(protected NotificationsService:NotificationsService, - readonly I18n:I18nService, - protected UrlParamsHelper:UrlParamsHelperService, - protected authorisationService:AuthorisationService, - protected $state:StateService, - protected apiV3Service:APIV3Service, - protected states:States, - protected querySpace:IsolatedQuerySpace, - protected pagination:PaginationService, - protected configuration:ConfigurationService, - protected wpTablePagination:WorkPackageViewPaginationService, - protected wpStatesInitialization:WorkPackageStatesInitializationService, - protected wpListInvalidQueryService:WorkPackagesListInvalidQueryService) { + readonly I18n:I18nService, + protected UrlParamsHelper:UrlParamsHelperService, + protected authorisationService:AuthorisationService, + protected $state:StateService, + protected apiV3Service:APIV3Service, + protected states:States, + protected querySpace:IsolatedQuerySpace, + protected pagination:PaginationService, + protected configuration:ConfigurationService, + protected wpTablePagination:WorkPackageViewPaginationService, + protected wpStatesInitialization:WorkPackageStatesInitializationService, + protected wpListInvalidQueryService:WorkPackagesListInvalidQueryService) { } /** @@ -123,7 +119,7 @@ export class WorkPackagesListService { // Load a default query const queryProps = this.UrlParamsHelper.buildV3GetQueryFromJsonParams(decodedProps); return from(this.handleQueryLoadingError(error, queryProps, queryParams.query_id, projectIdentifier)); - }) + }), ); } @@ -133,12 +129,12 @@ export class WorkPackagesListService { */ public fromQueryParams(queryParams:{ query_id?:string, query_props?:string }, projectIdentifier?:string):Observable { this.queryRequests.clear(); - this.queryRequests.putValue({ queryParams: queryParams, projectIdentifier: projectIdentifier }); + this.queryRequests.putValue({ queryParams, projectIdentifier }); return this .queryLoading .pipe( - take(1) + take(1), ); } @@ -170,13 +166,13 @@ export class WorkPackagesListService { this.queryRequests.clear(); this.queryRequests.putValue({ queryParams: { query_id: query.id || undefined, query_props: queryParams }, - projectIdentifier: projectIdentifier + projectIdentifier, }); return this .queryLoading .pipe( - take(1) + take(1), ); } @@ -241,7 +237,7 @@ export class WorkPackagesListService { .toPromise(); promise - .then(query => { + .then((query) => { this.NotificationsService.addSuccess(this.I18n.t('js.notice_successful_create')); // Reload the query, and then reload the menu @@ -282,7 +278,6 @@ export class WorkPackagesListService { this.states.changes.queries.next(query.id!); }); - return promise; } @@ -323,7 +318,7 @@ export class WorkPackagesListService { this.NotificationsService.addSuccess(this.I18n.t('js.notice_successful_update')); - this.states.changes.queries.next(query!.id!); + this.states.changes.queries.next(query.id!); }); return promise; @@ -343,7 +338,7 @@ export class WorkPackagesListService { private updateStatesFromQueryOnPromise(promise:Promise):Promise { promise - .then(query => { + .then((query) => { this.wpStatesInitialization.initialize(query, query.results); return query; }); @@ -395,8 +390,7 @@ export class WorkPackagesListService { private async ensurePerPageKnown() { if (this.pagination.isPerPageKnown) { return true; - } else { - return this.configuration.initialized; } + return this.configuration.initialized; } } diff --git a/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts b/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts index add4ff53c4..235afb7e44 100644 --- a/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-list/wp-states-initialization.service.ts @@ -1,51 +1,51 @@ -import { States } from "core-app/core/states/states.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackagesListChecksumService } from './wp-list-checksum.service'; +import { States } from 'core-app/core/states/states.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { take } from "rxjs/operators"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { WorkPackageViewSumService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageViewAdditionalElementsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewPaginationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageViewCollapsedGroupsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { take } from 'rxjs/operators'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageViewAdditionalElementsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { WorkPackagesListChecksumService } from './wp-list-checksum.service'; @Injectable() export class WorkPackageStatesInitializationService { constructor(protected states:States, - protected querySpace:IsolatedQuerySpace, - protected wpTableColumns:WorkPackageViewColumnsService, - protected wpTableGroupBy:WorkPackageViewGroupByService, - protected wpTableGroupFold:WorkPackageViewCollapsedGroupsService, - protected wpTableSortBy:WorkPackageViewSortByService, - protected wpTableFilters:WorkPackageViewFiltersService, - protected wpTableSum:WorkPackageViewSumService, - protected wpTableTimeline:WorkPackageViewTimelineService, - protected wpTableHierarchies:WorkPackageViewHierarchiesService, - protected wpTableHighlighting:WorkPackageViewHighlightingService, - protected wpTableRelationColumns:WorkPackageViewRelationColumnsService, - protected wpTablePagination:WorkPackageViewPaginationService, - protected wpTableOrder:WorkPackageViewOrderService, - protected wpTableAdditionalElements:WorkPackageViewAdditionalElementsService, - protected apiV3Service:APIV3Service, - protected wpListChecksumService:WorkPackagesListChecksumService, - protected authorisationService:AuthorisationService, - protected wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService) { + protected querySpace:IsolatedQuerySpace, + protected wpTableColumns:WorkPackageViewColumnsService, + protected wpTableGroupBy:WorkPackageViewGroupByService, + protected wpTableGroupFold:WorkPackageViewCollapsedGroupsService, + protected wpTableSortBy:WorkPackageViewSortByService, + protected wpTableFilters:WorkPackageViewFiltersService, + protected wpTableSum:WorkPackageViewSumService, + protected wpTableTimeline:WorkPackageViewTimelineService, + protected wpTableHierarchies:WorkPackageViewHierarchiesService, + protected wpTableHighlighting:WorkPackageViewHighlightingService, + protected wpTableRelationColumns:WorkPackageViewRelationColumnsService, + protected wpTablePagination:WorkPackageViewPaginationService, + protected wpTableOrder:WorkPackageViewOrderService, + protected wpTableAdditionalElements:WorkPackageViewAdditionalElementsService, + protected apiV3Service:APIV3Service, + protected wpListChecksumService:WorkPackagesListChecksumService, + protected authorisationService:AuthorisationService, + protected wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService) { } /** @@ -112,7 +112,7 @@ export class WorkPackageStatesInitializationService { this.authorisationService.initModelAuth('work_packages', results.$links); - results.elements.forEach(wp => this.apiV3Service.work_packages.cache.updateWorkPackage(wp, true)); + results.elements.forEach((wp) => this.apiV3Service.work_packages.cache.updateWorkPackage(wp, true)); this.querySpace.results.putValue(results); diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts index 044ba8ef08..c3487f896e 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-create.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,41 +26,48 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Directive, Injector, OnInit, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, Directive, Injector, OnInit, ViewChild, +} from '@angular/core'; import { StateService, Transition } from '@uirouter/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { RootResource } from "core-app/features/hal/resources/root-resource"; -import { WorkPackageCreateService } from './wp-create.service'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { RootResource } from 'core-app/features/hal/resources/root-resource'; import { takeWhile } from 'rxjs/operators'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; import * as URI from 'urijs'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { splitViewRoute } from "core-app/features/work-packages/routing/split-view-routes.helper"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { HalSource, HalSourceLinks } from "core-app/features/hal/resources/hal-resource"; -import { OpTitleService } from "core-app/core/html/op-title.service"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { HalSource } from 'core-app/features/hal/resources/hal-resource'; +import { OpTitleService } from 'core-app/core/html/op-title.service'; +import { WorkPackageCreateService } from './wp-create.service'; @Directive() export class WorkPackageCreateComponent extends UntilDestroyedMixin implements OnInit { public successState:string = splitViewRoute(this.$state); + public cancelState:string = this.$state.current.data.baseRoute; + public newWorkPackage:WorkPackageResource; + public parentWorkPackage:WorkPackageResource; + public change:WorkPackageChangeset; /** Are we in the copying substates ? */ public copying = false; public stateParams = this.$transition.params('to'); + public text = { - button_settings: this.I18n.t('js.button_settings') + button_settings: this.I18n.t('js.button_settings'), }; @ViewChild(EditFormComponent, { static: false }) protected editForm:EditFormComponent; @@ -69,18 +76,18 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O protected destroyed = false; constructor(public readonly injector:Injector, - protected readonly $transition:Transition, - protected readonly $state:StateService, - protected readonly I18n:I18nService, - protected readonly titleService:OpTitleService, - protected readonly notificationService:WorkPackageNotificationService, - protected readonly states:States, - protected readonly wpCreate:WorkPackageCreateService, - protected readonly wpViewFocus:WorkPackageViewFocusService, - protected readonly wpTableFilters:WorkPackageViewFiltersService, - protected readonly pathHelper:PathHelperService, - protected readonly apiV3Service:APIV3Service, - protected readonly cdRef:ChangeDetectorRef) { + protected readonly $transition:Transition, + protected readonly $state:StateService, + protected readonly I18n:I18nService, + protected readonly titleService:OpTitleService, + protected readonly notificationService:WorkPackageNotificationService, + protected readonly states:States, + protected readonly wpCreate:WorkPackageCreateService, + protected readonly wpViewFocus:WorkPackageViewFocusService, + protected readonly wpTableFilters:WorkPackageViewFiltersService, + protected readonly pathHelper:PathHelperService, + protected readonly apiV3Service:APIV3Service, + protected readonly cdRef:ChangeDetectorRef) { super(); } @@ -122,24 +129,24 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O this.setTitle(); - if (this.stateParams['parent_id']) { + if (this.stateParams.parent_id) { changeset.setValue( 'parent', - { href: this.apiV3Service.work_packages.id(this.stateParams['parent_id']).path } + { href: this.apiV3Service.work_packages.id(this.stateParams.parent_id).path }, ); } // Load the parent simply to display the type name :-/ - if (this.stateParams['parent_id']) { + if (this.stateParams.parent_id) { this .apiV3Service .work_packages - .id(this.stateParams['parent_id']) + .id(this.stateParams.parent_id) .get() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(parent => { + .subscribe((parent) => { this.parentWorkPackage = parent; this.cdRef.detectChanges(); }); @@ -171,7 +178,7 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O protected createdWorkPackage() { const defaults:HalSource = { - _links: {} + _links: {}, }; const type = this.stateParams.type ? parseInt(this.stateParams.type) : undefined; @@ -179,10 +186,10 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O const project = this.stateParams.projectPath; if (type) { - defaults._links['type'] = { href: this.apiV3Service.types.id(type).path }; + defaults._links.type = { href: this.apiV3Service.types.id(type).path }; } if (parent) { - defaults._links['parent'] = { href: this.apiV3Service.work_packages.id(parent).path }; + defaults._links.parent = { href: this.apiV3Service.work_packages.id(parent).path }; } return this.wpCreate.createOrContinueWorkPackage(project, type, defaults); @@ -192,7 +199,7 @@ export class WorkPackageCreateComponent extends UntilDestroyedMixin implements O this.wpCreate .onNewWorkPackage() .pipe( - takeWhile(() => !this.componentDestroyed) + takeWhile(() => !this.componentDestroyed), ) .subscribe((wp:WorkPackageResource) => { this.onSaved({ savedResource: wp, isInitial: true }); diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts index 6c282b90cf..48ebabad76 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-create.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,26 +28,25 @@ import { Injectable, Injector } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HookService } from 'core-app/features/plugins/hook-service'; -import { WorkPackageFilterValues } from "core-app/features/work-packages/components/wp-edit-form/work-package-filter-values"; +import { WorkPackageFilterValues } from 'core-app/features/work-packages/components/wp-edit-form/work-package-filter-values'; import { HalResourceEditingService, - ResourceChangesetCommit -} from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { filter } from "rxjs/operators"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { AuthorisationService } from "core-app/core/model-auth/model-auth.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { HalResource, HalSource, HalSourceLink } from "core-app/features/hal/resources/hal-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; - + ResourceChangesetCommit, +} from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { filter } from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { HalResource, HalSource, HalSourceLink } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; export const newWorkPackageHref = '/api/v3/work_packages/new'; @@ -59,21 +58,21 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { protected newWorkPackageCreatedSubject = new Subject(); constructor(protected injector:Injector, - protected hooks:HookService, - protected apiV3Service:APIV3Service, - protected halResourceService:HalResourceService, - protected querySpace:IsolatedQuerySpace, - protected authorisationService:AuthorisationService, - protected halEditing:HalResourceEditingService, - protected schemaCache:SchemaCacheService, - protected halEvents:HalEventsService) { + protected hooks:HookService, + protected apiV3Service:APIV3Service, + protected halResourceService:HalResourceService, + protected querySpace:IsolatedQuerySpace, + protected authorisationService:AuthorisationService, + protected halEditing:HalResourceEditingService, + protected schemaCache:SchemaCacheService, + protected halEvents:HalEventsService) { super(); this.halEditing .committedChanges .pipe( this.untilDestroyed(), - filter(commit => commit.resource._type === 'WorkPackage' && commit.wasNew) + filter((commit) => commit.resource._type === 'WorkPackage' && commit.wasNew), ) .subscribe((commit:ResourceChangesetCommit) => { this.newWorkPackageCreated(commit.resource); @@ -83,7 +82,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { .changes$(newWorkPackageHref) .pipe( this.untilDestroyed(), - filter(changeset => !changeset) + filter((changeset) => !changeset), ) .subscribe(() => { this.reset(); @@ -107,9 +106,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { .form .forPayload(payload) .toPromise() - .then((form:FormResource) => { - return this.fromCreateForm(form); - }); + .then((form:FormResource) => this.fromCreateForm(form)); } public fromCreateForm(form:FormResource):WorkPackageChangeset { @@ -152,7 +149,6 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { return this.halEditing.edit(wp, form); } - public getEmptyForm(projectIdentifier:string|null|undefined):Promise { if (!this.form) { this.form = this @@ -164,7 +160,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { .toPromise(); } - return this.form as Promise; + return this.form; } public cancelCreation() { @@ -240,12 +236,12 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { protected createNewWithDefaults(projectIdentifier:string|null|undefined, defaults?:HalSource) { return this .withFiltersPayload(projectIdentifier, defaults) - .then(filterDefaults => { + .then((filterDefaults) => { const mergedPayload = _.merge({ _links: {} }, filterDefaults, defaults); return this.createNewWorkPackage(projectIdentifier, mergedPayload).then((change:WorkPackageChangeset) => { if (!change) { - throw 'No new work package was created'; + throw new Error('No new work package was created'); } // We need to apply the defaults again (after them being applied in the form requests) @@ -273,7 +269,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { const query = this.querySpace.query.value; if (query) { - const except:string[] = defaults?._links && defaults._links['type'] ? ['type'] : []; + const except:string[] = defaults?._links && defaults._links.type ? ['type'] : []; new WorkPackageFilterValues(this.injector, query.filters, except) .applyDefaultsFromFilters(object); @@ -307,15 +303,14 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { this.toApiPayload(fromFilter, form.schema); return fromFilter; }); - } else { - return Promise.resolve(fromFilter); } + return Promise.resolve(fromFilter); } private toApiPayload(payload:HalSource, schema:SchemaResource) { const links:string[] = []; - Object.keys(schema.$source).forEach(attribute => { + Object.keys(schema.$source).forEach((attribute) => { if (!['Integer', 'Float', 'Date', @@ -330,7 +325,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { } }); - links.forEach(attribute => { + links.forEach((attribute) => { const value = payload[attribute]; if (value === undefined) { // nothing @@ -353,7 +348,7 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { const payload = form.payload.$plain(); // maintain the reference to the schema - payload['_links']['schema'] = { href: 'new' }; + payload._links.schema = { href: 'new' }; const wp = this.halResourceService.createHalResourceOfType('WorkPackage', payload); @@ -367,11 +362,9 @@ export class WorkPackageCreateService extends UntilDestroyedMixin { wp.__initialized_at = Date.now(); // Set update link to form - wp['update'] = wp.$links.update = form.$links.self; + wp.update = wp.$links.update = form.$links.self; // Use POST /work_packages for saving link - wp['updateImmediately'] = wp.$links.updateImmediately = (payload) => { - return this.apiV3Service.work_packages.post(payload).toPromise(); - }; + wp.updateImmediately = wp.$links.updateImmediately = (payload) => this.apiV3Service.work_packages.post(payload).toPromise(); // We need to provide the schema to the cache so that it is available in the html form to e.g. determine // the editability. diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.component.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.component.ts index ea93695786..e1400c682c 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-new-full-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -31,9 +31,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ selector: 'wp-new-full-view', - host: { 'class': 'work-packages-page--ui-view' }, + host: { class: 'work-packages-page--ui-view' }, templateUrl: './wp-new-full-view.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageNewFullViewComponent extends WorkPackageCreateComponent { public successState = 'work-packages.show'; diff --git a/frontend/src/app/features/work-packages/components/wp-new/wp-new-split-view.component.ts b/frontend/src/app/features/work-packages/components/wp-new/wp-new-split-view.component.ts index 248a1c1b4a..ca50ced1b8 100644 --- a/frontend/src/app/features/work-packages/components/wp-new/wp-new-split-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-new/wp-new-split-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // diff --git a/frontend/src/app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component.ts b/frontend/src/app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component.ts index 231fbe47c8..50eb4e7deb 100644 --- a/frontend/src/app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,22 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { States } from "core-app/core/states/states.service"; +import { States } from 'core-app/core/states/states.service'; import { StateService, TransitionService } from '@uirouter/core'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from "@angular/core"; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, +} from '@angular/core'; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageStaticQueriesService } from 'core-app/features/work-packages/components/wp-query-select/wp-static-queries.service'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { LinkHandling } from "core-app/shared/helpers/link-handling/link-handling"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { MainMenuNavigationService } from "core-app/core/main-menu/main-menu-navigation.service"; -import { MainMenuToggleService } from "core-app/core/main-menu/main-menu-toggle.service"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { MainMenuNavigationService } from 'core-app/core/main-menu/main-menu-navigation.service'; +import { MainMenuToggleService } from 'core-app/core/main-menu/main-menu-toggle.service'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; export type QueryCategory = 'starred'|'public'|'private'|'default'; @@ -75,9 +77,11 @@ export const wpQuerySelectSelector = 'wp-query-select'; }) export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin implements OnInit { @ViewChild('wpQueryMenuSearchInput', { static: true }) _wpQueryMenuSearchInput:ElementRef; + @ViewChild('queryResultsContainer', { static: true }) _queryResultsContainerElement:ElementRef; public loading = false; + public noResults = false; public text = { @@ -89,6 +93,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin scope_private: this.I18n.t('js.label_custom_queries'), no_results: this.I18n.t('js.work_packages.query.text_no_results'), }; + private unregisterTransitionListener:Function; private projectIdentifier:string|null; @@ -98,27 +103,27 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private reportsBodySelector = '.controller-work_packages\\/reports'; private queryResultsContainer:JQuery; + private buttonArrowLeft:JQuery; private searchInput:IQueryAutocompleteJQuery; private initialized = false; - constructor(readonly ref:ChangeDetectorRef, - readonly element:ElementRef, - readonly apiV3Service:APIV3Service, - readonly $state:StateService, - readonly $transitions:TransitionService, - readonly I18n:I18nService, - readonly states:States, - readonly CurrentProject:CurrentProjectService, - readonly loadingIndicator:LoadingIndicatorService, - readonly pathHelper:PathHelperService, - readonly wpStaticQueries:WorkPackageStaticQueriesService, - readonly mainMenuService:MainMenuNavigationService, - readonly toggleService:MainMenuToggleService, - readonly cdRef:ChangeDetectorRef) { + readonly element:ElementRef, + readonly apiV3Service:APIV3Service, + readonly $state:StateService, + readonly $transitions:TransitionService, + readonly I18n:I18nService, + readonly states:States, + readonly CurrentProject:CurrentProjectService, + readonly loadingIndicator:LoadingIndicatorService, + readonly pathHelper:PathHelperService, + readonly wpStaticQueries:WorkPackageStaticQueriesService, + readonly mainMenuService:MainMenuNavigationService, + readonly toggleService:MainMenuToggleService, + readonly cdRef:ChangeDetectorRef) { super(); } @@ -158,9 +163,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private transformQueries(collection:CollectionResource) { const loadedQueries:IAutocompleteItem[] = collection.elements - .map(query => { - return { label: query.name, query: query, query_props: null }; - }); + .map((query) => ({ label: query.name, query, query_props: null })); // Add to the loaded set of queries the fixed set of queries for the current project context const combinedQueries = loadedQueries.concat(this.wpStaticQueries.all); @@ -179,7 +182,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin // public public: [], // private - private: [] + private: [], }; let auto_id = 0; @@ -209,7 +212,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin return _.flatten( [categorized.starred, categorized.default, categorized.public, categorized.private] - .map(items => this.sortByLabel(items)) + .map((items) => this.sortByLabel(items)), ); } @@ -224,18 +227,17 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin .queries .filterNonHidden(this.CurrentProject.identifier) .toPromise() - .then(collection => { - + .then((collection) => { // Update the complete collection - this.searchInput.querycomplete("option", { source: this.transformQueries(collection) }); + this.searchInput.querycomplete('option', { source: this.transformQueries(collection) }); // To visibly show the changes, we need to search again - this.searchInput.querycomplete("search", this.searchInput.val()); + this.searchInput.querycomplete('search', this.searchInput.val()); // To search an empty string would expand all categories again every time // Remember all previously hidden categories and set them again after updating the menu - _.each(this.hiddenCategories, category => { - const thisCategory:string = jQuery(category).attr("category")!; + _.each(this.hiddenCategories, (category) => { + const thisCategory:string = jQuery(category).attr('category')!; this.expandCollapseCategory(thisCategory); }); @@ -265,16 +267,14 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin // The values are added later by the listener also covering // the changes to queries (updateMenuOnChanges()). source: [], - select: (ul:any, selected:{ item:IAutocompleteItem }) => { - return false; // Don't show title of selected query in the input field - }, + select: (ul:any, selected:{ item:IAutocompleteItem }) => false, // Don't show title of selected query in the input field response: (event:any, ui:any) => { // Show the noResults span if we don't have any matches this.noResults = (ui.content.length === 0); }, close: (event:any, ui:any) => { const autocompleteUi = this.queryResultsContainer.find('ul.ui-autocomplete'); - if (!autocompleteUi.is(":visible") && !this.noResults) { + if (!autocompleteUi.is(':visible') && !this.noResults) { autocompleteUi.show(); } }, @@ -282,7 +282,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin let sourceEvent:any|null = _event; while (sourceEvent && sourceEvent.originalEvent) { - sourceEvent = sourceEvent.originalEvent as any; + sourceEvent = sourceEvent.originalEvent; } // Focus the given item, but only when we're using the keyboard. @@ -299,10 +299,10 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin appendTo: '.collapsible-menu--results-container', classes: { 'ui-autocomplete': 'collapsible-menu--search-ul -inplace', - 'ui-menu-divider': 'collapsible-menu--category-icon' + 'ui-menu-divider': 'collapsible-menu--category-icon', }, autoFocus: false, // Don't automatically select first entry since we 'open' the autocomplete on page load - minLength: 0 + minLength: 0, }); } @@ -310,22 +310,22 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin const thisComponent = this; jQuery.widget('custom.querycomplete', jQuery.ui.autocomplete, { - _create: function (this:any) { + _create(this:any) { this._super(); this.widget().menu('option', 'items', '.collapsible-menu--item'); this._search(''); }, - _renderItem: function (this:{}, ul:any, item:IAutocompleteItem) { + _renderItem(this:{}, ul:any, item:IAutocompleteItem) { const link = jQuery('') .addClass('collapsible-menu--item-link') .attr('href', thisComponent.buildQueryItemUrl(item)) .text(item.label); const li = jQuery('
  • ') - .addClass(`ui-menu-item collapsible-menu--item`) + .addClass('ui-menu-item collapsible-menu--item') .attr('id', `collapsible-menu-item-${item.auto_id}`) .attr('data-category', item.category || '') - .data('ui-autocomplete-item', item) // Focus method of autocompleter needs this data for accessibility - if not set, it will throw errors + .data('ui-autocomplete-item', item) // Focus method of autocompleter needs this data for accessibility - if not set, it will throw errors .append(link) .appendTo(ul); @@ -333,10 +333,10 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin return li; }, - _renderMenu: function (this:any, ul:any, items:IAutocompleteItem[]) { + _renderMenu(this:any, ul:any, items:IAutocompleteItem[]) { let currentCategory:QueryCategory; - _.each(items, option => { + _.each(items, (option) => { // Check if item has same category as previous item and if not insert a new category label in the list if (option.category !== currentCategory) { currentCategory = option.category!; @@ -353,7 +353,6 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin this._renderItemData(ul, option); }); - // Scroll to selected element if search is empty if (thisComponent.searchInput.val() === '') { const selected = thisComponent.queryResultsContainer.find('.collapsible-menu--item.selected'); @@ -361,7 +360,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin setTimeout(() => selected[0].scrollIntoView({ behavior: 'auto', block: 'center' }), 20); } } - } + }, }); } @@ -394,16 +393,16 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private labelFunction(category:QueryCategory):string { switch (category) { - case 'starred': - return this.text.scope_starred; - case 'public': - return this.text.scope_global; - case 'private': - return this.text.scope_private; - case 'default': - return this.text.scope_default; - default: - return ''; + case 'starred': + return this.text.scope_starred; + case 'public': + return this.text.scope_global; + case 'private': + return this.text.scope_private; + case 'default': + return this.text.scope_default; + default: + return ''; } } @@ -413,7 +412,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private updateMenuOnChanges() { this.states.changes.queries .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => this.loadQueries()); } @@ -434,7 +433,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin this.$state.go( 'work-packages.partitioned.list', params, - opts + opts, ); this.toggleService.closeWhenOnMobile(); @@ -470,8 +469,8 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private highlightBySelector(selector:string) { // Remove old selection - this.queryResultsContainer.find(".ui-menu-item").removeClass('selected'); - //Find selected element in DOM and highlight it + this.queryResultsContainer.find('.ui-menu-item').removeClass('selected'); + // Find selected element in DOM and highlight it this.queryResultsContainer.find(selector).addClass('selected'); } @@ -483,7 +482,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin private addClickHandler() { this.queryResultsContainer .on('click keydown', '.ui-menu-item a', (evt:JQuery.TriggeredEvent) => { - if (evt.type === 'keydown' && evt.which !== keyCodes.ENTER) { + if (evt.type === 'keydown' && evt.which !== KeyCodes.ENTER) { return true; } @@ -494,7 +493,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin .data('ui-autocomplete-item'); // Either the link is clicked with a modifier, then always cancel any propagation - const clickedWithModifier = evt.type === 'click' && LinkHandling.isClickedWithModifier(evt); + const clickedWithModifier = evt.type === 'click' && isClickedWithModifier(evt); // Or the item is only a static link, then cancel propagation const isStatic = !!item.static_link; @@ -517,7 +516,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin return true; }) .on('click keydown', '.collapsible-menu--category-toggle', (evt:JQuery.TriggeredEvent) => { - if (evt.type === 'keydown' && evt.which !== keyCodes.ENTER) { + if (evt.type === 'keydown' && evt.which !== KeyCodes.ENTER) { return true; } @@ -529,7 +528,7 @@ export class WorkPackageQuerySelectDropdownComponent extends UntilDestroyedMixin } // Remember all hidden catagories - this.hiddenCategories = this.queryResultsContainer.find(".ui-autocomplete--category.hidden"); + this.hiddenCategories = this.queryResultsContainer.find('.ui-autocomplete--category.hidden'); evt.preventDefault(); return false; diff --git a/frontend/src/app/features/work-packages/components/wp-query-select/wp-static-queries.service.ts b/frontend/src/app/features/work-packages/components/wp-query-select/wp-static-queries.service.ts index ef99352d72..d5ac113b91 100644 --- a/frontend/src/app/features/work-packages/components/wp-query-select/wp-static-queries.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-query-select/wp-static-queries.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ import { IAutocompleteItem } from 'core-app/features/work-packages/components/wp-query-select/wp-query-select-dropdown.component'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Injectable } from '@angular/core'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { StateService } from "@uirouter/core"; -import { CurrentUserService } from "core-app/core/current-user/current-user.service"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { StateService } from '@uirouter/core'; +import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; @Injectable() export class WorkPackageStaticQueriesService { constructor(private readonly I18n:I18nService, - private readonly $state:StateService, - private readonly CurrentProject:CurrentProjectService, - private readonly PathHelper:PathHelperService, - private readonly CurrentUserService:CurrentUserService) { + private readonly $state:StateService, + private readonly CurrentProject:CurrentProjectService, + private readonly PathHelper:PathHelperService, + private readonly CurrentUserService:CurrentUserService) { } public text = { @@ -67,23 +67,23 @@ export class WorkPackageStaticQueriesService { { identifier: 'all_open', label: this.text.all_open, - query_props: null + query_props: null, }, { identifier: 'latest_activity', label: this.text.latest_activity, - query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc","f":[{"n":"status","o":"o","v":[]}]}' + query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc","f":[{"n":"status","o":"o","v":[]}]}', }, { identifier: 'gantt', label: this.text.gantt, - query_props: `{"c":["id","type","subject","status","startDate","dueDate"],"tv":true,"tzl":"auto","tll":"{\\"left\\":\\"startDate\\",\\"right\\":\\"dueDate\\",\\"farRight\\":\\"subject\\"}","hi":true,"g":"","t":"startDate:asc","f":[{"n":"status","o":"o","v":[]}]}` + query_props: '{"c":["id","type","subject","status","startDate","dueDate"],"tv":true,"tzl":"auto","tll":"{\\"left\\":\\"startDate\\",\\"right\\":\\"dueDate\\",\\"farRight\\":\\"subject\\"}","hi":true,"g":"","t":"startDate:asc","f":[{"n":"status","o":"o","v":[]}]}', }, { identifier: 'recently_created', label: this.text.recently_created, - query_props: '{"c":["id","subject","type","status","assignee","createdAt"],"hi":false,"g":"","t":"createdAt:desc","f":[{"n":"status","o":"o","v":[]}]}' - } + query_props: '{"c":["id","subject","type","status","assignee","createdAt"],"hi":false,"g":"","t":"createdAt:desc","f":[{"n":"status","o":"o","v":[]}]}', + }, ] as IAutocompleteItem[]; const projectIdentifier = this.CurrentProject.identifier; @@ -91,7 +91,7 @@ export class WorkPackageStaticQueriesService { items.push({ identifier: 'summary', label: this.text.summary, - static_link: this.PathHelper.projectWorkPackagesPath(projectIdentifier) + '/report' + static_link: `${this.PathHelper.projectWorkPackagesPath(projectIdentifier)}/report`, }); } @@ -100,13 +100,13 @@ export class WorkPackageStaticQueriesService { { identifier: 'created_by_me', label: this.text.created_by_me, - query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"author","o":"=","v":["me"]}]}' + query_props: '{"c":["id","subject","type","status","assignee","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"author","o":"=","v":["me"]}]}', }, { identifier: 'assigned_to_me', label: this.text.assigned_to_me, - query_props: '{"c":["id","subject","type","status","author","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"assigneeOrGroup","o":"=","v":["me"]}]}' - } + query_props: '{"c":["id","subject","type","status","author","updatedAt"],"hi":false,"g":"","t":"updatedAt:desc,id:asc","f":[{"n":"status","o":"o","v":[]},{"n":"assigneeOrGroup","o":"=","v":["me"]}]}', + }, ]); } @@ -120,9 +120,7 @@ export class WorkPackageStaticQueriesService { delete queryProps.pa; const queryPropsString = JSON.stringify(queryProps); - const matched = this.all.find( item => - item.query_props && item.query_props === queryPropsString - ); + const matched = this.all.find((item) => item.query_props && item.query_props === queryPropsString); if (matched) { return matched.label; @@ -130,9 +128,9 @@ export class WorkPackageStaticQueriesService { } // Try to detect the all open filter - if (query.filters.length === 1 && // Only one filter - query.filters[0].id === 'status' && // that is status - query.filters[0].operator.id === 'o') { // and is open + if (query.filters.length === 1 // Only one filter + && query.filters[0].id === 'status' // that is status + && query.filters[0].operator.id === 'o') { // and is open return this.text.all_open; } diff --git a/frontend/src/app/features/work-packages/components/wp-query/query-column.ts b/frontend/src/app/features/work-packages/components/wp-query/query-column.ts index db199d25f2..9dec0836fa 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/query-column.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/query-column.ts @@ -1,5 +1,5 @@ +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; export const queryColumnTypes = { PROPERTY: 'QueryColumn::Property', RELATION_OF_TYPE: 'QueryColumn::RelationOfType', diff --git a/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts b/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts index 1f235f89cb..ef0a450e64 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/query-filters.service.ts @@ -1,13 +1,13 @@ -import { Injectable } from "@angular/core"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; +import { Injectable } from '@angular/core'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; import { QueryFilterInstanceSchemaResource, - QueryFilterInstanceSchemaResourceLinks -} from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; + +} from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; @Injectable() export class QueryFiltersService { @@ -20,7 +20,7 @@ export class QueryFiltersService { */ private getFilterSchema(filter:QueryFilterInstanceResource, form:QueryFormResource):QueryFilterInstanceSchemaResource|undefined { const available = form.$embedded.schema.filtersSchemas.elements; - return _.find(available, schema => schema.allowedFilterValue.href === filter.filter.href); + return _.find(available, (schema) => schema.allowedFilterValue.href === filter.filter.href); } /** @@ -30,7 +30,7 @@ export class QueryFiltersService { * @param form */ public mapSchemasIntoFilters(query:QueryResource, form:QueryFormResource) { - query.filters.forEach(filter => { + query.filters.forEach((filter) => { const schema = this.getFilterSchema(filter, form)!; filter.$links.schema = schema.$links.self; this.schemaCache.update(filter, schema); @@ -38,7 +38,7 @@ export class QueryFiltersService { } public setSchemas(schemas:CollectionResource) { - schemas.elements.forEach(schema => { + schemas.elements.forEach((schema) => { this.schemaCache.updateValue(schema.$links.self.href!, schema); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts b/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts index 2fc5b40085..e3b94d61c6 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/query-param-listener.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,18 +27,21 @@ //++ import { Injectable, Injector } from '@angular/core'; -import { WorkPackagesListChecksumService } from "core-app/features/work-packages/components/wp-list/wp-list-checksum.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { TransitionService } from "@uirouter/core"; -import { Subject } from "rxjs"; +import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { TransitionService } from '@uirouter/core'; +import { Subject } from 'rxjs'; @Injectable() export class QueryParamListenerService { readonly wpListChecksumService:WorkPackagesListChecksumService = this.injector.get(WorkPackagesListChecksumService); + readonly wpListService:WorkPackagesListService = this.injector.get(WorkPackagesListService); + readonly $transitions:TransitionService = this.injector.get(TransitionService); public observe$ = new Subject(); + public queryChangeListener:Function; constructor(readonly injector:Injector) { diff --git a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts index 2f8f3a7f12..030612c9d3 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,65 +26,65 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ +/* jshint expr: true */ import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -describe('UrlParamsHelper', function() { +describe('UrlParamsHelper', () => { const paginationStub = { - getPerPage: () => 20 + getPerPage: () => 20, } as any; const UrlParamsHelper = new UrlParamsHelperService(paginationStub); let queryString; - describe('buildQueryString', function() { + describe('buildQueryString', () => { const params = { ids: [1, 2, 3], - str: '@#$%' + str: '@#$%', }; let queryString:string; - beforeEach(function() { + beforeEach(() => { queryString = UrlParamsHelper.buildQueryString(params)!; }); - it('concatenates propertys with \'&\'', function() { + it("concatenates propertys with '&'", () => { expect(queryString.split('&').length).toEqual(4); }); - it('escapes special characters', function() { + it('escapes special characters', () => { expect(queryString.indexOf('@') === -1).toBeTruthy(); }); }); - describe('encodeQueryJsonParams', function(){ + describe('encodeQueryJsonParams', () => { let query:any; let additional:any; - beforeEach(function() { + beforeEach(() => { const filter1 = { id: 'soße', name: 'soße_id', type: 'list_model', operator: { - id: '=' + id: '=', }, filter: { - href: '/api/filter/soße' + href: '/api/filter/soße', }, - values: ['knoblauch'] + values: ['knoblauch'], }; const filter2 = { id: 'created_at', type: 'datetime_past', operator: { - id: ' { const encodedJSON = UrlParamsHelper.encodeQueryJsonParams(query, additional); - const expectedJSON = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hl\":\"disabled\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc\",\"f\":[{\"n\":\"soße\",\"o\":\"=\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\" { let params:string; - beforeEach(function() { - params = "{\"c\":[\"type\",\"status\",\"soße\"],\"s\":true,\"tv\":true,\"tzl\":\"days\",\"hl\":\"inline\",\"hi\":true,\"g\":\"status\",\"t\":\"type:desc,status:asc\",\"f\":[{\"n\":\"soße\",\"o\":\"=\",\"v\":[\"knoblauch\"]},{\"n\":\"created_at\",\"o\":\" { + params = '{"c":["type","status","soße"],"s":true,"tv":true,"tzl":"days","hl":"inline","hi":true,"g":"status","t":"type:desc,status:asc","f":[{"n":"soße","o":"=","v":["knoblauch"]},{"n":"created_at","o":" { const decodedQueryParams = UrlParamsHelper.buildV3GetQueryFromJsonParams(params); const expected = { @@ -140,52 +140,52 @@ describe('UrlParamsHelper', function() { { soße: { operator: '=', - values: ['knoblauch'] - } + values: ['knoblauch'], + }, }, { created_at: { operator: ' { let query:any; let additional:any; - it('decodes query params to object', function() { + it('decodes query params to object', () => { const filter1 = { id: 'soße', name: 'soße_id', type: 'list_model', operator: { - id: '=' + id: '=', }, filter: { - href: '/api/filter/soße' + href: '/api/filter/soße', }, - values: ['knoblauch'] + values: ['knoblauch'], }; const filter2 = { id: 'created_at', type: 'datetime_past', operator: { - id: ' { const filter1 = { id: 'customField1', operator: { - id: '=' + id: '=', }, filter: { - href: '/api/filter/customField1' + href: '/api/filter/customField1', }, values: [ { - _type: "CustomOption", - value: "cde", - href: "/api/v3/custom_options/2" + _type: 'CustomOption', + value: 'cde', + href: '/api/v3/custom_options/2', }, { - _type: "CustomOption", - value: "abc", - href: "/api/v3/custom_options/7" - } - ] + _type: 'CustomOption', + value: 'abc', + href: '/api/v3/custom_options/7', + }, + ], }; query = { filters: [filter1], @@ -276,7 +276,7 @@ describe('UrlParamsHelper', function() { groupBy: '', timelineZoomLevel: 0, highlightingMode: 'inline', - sums: false + sums: false, }; additional = {}; @@ -289,9 +289,9 @@ describe('UrlParamsHelper', function() { { customField1: { operator: '=', - values: ['2', '7'] - } - } + values: ['2', '7'], + }, + }, ]), groupBy: '', showSums: false, @@ -299,7 +299,7 @@ describe('UrlParamsHelper', function() { showHierarchies: false, highlightingMode: 'inline', - sortBy: '[]' + sortBy: '[]', }; expect(_.isEqual(v3Params, expected)).toBeTruthy(); diff --git a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts index 977efa3291..137799bd33 100644 --- a/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts +++ b/frontend/src/app/features/work-packages/components/wp-query/url-params-helper.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; import { Injectable } from '@angular/core'; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { ApiV3Filter, FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { ApiV3Filter, FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; @Injectable({ providedIn: 'root' }) export class UrlParamsHelperService { - public constructor(public paginationService:PaginationService) { } @@ -59,8 +58,8 @@ export class UrlParamsHelperService { if (v !== null && typeof v === 'object') { v = JSON.stringify(v); } - parts.push(encodeURIComponent(key) + '=' + - encodeURIComponent(v)); + parts.push(`${encodeURIComponent(key)}=${ + encodeURIComponent(v)}`); }); }); @@ -87,9 +86,7 @@ export class UrlParamsHelperService { } private encodeColumns(paramsData:any, query:QueryResource) { - paramsData.c = query.columns.map(function (column) { - return column.id!; - }); + paramsData.c = query.columns.map((column) => column.id); return paramsData; } @@ -111,7 +108,7 @@ export class UrlParamsHelperService { private encodeHighlightedAttributes(paramsData:any, query:QueryResource) { if (query.highlightingMode === 'inline') { if (Array.isArray(query.highlightedAttributes) && query.highlightedAttributes.length > 0) { - paramsData.hla = query.highlightedAttributes.map(el => el.id); + paramsData.hla = query.highlightedAttributes.map((el) => el.id); } } return paramsData; @@ -121,9 +118,7 @@ export class UrlParamsHelperService { if (query.sortBy) { paramsData.t = query .sortBy - .map(function (sort:QuerySortByResource) { - return sort.id!.replace('-', ':'); - }) + .map((sort:QuerySortByResource) => sort.id!.replace('-', ':')) .join(); } return paramsData; @@ -133,14 +128,14 @@ export class UrlParamsHelperService { if (filters && filters.length > 0) { paramsData.f = filters .map((filter:any) => { - var id = filter.id; + const { id } = filter; - var operator = filter.operator.id; + const operator = filter.operator.id; return { n: id, o: operator, - v: _.map(filter.values, (v) => this.queryFilterValueToParam(v)) + v: _.map(filter.values, (v) => this.queryFilterValueToParam(v)), }; }); } else { @@ -164,20 +159,19 @@ export class UrlParamsHelperService { return paramsData; } - public buildV3GetQueryFromJsonParams(updateJson:string|null) { - var queryData:any = { - pageSize: this.paginationService.getPerPage() + const queryData:any = { + pageSize: this.paginationService.getPerPage(), }; if (!updateJson) { return queryData; } - var properties = JSON.parse(updateJson); + const properties = JSON.parse(updateJson); if (properties.c) { - queryData["columns[]"] = properties.c.map((column:any) => column); + queryData['columns[]'] = properties.c.map((column:any) => column); } if (properties.s) { queryData.showSums = properties.s; @@ -204,7 +198,7 @@ export class UrlParamsHelperService { } if (properties.hla) { - queryData["highlightedAttributes[]"] = properties.hla.map((column:any) => column); + queryData['highlightedAttributes[]'] = properties.hla.map((column:any) => column); } if (properties.hi === false || properties.hi === true) { @@ -215,14 +209,14 @@ export class UrlParamsHelperService { // Filters if (properties.f) { - var filters = properties.f.map(function (urlFilter:any) { - var attributes = { - operator: decodeURIComponent(urlFilter.o) + const filters = properties.f.map((urlFilter:any) => { + const attributes = { + operator: decodeURIComponent(urlFilter.o), }; if (urlFilter.v) { // the array check is only there for backwards compatibility reasons. // Nowadays, it will always be an array; - var vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v]; + const vs = Array.isArray(urlFilter.v) ? urlFilter.v : [urlFilter.v]; _.extend(attributes, { values: vs }); } const filterData:any = {}; @@ -251,9 +245,9 @@ export class UrlParamsHelperService { } public buildV3GetQueryFromQueryResource(query:QueryResource, additionalParams:any = {}, contextual:any = {}) { - var queryData:any = {}; + const queryData:any = {}; - queryData["columns[]"] = this.buildV3GetColumnsFromQueryResource(query); + queryData['columns[]'] = this.buildV3GetColumnsFromQueryResource(query); queryData.showSums = query.sums; queryData.timelineVisible = !!query.timelineVisible; @@ -267,7 +261,7 @@ export class UrlParamsHelperService { } if (query.highlightedAttributes && query.highlightingMode === 'inline') { - queryData['highlightedAttributes[]'] = query.highlightedAttributes.map(el => el.href); + queryData['highlightedAttributes[]'] = query.highlightedAttributes.map((el) => el.href); } if (query.displayRepresentation) { @@ -287,25 +281,24 @@ export class UrlParamsHelperService { } public queryFilterValueToParam(value:any) { - if (typeof(value) === 'boolean') { + if (typeof (value) === 'boolean') { return value ? 't' : 'f'; } if (!value) { return ''; - } else if (value.id) { + } if (value.id) { return value.id.toString(); - } else if (value.href) { + } if (value.href) { return value.href.split('/').pop().toString(); - } else { - return value.toString(); } + return value.toString(); } private buildV3GetColumnsFromQueryResource(query:QueryResource) { if (query.columns) { return query.columns.map((column:any) => column.id || column.idFromLink); - } else if (query._links.columns) { + } if (query._links.columns) { return query._links.columns.map((column:HalLink) => { const id = column.href!; @@ -318,7 +311,7 @@ export class UrlParamsHelperService { const newFilters = filters.map((filter:QueryFilterInstanceResource) => { const id = this.buildV3GetFilterIdFromFilter(filter); const operator = this.buildV3GetOperatorIdFromFilter(filter); - const values = this.buildV3GetValuesFromFilter(filter).map(value => { + const values = this.buildV3GetValuesFromFilter(filter).map((value) => { _.each(replacements, (val:string, key:string) => { value = value.replace(`{${key}}`, val); }); @@ -327,7 +320,7 @@ export class UrlParamsHelperService { }); const filterHash:ApiV3Filter = {}; - filterHash[id] = { operator: operator as FilterOperator, values: values }; + filterHash[id] = { operator: operator as FilterOperator, values }; return filterHash; }); @@ -348,20 +341,17 @@ export class UrlParamsHelperService { private buildV3GetOperatorIdFromFilter(filter:QueryFilterInstanceResource) { if (filter.operator) { return filter.operator.id || filter.operator.idFromLink; - } else { - const href = filter._links.operator.href; - - return this.idFromHref(href); } + const { href } = filter._links.operator; + + return this.idFromHref(href); } private buildV3GetValuesFromFilter(filter:QueryFilterInstanceResource) { if (filter.values) { return _.map(filter.values, (v:any) => this.queryFilterValueToParam(v)); - } else { - return _.map(filter._links.values, (v:any) => this.idFromHref(v.href)); } - + return _.map(filter._links.values, (v:any) => this.idFromHref(v.href)); } private buildV3GetSortByFromQuery(query:QueryResource) { @@ -369,13 +359,12 @@ export class UrlParamsHelperService { const sortByIds = sortBys.map((sort:QuerySortByResource) => { if (sort.id) { return sort.id; - } else { - const href = sort.href!; + } + const href = sort.href!; - const id = this.idFromHref(href); + const id = this.idFromHref(href); - return id; - } + return id; }); return JSON.stringify(sortByIds.map((id:string) => id.split('-'))); diff --git a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts index bbd61f0720..ac9fb3dc60 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts @@ -1,9 +1,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { combineLatest } from 'rxjs'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; - +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; @Component({ templateUrl: './wp-relations-count.html', @@ -11,10 +10,11 @@ import { WorkPackageRelationsService } from "core-app/features/work-packages/com }) export class WorkPackageRelationsCountComponent extends UntilDestroyedMixin implements OnInit { @Input('wpId') wpId:string; + public count = 0; constructor(protected apiV3Service:APIV3Service, - protected wpRelations:WorkPackageRelationsService) { + protected wpRelations:WorkPackageRelationsService) { super(); } @@ -30,9 +30,9 @@ export class WorkPackageRelationsCountComponent extends UntilDestroyedMixin impl .apiV3Service .work_packages .id(this.wpId) - .requireAndStream() + .requireAndStream(), ]).pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe(([relations, workPackage]) => { const relationCount = _.size(relations); const childrenCount = _.size(workPackage.children); diff --git a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-watchers-count.component.ts b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-watchers-count.component.ts index 514cfb94df..e6b75387df 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-watchers-count.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-watchers-count.component.ts @@ -1,21 +1,24 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageWatchersService } from "core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service"; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; @Component({ templateUrl: './wp-relations-count.html', selector: 'wp-watchers-count', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageWatchersCountComponent extends UntilDestroyedMixin implements OnInit { @Input('wpId') wpId:string; + public count = 0; constructor(protected apiV3Service:APIV3Service, - protected wpWatcherService:WorkPackageWatchersService, - protected cdRef:ChangeDetectorRef) { + protected wpWatcherService:WorkPackageWatchersService, + protected cdRef:ChangeDetectorRef) { super(); } @@ -26,7 +29,7 @@ export class WorkPackageWatchersCountComponent extends UntilDestroyedMixin imple .id(this.wpId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((workPackage) => { this.wpWatcherService .require(workPackage) diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts index 5b0c235307..454efbcb62 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,19 +27,18 @@ //++ import { Injectable, Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WpRelationInlineCreateServiceInterface } from "core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface"; -import { WpRelationInlineAddExistingComponent } from "core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WpRelationInlineCreateServiceInterface } from 'core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface'; +import { WpRelationInlineAddExistingComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; @Injectable() export class WpChildrenInlineCreateService extends WorkPackageInlineCreateService implements WpRelationInlineCreateServiceInterface { - constructor(readonly injector:Injector, - protected readonly wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, - protected readonly schemaCache:SchemaCacheService) { + protected readonly wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, + protected readonly schemaCache:SchemaCacheService) { super(injector); } @@ -89,7 +88,7 @@ export class WpChildrenInlineCreateService extends WorkPackageInlineCreateServic */ public readonly buttonTexts = { reference: this.I18n.t('js.relation_buttons.add_existing_child'), - create: this.I18n.t('js.relation_buttons.add_new_child') + create: this.I18n.t('js.relation_buttons.add_new_child'), }; private get schema() { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts index 5bc647b394..4c679f439e 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,33 +29,35 @@ import { Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { OpUnlinkTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action'; import { OpTableActionFactory } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WorkPackageRelationQueryBase } from "core-app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base"; -import { WpChildrenInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service"; -import { filter } from "rxjs/operators"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { GroupDescriptor } from "core-app/features/work-packages/components/wp-single-view/wp-single-view.component"; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WorkPackageRelationQueryBase } from 'core-app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base'; +import { WpChildrenInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service'; +import { filter } from 'rxjs/operators'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { GroupDescriptor } from 'core-app/features/work-packages/components/wp-single-view/wp-single-view.component'; @Component({ selector: 'wp-children-query', templateUrl: '../wp-relation-query.html', providers: [ { provide: WorkPackageInlineCreateService, useClass: WpChildrenInlineCreateService }, - ] + ], }) export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryBase implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public query:QueryResource; /** An optional group descriptor if we're rendering on the single view */ @Input() public group?:GroupDescriptor; + @Input() public addExistingChildEnabled = false; public tableActions:OpTableActionFactory[] = [ @@ -65,17 +67,17 @@ export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryB (child:WorkPackageResource) => { this.embeddedTable.loadingIndicator = this.wpRelationsHierarchyService.removeChild(child); }, - (child:WorkPackageResource) => !!child.changeParent - ) + (child:WorkPackageResource) => !!child.changeParent, + ), ]; constructor(protected wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, - protected PathHelper:PathHelperService, - protected wpInlineCreate:WorkPackageInlineCreateService, - protected halEvents:HalEventsService, - protected apiV3Service:APIV3Service, - protected queryUrlParamsHelper:UrlParamsHelperService, - readonly I18n:I18nService) { + protected PathHelper:PathHelperService, + protected wpInlineCreate:WorkPackageInlineCreateService, + protected halEvents:HalEventsService, + protected apiV3Service:APIV3Service, + protected queryUrlParamsHelper:UrlParamsHelperService, + readonly I18n:I18nService) { super(queryUrlParamsHelper); } @@ -89,13 +91,13 @@ export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryB // Fire event that children were added this.wpInlineCreate.newInlineWorkPackageCreated .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((toId:string) => { this.halEvents.push(this.workPackage, { eventType: 'association', relatedWorkPackage: toId, - relationType: 'child' + relationType: 'child', }); }); @@ -107,7 +109,7 @@ export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryB .observe() .pipe( filter(() => this.embeddedTable && this.embeddedTable.isInitialized), - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => this.refreshTable()); } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts index 2c4e9c19c3..3af63ebb28 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,25 +28,25 @@ import { Component, Inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WorkPackageInlineCreateComponent } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.component"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { WpRelationInlineCreateServiceInterface } from "core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; - +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WorkPackageInlineCreateComponent } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.component'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { WpRelationInlineCreateServiceInterface } from 'core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @Component({ - templateUrl: './wp-relation-inline-add-existing.component.html' + templateUrl: './wp-relation-inline-add-existing.component.html', }) export class WpRelationInlineAddExistingComponent { public selectedWpId:string; + public isDisabled = false; public queryFilters = this.buildQueryFilters(); @@ -56,14 +56,14 @@ export class WpRelationInlineAddExistingComponent { }; constructor(protected readonly parent:WorkPackageInlineCreateComponent, - @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateServiceInterface, - protected apiV3Service:APIV3Service, - protected wpRelations:WorkPackageRelationsService, - protected notificationService:WorkPackageNotificationService, - protected halEvents:HalEventsService, - protected urlParamsHelper:UrlParamsHelperService, - protected querySpace:IsolatedQuerySpace, - protected readonly I18n:I18nService) { + @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateServiceInterface, + protected apiV3Service:APIV3Service, + protected wpRelations:WorkPackageRelationsService, + protected notificationService:WorkPackageNotificationService, + protected halEvents:HalEventsService, + protected urlParamsHelper:UrlParamsHelperService, + protected querySpace:IsolatedQuerySpace, + protected readonly I18n:I18nService) { } public addExisting() { @@ -126,7 +126,7 @@ export class WpRelationInlineAddExistingComponent { } const relationTypes = RelationResource.RELATION_TYPES(true); - const filters = query.filters.filter(filter => { + const filters = query.filters.filter((filter) => { const id = this.urlParamsHelper.buildV3GetFilterIdFromFilter(filter); return relationTypes.indexOf(id) === -1; }); diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts index 95223561bb..9a4ce4b902 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,12 +27,12 @@ //++ import { Injectable, Injector, OnDestroy } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WpRelationInlineAddExistingComponent } from "core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { WpRelationInlineCreateServiceInterface } from "core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WpRelationInlineAddExistingComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { WpRelationInlineCreateServiceInterface } from 'core-app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Injectable() export class WpRelationInlineCreateService extends WorkPackageInlineCreateService implements WpRelationInlineCreateServiceInterface, OnDestroy { @@ -68,9 +68,8 @@ export class WpRelationInlineCreateService extends WorkPackageInlineCreateServic if (relation !== undefined) { return this.wpRelations.removeRelation(relation); - } else { - return Promise.reject(); } + return Promise.reject(); } /** @@ -78,7 +77,6 @@ export class WpRelationInlineCreateService extends WorkPackageInlineCreateServic */ public referenceTarget:WorkPackageResource|null = null; - public get canAdd() { return !!(this.referenceTarget && this.canCreateWorkPackages && this.referenceTarget.addRelation); } @@ -92,6 +90,6 @@ export class WpRelationInlineCreateService extends WorkPackageInlineCreateServic */ public readonly buttonTexts = { reference: this.I18n.t('js.relation_buttons.add_existing'), - create: this.I18n.t('js.relation_buttons.create_new') + create: this.I18n.t('js.relation_buttons.create_new'), }; } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts index b52bce4100..c4362e6ea9 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,37 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { + Component, Inject, Input, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; import { OpUnlinkTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action'; import { OpTableActionFactory } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WorkPackageRelationQueryBase } from "core-app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base"; -import { WpRelationInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { filter } from "rxjs/operators"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { GroupDescriptor } from "core-app/features/work-packages/components/wp-single-view/wp-single-view.component"; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WorkPackageRelationQueryBase } from 'core-app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base'; +import { WpRelationInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { filter } from 'rxjs/operators'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { GroupDescriptor } from 'core-app/features/work-packages/components/wp-single-view/wp-single-view.component'; @Component({ selector: 'wp-relation-query', templateUrl: '../wp-relation-query.html', providers: [ - { provide: WorkPackageInlineCreateService, useClass: WpRelationInlineCreateService } - ] + { provide: WorkPackageInlineCreateService, useClass: WpRelationInlineCreateService }, + ], }) export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryBase implements OnInit { @Input() public workPackage:WorkPackageResource; @Input() public query:QueryResource; + @Input() public group:GroupDescriptor; public tableActions:OpTableActionFactory[] = [ @@ -66,17 +69,17 @@ export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryB .then(() => this.refreshTable()) .catch((error) => this.notificationService.handleRawError(error, this.workPackage)); }, - (child:WorkPackageResource) => !!child.changeParent - ) + (child:WorkPackageResource) => !!child.changeParent, + ), ]; constructor(protected readonly PathHelper:PathHelperService, - @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateService, - protected readonly wpRelations:WorkPackageRelationsService, - protected readonly halEvents:HalEventsService, - protected readonly queryUrlParamsHelper:UrlParamsHelperService, - protected readonly notificationService:WorkPackageNotificationService, - protected readonly I18n:I18nService) { + @Inject(WorkPackageInlineCreateService) protected readonly wpInlineCreate:WpRelationInlineCreateService, + protected readonly wpRelations:WorkPackageRelationsService, + protected readonly halEvents:HalEventsService, + protected readonly queryUrlParamsHelper:UrlParamsHelperService, + protected readonly notificationService:WorkPackageNotificationService, + protected readonly I18n:I18nService) { super(queryUrlParamsHelper); } @@ -93,15 +96,15 @@ export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryB // Wire the successful saving of a new addition to refreshing the embedded table this.wpInlineCreate.newInlineWorkPackageCreated .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((toId:string) => this.addRelation(toId)); // When relations have changed, refresh this table this.wpRelations.observe(this.workPackage.id!) .pipe( - filter(val => !_.isEmpty(val)), - this.untilDestroyed() + filter((val) => !_.isEmpty(val)), + this.untilDestroyed(), ) .subscribe(() => this.refreshTable()); } @@ -113,10 +116,10 @@ export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryB this.halEvents.push(this.workPackage, { eventType: 'association', relatedWorkPackage: toId, - relationType: this.getRelationTypeFromQuery() + relationType: this.getRelationTypeFromQuery(), }); }) - .catch(error => this.notificationService.handleRawError(error, this.workPackage)); + .catch((error) => this.notificationService.handleRawError(error, this.workPackage)); } private getRelationTypeFromQuery() { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface.ts index 6a70ee8168..5478bfa274 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-inline-create.service.interface.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; export interface WpRelationInlineCreateServiceInterface extends WorkPackageInlineCreateService { - /** * Defines the relation type for the relations inline create */ diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts index fc98fdde2d..c69fa7a947 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/wp-relation-query.base.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Directive, ViewChild } from "@angular/core"; -import { WorkPackageEmbeddedTableComponent } from "core-app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Directive, ViewChild } from '@angular/core'; +import { WorkPackageEmbeddedTableComponent } from 'core-app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Directive() export abstract class WorkPackageRelationQueryBase extends UntilDestroyedMixin { @@ -92,7 +92,7 @@ export abstract class WorkPackageRelationQueryBase extends UntilDestroyedMixin { * Get the filters of the query props */ protected projectValuesCount(query:QueryResource):number|null { - const project = query.filters.find(f => f.id === 'project'); + const project = query.filters.find((f) => f.id === 'project'); return project ? project.values.length : null; } @@ -104,10 +104,9 @@ export abstract class WorkPackageRelationQueryBase extends UntilDestroyedMixin { return this.queryUrlParamsHelper.buildV3GetQueryFromQueryResource( this.query, { valid_subset: true }, - { id: this.workPackage.id! } + { id: this.workPackage.id! }, ); - } else { - return this.query; } + return this.query; } } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts index dd4e1b4c3d..b504a4c7fb 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts @@ -1,30 +1,37 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageRelationsService } from '../wp-relations.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; - +import { + ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { WorkPackageRelationsService } from '../wp-relations.service'; @Component({ selector: 'wp-relation-row', - templateUrl: './wp-relation-row.template.html' + templateUrl: './wp-relation-row.template.html', }) export class WorkPackageRelationRowComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public relatedWorkPackage:WorkPackageResource; + @Input() public groupByWorkPackageType:boolean; @ViewChild('relationDescriptionTextarea') readonly relationDescriptionTextarea:ElementRef; public relationType:string; + public showRelationInfo = false; + public showEditForm = false; + public availableRelationTypes:{ label:string, name:string }[]; + public selectedRelationType:{ name:string }; public userInputs = { @@ -39,10 +46,11 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme handler: { active: true, }, - required: false + required: false, }; public relation:RelationResource; + public text = { cancel: this.I18n.t('js.button_cancel'), save: this.I18n.t('js.button_save'), @@ -51,17 +59,17 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme toggleDescription: this.I18n.t('js.relation_buttons.toggle_description'), updateRelation: this.I18n.t('js.relation_buttons.update_relation'), placeholder: { - description: this.I18n.t('js.placeholders.relation_description') - } + description: this.I18n.t('js.placeholders.relation_description'), + }, }; constructor(protected apiV3Service:APIV3Service, - protected notificationService:WorkPackageNotificationService, - protected wpRelations:WorkPackageRelationsService, - protected halEvents:HalEventsService, - protected I18n:I18nService, - protected cdRef:ChangeDetectorRef, - protected PathHelper:PathHelperService) { + protected notificationService:WorkPackageNotificationService, + protected wpRelations:WorkPackageRelationsService, + protected halEvents:HalEventsService, + protected I18n:I18nService, + protected cdRef:ChangeDetectorRef, + protected PathHelper:PathHelperService) { super(); } @@ -71,7 +79,7 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme this.userInputs.newRelationText = this.relation.description || ''; this.availableRelationTypes = RelationResource.LOCALIZED_RELATION_TYPES(false); this.selectedRelationType = _.find(this.availableRelationTypes, - { 'name': this.relation.normalizedType(this.workPackage) })!; + { name: this.relation.normalizedType(this.workPackage) })!; this .apiV3Service @@ -79,7 +87,7 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme .id(this.relatedWorkPackage) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((wp) => { this.relatedWorkPackage = wp; }); @@ -90,8 +98,8 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme * That is, normalize `precedes` where the work package is the `to` link. */ public get normalizedRelationType() { - var type = this.relation.normalizedType(this.workPackage); - return this.I18n.t('js.relation_labels.' + type); + const type = this.relation.normalizedType(this.workPackage); + return this.I18n.t(`js.relation_labels.${type}`); } public get relationReady() { @@ -125,7 +133,8 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme public saveDescription() { this.wpRelations.updateRelation( this.relation, - { description: this.userInputs.newRelationText }) + { description: this.userInputs.newRelationText }, + ) .then((savedRelation:RelationResource) => { this.relation = savedRelation; this.relatedWorkPackage.relatedBy = savedRelation; @@ -154,7 +163,8 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme this.workPackage, this.relatedWorkPackage, this.relation, - this.selectedRelationType.name) + this.selectedRelationType.name, + ) .then((savedRelation:RelationResource) => { this.notificationService.showSave(this.relatedWorkPackage); this.relatedWorkPackage.relatedBy = savedRelation; @@ -176,7 +186,7 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme this.halEvents.push(this.workPackage, { eventType: 'association', relatedWorkPackage: null, - relationType: this.relation.normalizedType(this.workPackage) + relationType: this.relation.normalizedType(this.workPackage), }); this diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts index f3883f77e6..9cf1954fd3 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -36,19 +36,19 @@ import { ViewEncapsulation, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { from, Observable, of } from "rxjs"; -import { catchError, map, tap } from "rxjs/operators"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { ApiV3Filter } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { OpAutocompleterComponent } from "core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { from, Observable, of } from 'rxjs'; +import { catchError, map, tap } from 'rxjs/operators'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { ApiV3Filter } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { OpAutocompleterComponent } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ selector: 'wp-relations-autocomplete', @@ -56,26 +56,32 @@ import { HalResource } from "core-app/features/hal/resources/hal-resource"; // Allow styling the embedded ng-select encapsulation: ViewEncapsulation.None, - styleUrls: ['./wp-relations-autocomplete.sass'] + styleUrls: ['./wp-relations-autocomplete.sass'], }) -export class WorkPackageRelationsAutocomplete { +export class WorkPackageRelationsAutocompleteComponent { readonly text = { - placeholder: this.I18n.t('js.relations_autocomplete.placeholder') + placeholder: this.I18n.t('js.relations_autocomplete.placeholder'), }; @Input() inputPlaceholder:string = this.text.placeholder; + @Input() workPackage:WorkPackageResource; + @Input() selectedRelationType:string; + @Input() filterCandidatesFor:string; /** Do we take the current query filters into account? */ @Input() additionalFilters:ApiV3Filter[] = []; @Input() hiddenOverflowContainer = 'body'; + @ViewChild(OpAutocompleterComponent, { static: true }) public ngSelectComponent:OpAutocompleterComponent; @Output() onCancel = new EventEmitter(); + @Output() onSelected = new EventEmitter(); + @Output() onEmptySelected = new EventEmitter(); // Whether we're currently loading @@ -93,36 +99,37 @@ export class WorkPackageRelationsAutocomplete { return from( this.workPackage.availableRelationCandidates.$link.$fetch({ - query: query, + query, filters: JSON.stringify(this.additionalFilters), - type: this.filterCandidatesFor || this.selectedRelationType - }) as Promise + type: this.filterCandidatesFor || this.selectedRelationType, + }) as Promise, ) .pipe( - map(collection => collection.elements), + map((collection) => collection.elements), catchError((error:unknown) => { this.notificationService.handleRawError(error); return of([]); }), - tap(() => this.isLoading = false) + tap(() => this.isLoading = false), ); }; + public autocompleterOptions = { - resource:'work_packages', - getOptionsFn: this.getAutocompleterData + resource: 'work_packages', + getOptionsFn: this.getAutocompleterData, }; public appendToContainer = 'body'; constructor(private readonly querySpace:IsolatedQuerySpace, - private readonly pathHelper:PathHelperService, - private readonly notificationService:WorkPackageNotificationService, - private readonly CurrentProject:CurrentProjectService, - private readonly halResourceService:HalResourceService, - private readonly schemaCacheService:SchemaCacheService, - private readonly cdRef:ChangeDetectorRef, - private readonly ngZone:NgZone, - private readonly I18n:I18nService) { + private readonly pathHelper:PathHelperService, + private readonly notificationService:WorkPackageNotificationService, + private readonly CurrentProject:CurrentProjectService, + private readonly halResourceService:HalResourceService, + private readonly schemaCacheService:SchemaCacheService, + private readonly cdRef:ChangeDetectorRef, + private readonly ngZone:NgZone, + private readonly I18n:I18nService) { } @HostListener('keydown.escape') diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts index a6762c28db..38d34ad55b 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts @@ -1,21 +1,24 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Component, Input } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; import { WorkPackageRelationsService } from '../wp-relations.service'; -import { Component, Input } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; @Component({ selector: 'wp-relations-create', - templateUrl: './wp-relation-create.template.html' + templateUrl: './wp-relation-create.template.html', }) export class WorkPackageRelationsCreateComponent { @Input() readonly workPackage:WorkPackageResource; public showRelationsCreateForm = false; + public selectedRelationType:string = RelationResource.DEFAULT(); + public selectedWpId:string; + public relationTypes = RelationResource.LOCALIZED_RELATION_TYPES(false); public isDisabled = false; @@ -23,18 +26,16 @@ export class WorkPackageRelationsCreateComponent { public text = { abort: this.I18n.t('js.relation_buttons.abort'), relationType: this.I18n.t('js.relation_buttons.relation_type'), - addNewRelation: this.I18n.t('js.relation_buttons.add_new_relation') + addNewRelation: this.I18n.t('js.relation_buttons.add_new_relation'), }; constructor(readonly I18n:I18nService, - protected wpRelations:WorkPackageRelationsService, - protected notificationService:WorkPackageNotificationService, - protected halEvents:HalEventsService) { + protected wpRelations:WorkPackageRelationsService, + protected notificationService:WorkPackageNotificationService, + protected halEvents:HalEventsService) { } - public createRelation() { - if (!this.selectedRelationType || !this.selectedWpId) { return; } @@ -56,16 +57,16 @@ export class WorkPackageRelationsCreateComponent { return this.wpRelations.addCommonRelation(this.workPackage.id!, this.selectedRelationType, this.selectedWpId) - .then(relation => { + .then((relation) => { this.halEvents.push(this.workPackage, { eventType: 'association', relatedWorkPackage: relation.id!, - relationType: this.selectedRelationType + relationType: this.selectedRelationType, }); this.notificationService.showSave(this.workPackage); this.toggleRelationsCreateForm(); }) - .catch(err => { + .catch((err) => { this.notificationService.handleRawError(err, this.workPackage); this.toggleRelationsCreateForm(); }); diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts index 38ca5e2e43..f7b2a1cc59 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; - - +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { + Component, ElementRef, EventEmitter, Input, Output, ViewChild, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ selector: 'wp-relations-group', - templateUrl: './wp-relations-group.template.html' + templateUrl: './wp-relations-group.template.html', }) export class WorkPackageRelationsGroupComponent { @Input() public relatedWorkPackages:WorkPackageResource[]; + @Input() public workPackage:WorkPackageResource; + @Input() public header:string; + @Input() public firstGroup:boolean; + @Input() public groupByWorkPackageType:boolean; @Output() public onToggleGroupBy = new EventEmitter(); @@ -49,19 +53,19 @@ export class WorkPackageRelationsGroupComponent { public text = { groupByType: this.I18n.t('js.relation_buttons.group_by_wp_type'), - groupByRelation: this.I18n.t('js.relation_buttons.group_by_relation_type') + groupByRelation: this.I18n.t('js.relation_buttons.group_by_relation_type'), }; constructor( - readonly I18n:I18nService) { + readonly I18n:I18nService, + ) { } public get togglerText() { if (this.groupByWorkPackageType) { return this.text.groupByRelation; - } else { - return this.text.groupByType; } + return this.text.groupByType; } public toggleButton() { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts index 195e03ea95..8c3fbf6454 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,33 +28,38 @@ import { Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { take } from 'rxjs/operators'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ selector: 'wp-relations-hierarchy', - templateUrl: './wp-relations-hierarchy.template.html' + templateUrl: './wp-relations-hierarchy.template.html', }) export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; + @Input() public relationType:string; public showEditForm = false; + public workPackagePath:string; + public canHaveChildren:boolean; + public canModifyHierarchy:boolean; + public canAddRelation:boolean; public childrenQueryProps:any; constructor(protected wpRelationsHierarchyService:WorkPackageRelationsHierarchyService, - protected apiV3Service:APIV3Service, - protected PathHelper:PathHelperService, - readonly I18n:I18nService) { + protected apiV3Service:APIV3Service, + protected PathHelper:PathHelperService, + readonly I18n:I18nService) { super(); } @@ -71,7 +76,7 @@ export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin this.childrenQueryProps = { filters: JSON.stringify([{ parent: { operator: '=', values: [this.workPackage.id] } }]), 'columns[]': ['id', 'type', 'subject', 'status'], - showHierarchies: false + showHierarchies: false, }; this @@ -80,7 +85,7 @@ export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin .id(this.workPackage) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; @@ -94,7 +99,7 @@ export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin .id(parentId) .get() .pipe( - take(1) + take(1), ) .subscribe((parent:WorkPackageResource) => { this.workPackage.parent = parent; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts index 4d845e443a..2c6d6a2e4c 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,42 +26,42 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; import { StateService } from '@uirouter/core'; import { Injectable } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; @Injectable() export class WorkPackageRelationsHierarchyService { constructor(protected $state:StateService, - protected states:States, - protected halEvents:HalEventsService, - protected notificationService:WorkPackageNotificationService, - protected pathHelper:PathHelperService, - protected apiV3Service:APIV3Service) { + protected states:States, + protected halEvents:HalEventsService, + protected notificationService:WorkPackageNotificationService, + protected pathHelper:PathHelperService, + protected apiV3Service:APIV3Service) { } public changeParent(workPackage:WorkPackageResource, parentId:string|null) { const payload:any = { - lockVersion: workPackage.lockVersion + lockVersion: workPackage.lockVersion, }; if (parentId) { - payload['_links'] = { + payload._links = { parent: { - href: this.apiV3Service.work_packages.id(parentId).path - } + href: this.apiV3Service.work_packages.id(parentId).path, + }, }; } else { - payload['_links'] = { + payload._links = { parent: { - href: null - } + href: null, + }, }; } @@ -77,7 +77,7 @@ export class WorkPackageRelationsHierarchyService { this.halEvents.push(workPackage, { eventType: 'association', relatedWorkPackage: parentId, - relationType: 'parent' + relationType: 'parent', }); return wp; @@ -99,42 +99,40 @@ export class WorkPackageRelationsHierarchyService { .id(childWpId) .get() .toPromise() - .then((wpToBecomeChild:WorkPackageResource|undefined) => { - return this.changeParent(wpToBecomeChild!, workPackage.id!) - .then(wp => { - // Reload work package - this - .apiV3Service - .work_packages - .id(workPackage) - .refresh(); - - this.halEvents.push(workPackage, { - eventType: 'association', - relatedWorkPackage: wpToBecomeChild!.id!, - relationType: 'child' - }); + .then((wpToBecomeChild:WorkPackageResource|undefined) => this.changeParent(wpToBecomeChild!, workPackage.id) + .then((wp) => { + // Reload work package + this + .apiV3Service + .work_packages + .id(workPackage) + .refresh(); - return wp; + this.halEvents.push(workPackage, { + eventType: 'association', + relatedWorkPackage: wpToBecomeChild!.id!, + relationType: 'child', }); - }); + + return wp; + })); } public addNewChildWp(baseRoute:string, workPackage:WorkPackageResource) { workPackage.project.$load() .then(() => { const args = [ - baseRoute + '.new', + `${baseRoute}.new`, { - parent_id: workPackage.id - } + parent_id: workPackage.id, + }, ]; if (this.$state.includes('work-packages.show')) { args[0] = 'work-packages.new'; } - (this.$state).go(...args); + ( this.$state).go(...args); }); } @@ -144,11 +142,11 @@ export class WorkPackageRelationsHierarchyService { return childWorkPackage.changeParent({ _links: { parent: { - href: null - } + href: null, + }, }, - lockVersion: childWorkPackage.lockVersion - }).then(wp => { + lockVersion: childWorkPackage.lockVersion, + }).then((wp) => { if (parentWorkPackage) { this .apiV3Service @@ -159,7 +157,7 @@ export class WorkPackageRelationsHierarchyService { this.halEvents.push(wp, { eventType: 'association', relatedWorkPackage: null, - relationType: 'child' + relationType: 'child', }); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts index 39f2804662..312ef0f6eb 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,43 +26,49 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { Observable, zip } from 'rxjs'; import { take, takeUntil } from 'rxjs/operators'; -import { RelatedWorkPackagesGroup } from './wp-relations.interfaces'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; import { RelationsStateValue, WorkPackageRelationsService } from './wp-relations.service'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; - +import { RelatedWorkPackagesGroup } from './wp-relations.interfaces'; @Component({ selector: 'wp-relations', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './wp-relations.template.html' + templateUrl: './wp-relations.template.html', }) export class WorkPackageRelationsComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; + public relationGroups:RelatedWorkPackagesGroup = {}; + public relationGroupKeys:string[] = []; + public relationsPresent = false; + public canAddRelation:boolean; // By default, group by relation type public groupByWorkPackageType = false; public text = { - relations_header: this.I18n.t('js.work_packages.tabs.relations') + relations_header: this.I18n.t('js.work_packages.tabs.relations'), }; + public currentRelations:WorkPackageResource[] = []; constructor(private I18n:I18nService, - private wpRelations:WorkPackageRelationsService, - private cdRef:ChangeDetectorRef, - private apiV3Service:APIV3Service) { + private wpRelations:WorkPackageRelationsService, + private cdRef:ChangeDetectorRef, + private apiV3Service:APIV3Service) { super(); } @@ -73,7 +79,7 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement .state(this.workPackage.id!) .values$() .pipe( - takeUntil(componentDestroyed(this)) + takeUntil(componentDestroyed(this)), ) .subscribe((relations:RelationsStateValue) => { this.loadedRelations(relations); @@ -88,7 +94,7 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement .id(this.workPackage) .requireAndStream() .pipe( - takeUntil(componentDestroyed(this)) + takeUntil(componentDestroyed(this)), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; @@ -96,13 +102,11 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement } private getRelatedWorkPackages(workPackageIds:string[]):Observable { - const observablesToGetZipped:Observable[] = workPackageIds.map(wpId => - this - .apiV3Service - .work_packages - .id(wpId) - .get() - ); + const observablesToGetZipped:Observable[] = workPackageIds.map((wpId) => this + .apiV3Service + .work_packages + .id(wpId) + .get()); return zip(...observablesToGetZipped); } @@ -126,10 +130,9 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement (wp:WorkPackageResource) => { if (this.groupByWorkPackageType) { return wp.type.name; - } else { - var normalizedType = (wp.relatedBy as RelationResource).normalizedType(this.workPackage); - return this.I18n.t('js.relation_labels.' + normalizedType); } + const normalizedType = (wp.relatedBy as RelationResource).normalizedType(this.workPackage); + return this.I18n.t(`js.relation_labels.${normalizedType}`); }); this.relationGroupKeys = _.keys(this.relationGroups); this.relationsPresent = _.size(this.relationGroups) > 0; @@ -137,8 +140,8 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement } protected loadedRelations(stateValues:RelationsStateValue):void { - var relatedWpIds:string[] = []; - var relations:{ [wpId:string]:any } = []; + const relatedWpIds:string[] = []; + const relations:{ [wpId:string]:any } = []; if (_.size(stateValues) === 0) { this.currentRelations = []; @@ -153,7 +156,7 @@ export class WorkPackageRelationsComponent extends UntilDestroyedMixin implement this.getRelatedWorkPackages(relatedWpIds) .pipe( - take(1) + take(1), ) .subscribe((relatedWorkPackages:WorkPackageResource[]) => { this.currentRelations = relatedWorkPackages.map((wp:WorkPackageResource) => { diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.interfaces.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.interfaces.ts index 97c59739f8..35f3af7c43 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.interfaces.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.interfaces.ts @@ -1,4 +1,3 @@ export interface RelatedWorkPackagesGroup { [key:string]:any; } - diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts index 9f2ed2b2e1..90d6cb076c 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations.service.ts @@ -1,13 +1,13 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { multiInput, MultiInputState, StatesGroup } from 'reactivestates'; -import { Injectable } from "@angular/core"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; -import { Observable } from "rxjs"; -import { map, take, tap } from "rxjs/operators"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; +import { Injectable } from '@angular/core'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; +import { Observable } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; export type RelationsStateValue = { [relationId:string]:RelationResource }; @@ -24,10 +24,9 @@ export class RelationStateGroup extends StatesGroup { @Injectable() export class WorkPackageRelationsService extends StateCacheService { - constructor(private PathHelper:PathHelperService, - private apiV3Service:APIV3Service, - private halResource:HalResourceService) { + private apiV3Service:APIV3Service, + private halResource:HalResourceService) { super(new RelationStateGroup().relations); } @@ -44,7 +43,7 @@ export class WorkPackageRelationsService extends StateCacheService this.relationsStateValue(id, collection.elements)) + map((collection) => this.relationsStateValue(id, collection.elements)), ); } @@ -117,8 +116,8 @@ export class WorkPackageRelationsService extends StateCacheService { + _.values(relation.ids).forEach((wpId) => { this.multiState.get(wpId).doModify((value:RelationsStateValue) => { value[relation.id!] = relation; return value; @@ -197,13 +196,11 @@ export class WorkPackageRelationsService extends StateCacheService { + _.values(relation.ids).forEach((wpId) => { this.multiState.get(wpId).doModify((value:RelationsStateValue) => { delete value[relation.id!]; return value; - }, () => { - return {}; - }); + }, () => ({})); }); } @@ -215,7 +212,7 @@ export class WorkPackageRelationsService extends StateCacheService r.id!); + return _.keyBy(relations, (r) => r.id!); } /** @@ -226,12 +223,11 @@ export class WorkPackageRelationsService extends StateCacheService { - const relevant = relations.filter(r => r.isInvolved(wpId)); + involved.forEach((wpId) => { + const relevant = relations.filter((r) => r.isInvolved(wpId)); const value = this.relationsStateValue(wpId, relevant); this.updateValue(wpId, value); }); - } } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts index 317fab1d02..0159640876 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,18 +27,19 @@ //++ import { ChangeDetectorRef, Directive, OnInit } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { ActivityEntryInfo } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info'; import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Transition } from "@uirouter/core"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Transition } from '@uirouter/core'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Directive() export class ActivityPanelBaseController extends UntilDestroyedMixin implements OnInit { public workPackage:WorkPackageResource; + public workPackageId:string; // All activities retrieved for the work package @@ -48,20 +49,23 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements public visibleActivities:ActivityEntryInfo[] = []; public reverse:boolean; + public showToggler:boolean; public onlyComments = false; + public togglerText:string; + public text = { commentsOnly: this.I18n.t('js.label_activity_show_only_comments'), - showAll: this.I18n.t('js.label_activity_show_all') + showAll: this.I18n.t('js.label_activity_show_all'), }; constructor(readonly apiV3Service:APIV3Service, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly $transition:Transition, - readonly wpActivity:WorkPackagesActivityService) { + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef, + readonly $transition:Transition, + readonly wpActivity:WorkPackagesActivityService) { super(); this.reverse = wpActivity.isReversed; @@ -75,7 +79,7 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements .id(this.workPackageId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; @@ -98,17 +102,16 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements const count_all = this.unfilteredActivities.length; const count_with_comments = this.getActivitiesWithComments().length; - return count_all > 1 && - count_with_comments > 0 && - count_with_comments < this.unfilteredActivities.length; + return count_all > 1 + && count_with_comments > 0 + && count_with_comments < this.unfilteredActivities.length; } protected getVisibleActivities() { if (!this.onlyComments) { return this.unfilteredActivities; - } else { - return this.getActivitiesWithComments(); } + return this.getActivitiesWithComments(); } protected getActivitiesWithComments() { @@ -131,4 +134,3 @@ export class ActivityPanelBaseController extends UntilDestroyedMixin implements return this.wpActivity.info(this.unfilteredActivities, activity, index); } } - diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info.ts index 64229f3c9c..0d531cd9a6 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,16 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; export class ActivityEntryInfo { - constructor(public timezoneService:TimezoneService, - public isReversed:boolean, - public activities:any[], - public activity:any, - public index:number) { + public isReversed:boolean, + public activities:any[], + public activity:any, + public index:number) { } public number(forceReverse = false) { @@ -69,7 +67,7 @@ export class ActivityEntryInfo { } public isInitial(forceReverse = false) { - var activityNo = this.number(forceReverse); + let activityNo = this.number(forceReverse); if (this.activity._type.indexOf('Activity') !== 0) { return false; @@ -80,8 +78,8 @@ export class ActivityEntryInfo { } while (--activityNo > 0) { - var idx = this.orderedIndex(activityNo, forceReverse) - 1; - var activity = this.activities[idx]; + const idx = this.orderedIndex(activityNo, forceReverse) - 1; + const activity = this.activities[idx]; if (!_.isNil(activity) && activity._type.indexOf('Activity') === 0) { return false; } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.component.ts index 78d8ed456a..85590ad1fd 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-on-overview.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,22 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { ActivityPanelBaseController } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller'; import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { ActivityEntryInfo } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-entry-info'; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; @Component({ selector: 'newest-activity-on-overview', templateUrl: './activity-on-overview.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NewestActivityOnOverviewComponent extends ActivityPanelBaseController { @Input('workPackage') public workPackage:WorkPackageResource; public latestActivityInfo:ActivityEntryInfo[] = []; + public trackByHref = AngularTrackingHelpers.trackByProperty('identifier'); ngOnInit() { @@ -59,17 +60,15 @@ export class NewestActivityOnOverviewComponent extends ActivityPanelBaseControll } private latestActivities(visible = 3) { - if (this.reverse) { // In reverse, we already get reversed entries from API. // So simply take the first three const segment = this.unfilteredActivities.slice(0, visible); return segment.map((el:HalResource, i:number) => this.info(el, i)); - } else { - // In ascending sort, take the last three items - const segment = this.unfilteredActivities.slice(-visible); - const startIndex = this.unfilteredActivities.length - segment.length; - return segment.map((el:HalResource, i:number) => this.info(el, startIndex + i)); } + // In ascending sort, take the last three items + const segment = this.unfilteredActivities.slice(-visible); + const startIndex = this.unfilteredActivities.length - segment.length; + return segment.map((el:HalResource, i:number) => this.info(el, startIndex + i)); } } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component.ts index fe967d680e..1307a4b9a7 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,9 +27,9 @@ //++ import { Component } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { ActivityPanelBaseController } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { ActivityPanelBaseController } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-base.controller'; @Component({ templateUrl: './activity-tab.html', @@ -37,7 +37,9 @@ import { ActivityPanelBaseController } from "core-app/features/work-packages/com }) export class WorkPackageActivityTabComponent extends ActivityPanelBaseController { public workPackage:WorkPackageResource; + public tabName = this.I18n.t('js.work_packages.tabs.activity'); + public trackByHref = AngularTrackingHelpers.trackByHrefAndProperty('version'); ngOnInit() { diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts index e241f1483f..54d9330748 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ActivityEntryInfo } from './activity-entry-info'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { Injectable } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { ActivityEntryInfo } from './activity-entry-info'; @Injectable() export class WorkPackagesActivityService extends WorkPackageLinkedResourceCache { - constructor(public ConfigurationService:ConfigurationService, - readonly timezoneService:TimezoneService) { + readonly timezoneService:TimezoneService) { super(); } @@ -56,9 +55,10 @@ export class WorkPackagesActivityService extends WorkPackageLinkedResourceCache< * whose order depends on the 'commentsSortedInDescendingOrder' property. */ protected load(workPackage:WorkPackageResource):Promise { - var aggregated:any[] = [], promises:Promise[] = []; + const aggregated:any[] = []; const + promises:Promise[] = []; - var add = function (data:any) { + const add = function (data:any) { aggregated.push(data.elements); }; @@ -67,9 +67,7 @@ export class WorkPackagesActivityService extends WorkPackageLinkedResourceCache< if (workPackage.revisions) { promises.push(workPackage.revisions.$update().then(add)); } - return Promise.all(promises).then(() => { - return this.sortedActivityList(aggregated); - }); + return Promise.all(promises).then(() => this.sortedActivityList(aggregated)); } protected sortedActivityList(activities:HalResource[], attr = 'createdAt'):HalResource[] { @@ -77,9 +75,8 @@ export class WorkPackagesActivityService extends WorkPackageLinkedResourceCache< if (this.isReversed) { return sorted.reverse(); - } else { - return sorted; } + return sorted; } public info(activities:HalResource[], activity:HalResource, index:number) { diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts index e7b0e3f630..a8730e07fd 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -42,22 +42,22 @@ describe('keepTab service', () => { current: { name: 'whatever', }, - includes: includes + includes, }; $transitions = { - onSuccess: (criteria:any, cb:(transition:any) => void) => callback = cb + onSuccess: (criteria:any, cb:(transition:any) => void) => callback = cb, }; uiRouterGlobals = { - params: { tabIdentifier: 'activity' } + params: { tabIdentifier: 'activity' }, }; keepTab = new KeepTabService($state, uiRouterGlobals, $transitions); defaults = { showTab: 'work-packages.show.tabs', - detailsTab: 'work-packages.partitioned.list.details.tabs' + detailsTab: 'work-packages.partitioned.list.details.tabs', }; }); @@ -72,12 +72,10 @@ describe('keepTab service', () => { }); describe('when opening a show route', () => { - var currentPathPrefix = 'work-packages.show.*'; + let currentPathPrefix = 'work-packages.show.*'; beforeEach(() => { - spyOn($state, 'includes').and.callFake((path:string) => { - return path === currentPathPrefix; - }); + spyOn($state, 'includes').and.callFake((path:string) => path === currentPathPrefix); $state.current.name = 'work-packages.show.tabs'; uiRouterGlobals.params.tabIdentifier = 'relations'; @@ -93,12 +91,12 @@ describe('keepTab service', () => { }); it('should propagate the previous change', () => { - var cb = jasmine.createSpy(); + const cb = jasmine.createSpy(); - var expected = { + const expected = { active: 'relations', show: 'relations', - details: 'relations' + details: 'relations', }; keepTab.observable.subscribe(cb); @@ -118,9 +116,7 @@ describe('keepTab service', () => { describe('when opening show#activity', () => { beforeEach(() => { - spyOn($state, 'includes').and.callFake((path:string) => { - return path === 'work-packages.show.*'; - }); + spyOn($state, 'includes').and.callFake((path:string) => path === 'work-packages.show.*'); uiRouterGlobals.params.tabIdentifier = 'activity'; $state.current.name = 'work-packages.show.tabs'; @@ -134,9 +130,7 @@ describe('keepTab service', () => { describe('when opening a details route', () => { beforeEach(() => { - spyOn($state, 'includes').and.callFake((path:string) => { - return path === '**.details.*'; - }); + spyOn($state, 'includes').and.callFake((path:string) => path === '**.details.*'); uiRouterGlobals.params.tabIdentifier = 'activity'; $state.current.name = 'work-packages.partitioned.list.details.tabs'; @@ -152,12 +146,12 @@ describe('keepTab service', () => { }); it('should propagate the previous and next change', () => { - var cb = jasmine.createSpy(); + const cb = jasmine.createSpy(); - var expected = { + const expected = { active: 'activity', details: 'activity', - show: 'activity' + show: 'activity', }; keepTab.observable.subscribe(cb); @@ -167,6 +161,5 @@ describe('keepTab service', () => { expect(cb.calls.count()).toEqual(2); }); - }); }); diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts index 22fa07e768..d58c431ef7 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { StateService, Transition, TransitionService, UIRouterGlobals } from '@uirouter/core'; +import { + StateService, Transition, TransitionService, UIRouterGlobals, +} from '@uirouter/core'; import { ReplaySubject } from 'rxjs'; -import { Injectable } from "@angular/core"; -import { splitViewRoute } from "core-app/features/work-packages/routing/split-view-routes.helper"; +import { Injectable } from '@angular/core'; +import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper'; @Injectable({ providedIn: 'root' }) export class KeepTabService { @@ -38,9 +40,8 @@ export class KeepTabService { protected subject = new ReplaySubject<{ [tab:string]:string; }>(1); constructor(protected $state:StateService, - protected uiRouterGlobals:UIRouterGlobals, - protected $transitions:TransitionService) { - + protected uiRouterGlobals:UIRouterGlobals, + protected $transitions:TransitionService) { this.updateTabs(); $transitions.onSuccess({}, (transition:Transition) => { this.updateTabs(transition.params('to').tabIdentifier); @@ -77,7 +78,7 @@ export class KeepTabService { const route = splitViewRoute(this.$state); this.$state.go( - route + '.tabs', + `${route}.tabs`, { ...this.uiRouterGlobals.params, ...params, diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts index 5540b4f15c..bc02520df3 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,10 +28,10 @@ import { Component } from '@angular/core'; import { StateService } from '@uirouter/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './overview-tab.html', @@ -39,12 +39,14 @@ import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; }) export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin { public workPackageId:string; + public workPackage:WorkPackageResource; + public tabName = this.I18n.t('js.label_latest_activity'); public constructor(readonly I18n:I18nService, - readonly $state:StateService, - readonly apiV3Service:APIV3Service) { + readonly $state:StateService, + readonly apiV3Service:APIV3Service) { super(); this.workPackageId = this.$state.params.workPackageId; @@ -55,7 +57,7 @@ export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin { .id(this.workPackageId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp) => this.workPackage = wp); } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts index 0cb8f18cbc..8011b1a94e 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,9 +29,9 @@ import { Transition } from '@uirouter/core'; import { Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './relations-tab.html', @@ -39,11 +39,12 @@ import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; }) export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackageId?:string; + public workPackage:WorkPackageResource; public constructor(readonly I18n:I18nService, - readonly $transition:Transition, - readonly apiV3Service:APIV3Service) { + readonly $transition:Transition, + readonly apiV3Service:APIV3Service) { super(); } @@ -55,12 +56,11 @@ export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implem .id(wpId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp) => { this.workPackageId = wp.id!; this.workPackage = wp; }); } - } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts index 3de8c8a468..4b2706c706 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, +} from '@angular/core'; import { Transition } from '@uirouter/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ templateUrl: './watchers-tab.html', @@ -46,35 +48,44 @@ import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; }) export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin implements OnInit { public workPackageId:string; + public workPackage:WorkPackageResource; + public trackByHref = AngularTrackingHelpers.trackByHref; public error = false; + public noResults = false; + public allowedToView = false; + public allowedToAdd = false; + public allowedToRemove = false; + public availableWatchersPath:string; + private $element:JQuery; public watching:any[] = []; + public text = { loading: this.I18n.t('js.watchers.label_loading'), loadingError: this.I18n.t('js.watchers.label_error_loading'), autocomplete: { - placeholder: this.I18n.t('js.watchers.typeahead_placeholder') - } + placeholder: this.I18n.t('js.watchers.typeahead_placeholder'), + }, }; public constructor(readonly I18n:I18nService, - readonly elementRef:ElementRef, - readonly wpWatchersService:WorkPackageWatchersService, - readonly $transition:Transition, - readonly notificationService:WorkPackageNotificationService, - readonly loadingIndicator:LoadingIndicatorService, - readonly cdRef:ChangeDetectorRef, - readonly pathHelper:PathHelperService, - readonly apiV3Service:APIV3Service) { + readonly elementRef:ElementRef, + readonly wpWatchersService:WorkPackageWatchersService, + readonly $transition:Transition, + readonly notificationService:WorkPackageNotificationService, + readonly loadingIndicator:LoadingIndicatorService, + readonly cdRef:ChangeDetectorRef, + readonly pathHelper:PathHelperService, + readonly apiV3Service:APIV3Service) { super(); } @@ -88,7 +99,7 @@ export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin impleme .id(this.workPackageId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; @@ -123,7 +134,6 @@ export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin impleme this.loadingIndicator.wpDetails.promise = promise; } - public addWatcher(user:any) { this.loadingPromise = this.workPackage.addWatcher.$link.$fetch({ user: { href: user.href } }) .then(() => { @@ -144,9 +154,7 @@ export class WorkPackageWatchersTabComponent extends UntilDestroyedMixin impleme public removeWatcher(watcher:any) { this.workPackage.removeWatcher.$link.$prepare({ user_id: watcher.id })() .then(() => { - _.remove(this.watching, (other:HalResource) => { - return other.href === watcher.href; - }); + _.remove(this.watching, (other:HalResource) => other.href === watcher.href); // Forcefully reload the resource to update the watch/unwatch links // should the current user have been removed diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts index 63a2b3893f..b4fefe2d81 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,8 +28,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; import { WorkPackageWatchersTabComponent } from './watchers-tab.component'; -import { UserResource } from "core-app/features/hal/resources/user-resource"; @Component({ templateUrl: './wp-watcher-entry.html', @@ -37,16 +37,18 @@ import { UserResource } from "core-app/features/hal/resources/user-resource"; }) export class WorkPackageWatcherEntryComponent implements OnInit { @Input('watcher') public watcher:UserResource; + public deleting = false; + public text:{ remove:string }; constructor(readonly I18n:I18nService, - readonly panelCtrl:WorkPackageWatchersTabComponent) { + readonly panelCtrl:WorkPackageWatchersTabComponent) { } ngOnInit() { this.text = { - remove: this.I18n.t('js.label_remove_watcher', { name: this.watcher.name }) + remove: this.I18n.t('js.label_remove_watcher', { name: this.watcher.name }), }; } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service.ts index 2be08b6984..73247cb8a9 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,18 +27,15 @@ //++ import { Injectable } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; import { WorkPackageLinkedResourceCache } from 'core-app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service'; @Injectable() export class WorkPackageWatchersService extends WorkPackageLinkedResourceCache { - protected load(workPackage:WorkPackageResource) { return workPackage.watchers.$update() - .then((collection:CollectionResource) => { - return collection.elements; - }); + .then((collection:CollectionResource) => collection.elements); } } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service.ts index f02d1c9408..d24fa7bcde 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/wp-linked-resource-cache.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,23 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { input, InputState } from 'reactivestates'; import { take } from 'rxjs/operators'; -import { Observable, of } from "rxjs"; -import { EntityUIStore } from "@datorama/akita"; +import { Observable, of } from 'rxjs'; +import { EntityUIStore } from '@datorama/akita'; -EntityUIStore +EntityUIStore; export abstract class WorkPackageLinkedResourceCache { - protected cacheDurationInSeconds = 120; // Cache activities for the last work package // to allow fast switching between work packages without refreshing. protected cache:{ id:string|null, state:InputState } = { id: null, - state: input() + state: input(), }; /** @@ -56,7 +55,7 @@ export abstract class WorkPackageLinkedResourceCache { */ public requireAndStream(workPackage:WorkPackageResource, force = false):Observable { const id = workPackage.id!; - const state = this.cache.state; + const { state } = this.cache; // Clear cache if requesting different resource if (force || this.cache.id !== id) { @@ -79,7 +78,7 @@ export abstract class WorkPackageLinkedResourceCache { return this .requireAndStream(workPackage, force) .pipe( - take(1) + take(1), ) .toPromise(); } @@ -96,7 +95,7 @@ export abstract class WorkPackageLinkedResourceCache { * @returns {boolean} */ public isCached(workPackageId:string) { - const state = this.cache.state; + const { state } = this.cache; return this.cache.id === workPackageId && state.hasValue() && !state.isValueOlderThan(this.cacheDurationInSeconds * 1000); } @@ -105,4 +104,4 @@ export abstract class WorkPackageLinkedResourceCache { * @param {WorkPackageResource} workPackage */ protected abstract load(workPackage:WorkPackageResource):Promise; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts index efde1a329a..2c41328b71 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view/wp-single-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -33,28 +33,27 @@ import { ElementRef, Injector, Input, - OnInit + OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { distinctUntilChanged, map } from 'rxjs/operators'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { HookService } from 'core-app/features/plugins/hook-service'; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { Subject } from "rxjs"; -import { randomString } from "core-app/shared/helpers/random-string"; -import { BrowserDetector } from "core-app/core/browser/browser-detector.service"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { ISchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { States } from "core-app/core/states/states.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { debugLog } from "core-app/shared/helpers/debug_output"; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { Subject } from 'rxjs'; +import { randomString } from 'core-app/shared/helpers/random-string'; +import { BrowserDetector } from 'core-app/core/browser/browser-detector.service'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { States } from 'core-app/core/states/states.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; export interface FieldDescriptor { name:string; @@ -86,7 +85,7 @@ export const overflowingContainerAttribute = 'overflowingIdentifier'; @Component({ templateUrl: './wp-single-view.html', selector: 'wp-single-view', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; @@ -108,9 +107,10 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen href:string|null, field?:FieldDescriptor[] }; + public text = { attachments: { - label: this.I18n.t('js.label_attachments') + label: this.I18n.t('js.label_attachments'), }, project: { required: this.I18n.t('js.project.required_outside_context'), @@ -123,7 +123,7 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen }, infoRow: { createdBy: this.I18n.t('js.label_created_by'), - lastUpdatedOn: this.I18n.t('js.label_last_updated_on') + lastUpdatedOn: this.I18n.t('js.label_last_updated_on'), }, }; @@ -132,18 +132,18 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen $element:JQuery; constructor(readonly I18n:I18nService, - protected currentProject:CurrentProjectService, - protected PathHelper:PathHelperService, - protected states:States, - protected halEditing:HalResourceEditingService, - protected halResourceService:HalResourceService, - protected displayFieldService:DisplayFieldService, - protected schemaCache:SchemaCacheService, - protected hook:HookService, - protected injector:Injector, - protected cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly browserDetector:BrowserDetector) { + protected currentProject:CurrentProjectService, + protected PathHelper:PathHelperService, + protected states:States, + protected halEditing:HalResourceEditingService, + protected halResourceService:HalResourceService, + protected displayFieldService:DisplayFieldService, + protected schemaCache:SchemaCacheService, + protected hook:HookService, + protected injector:Injector, + protected cdRef:ChangeDetectorRef, + readonly elementRef:ElementRef, + readonly browserDetector:BrowserDetector) { super(); } @@ -160,7 +160,7 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen .pipe( this.untilDestroyed(), distinctUntilChanged((a, b) => _.isEqual(a, b)), - map(() => this.halEditing.changeFor(this.workPackage)) + map(() => this.halEditing.changeFor(this.workPackage)), ) .subscribe((change:WorkPackageChangeset) => this.refresh(change)); @@ -170,16 +170,16 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen .temporaryEditResource(this.workPackage) .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(resource => { + .subscribe((resource) => { this.resourceContextChange.next(this.contextFrom(resource)); }); } private refresh(change:WorkPackageChangeset) { // Prepare the fields that are required always - const isNew = this.workPackage.isNew; + const { isNew } = this.workPackage; const resource = change.projectedResource; if (!resource.project) { @@ -187,7 +187,7 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen } else { this.projectContext = { href: this.PathHelper.projectWorkPackagePath(resource.project.idFromLink, this.workPackage.id!), - matches: resource.project.href === this.currentProject.apiv3Path + matches: resource.project.href === this.currentProject.apiv3Path, }; } @@ -284,19 +284,18 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen id: groupId || randomString(16), members: this.getFields(change, group.attributes), type: group._type, - isolated: false - }; - } else { - return { - name: group.name, - id: groupId || randomString(16), - query: this.halResourceService.createHalResourceOfClass(QueryResource, group._embedded.query), - relationType: group.relationType, - members: [group._embedded.query], - type: group._type, - isolated: true + isolated: false, }; } + return { + name: group.name, + id: groupId || randomString(16), + query: this.halResourceService.createHalResourceOfClass(QueryResource, group._embedded.query), + relationType: group.relationType, + members: [group._embedded.query], + type: group._type, + isolated: true, + }; }); } @@ -324,7 +323,7 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen label: field.label, multiple: false, spanAll: field.isFormattable, - field: field + field, }); }); @@ -339,7 +338,7 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen private getDateField(change:WorkPackageChangeset):FieldDescriptor { const object:any = { label: this.I18n.t('js.work_packages.properties.date'), - multiple: false + multiple: false, }; if (change.schema.ofProperty('date')) { @@ -373,11 +372,10 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen schemaHref = schema.href; } - return { isNew: workPackage.isNew, schema: schemaHref, - project: projectHref + project: projectHref, }; } @@ -386,27 +384,25 @@ export class WorkPackageSingleViewComponent extends UntilDestroyedMixin implemen change.projectedResource, name, change.schema.ofProperty(name), - { container: 'single-view', injector: this.injector, options: {} } - ) as DisplayField; + { container: 'single-view', injector: this.injector, options: {} }, + ); } private getAttributesGroupId(group:any):string { const overflowingIdentifier = this.$element - .find("[data-group-name=\'" + group.name + "\']") + .find(`[data-group-name=\'${group.name}\']`) .data(overflowingContainerAttribute); if (overflowingIdentifier) { return overflowingIdentifier.replace('.__overflowing_', ''); - } else { - return ''; } + return ''; } private schema(resource:WorkPackageResource) { if (this.halEditing.typedState(resource).hasValue()) { return this.halEditing.typedState(this.workPackage).value!.schema; - } else { - return this.schemaCache.of(resource) as ISchemaProxy; } + return this.schemaCache.of(resource); } } diff --git a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts index 1c4c5d0f5f..70cf7b5b54 100644 --- a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,14 +28,14 @@ import { Component, Input, OnInit } from '@angular/core'; import { UIRouterGlobals } from '@uirouter/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { randomString } from "core-app/shared/helpers/random-string"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { randomString } from 'core-app/shared/helpers/random-string'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ selector: 'wp-subject', - templateUrl: './wp-subject.html' + templateUrl: './wp-subject.html', }) export class WorkPackageSubjectComponent extends UntilDestroyedMixin implements OnInit { @Input('workPackage') workPackage:WorkPackageResource; @@ -43,7 +43,7 @@ export class WorkPackageSubjectComponent extends UntilDestroyedMixin implements public readonly uniqueElementIdentifier = `work-packages--subject-type-row-${randomString(16)}`; constructor(protected uiRouterGlobals:UIRouterGlobals, - protected apiV3Service:APIV3Service) { + protected apiV3Service:APIV3Service) { super(); } @@ -52,10 +52,10 @@ export class WorkPackageSubjectComponent extends UntilDestroyedMixin implements this .apiV3Service .work_packages - .id(this.uiRouterGlobals.params['workPackageId']) + .id(this.uiRouterGlobals.params.workPackageId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; diff --git a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts index f76b6ccb51..a53930376c 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts @@ -1,25 +1,25 @@ -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Component, OnInit, Injector } from '@angular/core'; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; -import { WpTableConfigurationModalComponent } from "core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal"; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; @Component({ templateUrl: './config-menu.template.html', selector: 'wp-table-config-menu', }) -export class WorkPackagesTableConfigMenu implements OnInit { +export class WorkPackagesTableConfigMenuComponent implements OnInit { public text:any; constructor(readonly I18n:I18nService, - readonly injector:Injector, - readonly opModalService:OpModalService, - readonly opContextMenu:OPContextMenuService) { + readonly injector:Injector, + readonly opModalService:OpModalService, + readonly opContextMenu:OPContextMenuService) { } ngOnInit():void { this.text = { - configureTable: I18n.t('js.toolbar.settings.configure_view') + configureTable: I18n.t('js.toolbar.settings.configure_view'), }; } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet.ts index 023c24a59e..3a2199463c 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet.ts @@ -8,9 +8,9 @@ import { ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, - Injector + Injector, } from '@angular/core'; -import { TabDefinition } from "core-app/shared/components/tabs/tab.interface"; +import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; export interface TabInterface extends TabDefinition { componentClass:{ new(...args:any[]):TabComponent }; @@ -27,7 +27,6 @@ export interface ActiveTabInterface extends TabDefinition { } export class TabPortalOutlet { - // Active tabs that have been instantiated public activeTabs:{ [name:string]:ActiveTabInterface } = {}; @@ -39,7 +38,8 @@ export class TabPortalOutlet { public outletElement:HTMLElement, private componentFactoryResolver:ComponentFactoryResolver, private appRef:ApplicationRef, - private injector:Injector) { + private injector:Injector, + ) { } public get activeComponents():TabComponent[] { @@ -64,8 +64,6 @@ export class TabPortalOutlet { this.outletElement.appendChild(this._getComponentRootNode(instance.componentRef)); this.outletElement.dataset.tabName = tab.name; this.currentTab = instance; - - return; } public detach():void { @@ -81,7 +79,7 @@ export class TabPortalOutlet { */ dispose():void { // Dispose all active tabs - _.each(this.activeTabs, active => active.dispose()); + _.each(this.activeTabs, (active) => active.dispose()); // Remove outlet element if (this.outletElement.parentNode != null) { @@ -107,12 +105,12 @@ export class TabPortalOutlet { return { ...tab, - portal: portal, - componentRef: componentRef, + portal, + componentRef, dispose: () => { this.appRef.detachView(componentRef.hostView); componentRef.destroy(); - } + }, }; } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts index a50e98dc4b..bb085f872b 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts @@ -4,21 +4,25 @@ import { QueryColumn } from 'core-app/features/work-packages/components/wp-query import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { DraggableOption } from "core-app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component"; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { DraggableOption } from 'core-app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component'; @Component({ - templateUrl: './columns-tab.component.html' + templateUrl: './columns-tab.component.html', }) -export class WpTableConfigurationColumnsTab implements TabComponent, OnInit { - public availableColumnsOptions = this.wpTableColumns.all.map(c => this.column2Like(c)); +export class WpTableConfigurationColumnsTabComponent implements TabComponent, OnInit { + public availableColumnsOptions = this.wpTableColumns.all.map((c) => this.column2Like(c)); public availableColumns = this.wpTableColumns.all; - public availableColumnsMap:{ [id:string]:QueryColumn } = _.keyBy(this.availableColumns, c => c.id); - public selectedColumns:DraggableOption[] = this.wpTableColumns.getColumns().map(c => this.column2Like(c)); + + public availableColumnsMap:{ [id:string]:QueryColumn } = _.keyBy(this.availableColumns, (c) => c.id); + + public selectedColumns:DraggableOption[] = this.wpTableColumns.getColumns().map((c) => this.column2Like(c)); public selectedColumnMap:{ [id:string]:boolean } = {}; + public eeShowBanners = false; + public text = { columnsHelp: this.I18n.t('js.work_packages.table_configuration.columns_help_text'), @@ -27,18 +31,18 @@ export class WpTableConfigurationColumnsTab implements TabComponent, OnInit { multiSelectLabel: this.I18n.t('js.work_packages.label_column_multiselect'), upsaleRelationColumns: this.I18n.t('js.work_packages.table_configuration.upsale.relation_columns'), - upsaleCheckOutLink: this.I18n.t('js.work_packages.table_configuration.upsale.check_out_link') + upsaleCheckOutLink: this.I18n.t('js.work_packages.table_configuration.upsale.check_out_link'), }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly ConfigurationService:ConfigurationService, - readonly bannerService:BannersService) { + readonly I18n:I18nService, + readonly wpTableColumns:WorkPackageViewColumnsService, + readonly ConfigurationService:ConfigurationService, + readonly bannerService:BannersService) { } public onSave() { - this.wpTableColumns.setColumnsById(this.selectedColumns.map(c => c.id)); + this.wpTableColumns.setColumnsById(this.selectedColumns.map((c) => c.id)); } ngOnInit() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts index 96dd2c74e8..e1863b327a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts @@ -1,22 +1,21 @@ - import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; -import { Component, Injector } from "@angular/core"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +import { Component, Injector } from '@angular/core'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; @Component({ - templateUrl: './display-settings-tab.component.html' + templateUrl: './display-settings-tab.component.html', }) -export class WpTableConfigurationDisplaySettingsTab implements TabComponent { - +export class WpTableConfigurationDisplaySettingsTabComponent implements TabComponent { // Display mode public displayMode:'hierarchy'|'grouped'|'default' = 'default'; // Grouping public currentGroup:QueryGroupByResource|null; + public availableGroups:QueryGroupByResource[] = []; // Sums row display @@ -28,22 +27,22 @@ export class WpTableConfigurationDisplaySettingsTab implements TabComponent { title: this.I18n.t('js.label_group_by'), placeholder: this.I18n.t('js.placeholders.default'), please_select: this.I18n.t('js.placeholders.selection'), - default: '— ' + this.I18n.t('js.work_packages.table_configuration.default'), + default: `— ${this.I18n.t('js.work_packages.table_configuration.default')}`, display_sums: this.I18n.t('js.work_packages.query.display_sums'), - display_sums_hint: '— ' + this.I18n.t('js.work_packages.table_configuration.display_sums_hint'), + display_sums_hint: `— ${this.I18n.t('js.work_packages.table_configuration.display_sums_hint')}`, display_mode: { default: this.I18n.t('js.work_packages.table_configuration.default_mode'), grouped: this.I18n.t('js.work_packages.table_configuration.grouped_mode'), hierarchy: this.I18n.t('js.work_packages.table_configuration.hierarchy_mode'), - hierarchy_hint: '— ' + this.I18n.t('js.work_packages.table_configuration.hierarchy_hint') - } + hierarchy_hint: `— ${this.I18n.t('js.work_packages.table_configuration.hierarchy_hint')}`, + }, }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpTableHierarchies:WorkPackageViewHierarchiesService, - readonly wpTableSums:WorkPackageViewSumService) { + readonly I18n:I18nService, + readonly wpTableGroupBy:WorkPackageViewGroupByService, + readonly wpTableHierarchies:WorkPackageViewHierarchiesService, + readonly wpTableSums:WorkPackageViewSumService) { } public onSave() { @@ -60,7 +59,7 @@ export class WpTableConfigurationDisplaySettingsTab implements TabComponent { public updateGroup(href:string) { this.displayMode = 'grouped'; - this.currentGroup = _.find(this.availableGroups, group => group.href === href) || null; + this.currentGroup = _.find(this.availableGroups, (group) => group.href === href) || null; } ngOnInit() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts index b5ef7a249c..322d21c5ab 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component.ts @@ -1,18 +1,18 @@ import { Component, Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { BannersService } from "core-app/core/enterprise/banners.service"; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; @Component({ templateUrl: './filters-tab.component.html', - selector: 'wp-table-config-filters-tab' + selector: 'wp-table-config-filters-tab', }) export class WpTableConfigurationFiltersTab implements TabComponent { - public filters:QueryFilterInstanceResource[] = []; + public eeShowBanners = false; public text = { @@ -21,14 +21,14 @@ export class WpTableConfigurationFiltersTab implements TabComponent { multiSelectLabel: this.I18n.t('js.work_packages.label_column_multiselect'), upsaleRelationColumns: this.I18n.t('js.modals.upsale_relation_columns'), - upsaleRelationColumnsLink: this.I18n.t('js.modals.upsale_relation_columns_link') + upsaleRelationColumnsLink: this.I18n.t('js.modals.upsale_relation_columns_link'), }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpFiltersService:WorkPackageFiltersService, - readonly bannerService:BannersService) { + readonly I18n:I18nService, + readonly wpTableFilters:WorkPackageViewFiltersService, + readonly wpFiltersService:WorkPackageFiltersService, + readonly bannerService:BannersService) { } ngOnInit() { @@ -37,7 +37,7 @@ export class WpTableConfigurationFiltersTab implements TabComponent { .onReady() .then(() => this.filters = this.wpTableFilters.current); - this.wpTableFilters.changes$().subscribe(filters => { + this.wpTableFilters.changes$().subscribe((filters) => { this.filters = this.wpTableFilters.current; }); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts index 469f83288f..49b5e23f74 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component.ts @@ -1,31 +1,35 @@ import { Component, Injector, ViewChild } from '@angular/core'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { States } from "core-app/core/states/states.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { States } from 'core-app/core/states/states.service'; @Component({ - templateUrl: './highlighting-tab.component.html' + templateUrl: './highlighting-tab.component.html', }) -export class WpTableConfigurationHighlightingTab implements TabComponent { - +export class WpTableConfigurationHighlightingTabComponent implements TabComponent { // Display mode public highlightingMode:HighlightingMode = 'inline'; + public entireRowMode = false; + public lastEntireRowAttribute:HighlightingMode = 'status'; + public eeShowBanners = false; public availableInlineHighlightedAttributes:HalResource[] = []; + public selectedAttributes:any[] = []; - public availableRowHighlightedAttributes:{name:string; value:HighlightingMode}[] = []; + public availableRowHighlightedAttributes:{ name:string; value:HighlightingMode }[] = []; @ViewChild('highlightedAttributesNgSelect') public highlightedAttributesNgSelect:NgSelectComponent; + @ViewChild('rowHighlightNgSelect') public rowHighlightNgSelect:NgSelectComponent; public text = { @@ -41,15 +45,15 @@ export class WpTableConfigurationHighlightingTab implements TabComponent { entire_row_by: this.I18n.t('js.work_packages.table_configuration.highlighting_mode.entire_row_by'), }, upsaleAttributeHighlighting: this.I18n.t('js.work_packages.table_configuration.upsale.attribute_highlighting'), - upsaleCheckOutLink: this.I18n.t('js.work_packages.table_configuration.upsale.check_out_link') + upsaleCheckOutLink: this.I18n.t('js.work_packages.table_configuration.upsale.check_out_link'), }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly Banners:BannersService, - readonly wpTableHighlight:WorkPackageViewHighlightingService) { + readonly I18n:I18nService, + readonly states:States, + readonly querySpace:IsolatedQuerySpace, + readonly Banners:BannersService, + readonly wpTableHighlight:WorkPackageViewHighlightingService) { } ngOnInit() { @@ -71,7 +75,7 @@ export class WpTableConfigurationHighlightingTab implements TabComponent { public onSave() { const mode = this.highlightingMode; - this.wpTableHighlight.update({ mode: mode, selectedAttributes: this.selectedAttributes }); + this.wpTableHighlight.update({ mode, selectedAttributes: this.selectedAttributes }); } public updateMode(mode:HighlightingMode | 'entire-row') { @@ -98,7 +102,7 @@ export class WpTableConfigurationHighlightingTab implements TabComponent { } public get availableHighlightedAttributes():HalResource[] { - const schema = this.querySpace.queryForm.value!.schema; + const { schema } = this.querySpace.queryForm.value!; return schema.highlightedAttributes.allowedValues; } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts index fdf331584d..55ed7408ae 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts @@ -6,11 +6,11 @@ import { QUERY_SORT_BY_ASC, QUERY_SORT_BY_DESC, QuerySortByResource, -} from "core-app/features/hal/resources/query-sort-by-resource"; +} from 'core-app/features/hal/resources/query-sort-by-resource'; export class SortModalObject { constructor(public column:SortColumn, - public direction:string) { + public direction:string) { } } @@ -22,10 +22,9 @@ export interface SortColumn { export type SortingMode = 'automatic'|'manual'; @Component({ - templateUrl: './sort-by-tab.component.html' + templateUrl: './sort-by-tab.component.html', }) -export class WpTableConfigurationSortByTab implements TabComponent { - +export class WpTableConfigurationSortByTabComponent implements TabComponent { public text = { title: this.I18n.t('js.label_sort_by'), placeholder: this.I18n.t('js.placeholders.default'), @@ -42,32 +41,36 @@ export class WpTableConfigurationSortByTab implements TabComponent { readonly availableDirections = [ { href: QUERY_SORT_BY_ASC, name: this.I18n.t('js.label_ascending') }, - { href: QUERY_SORT_BY_DESC, name: this.I18n.t('js.label_descending') } + { href: QUERY_SORT_BY_DESC, name: this.I18n.t('js.label_descending') }, ]; public availableColumns:SortColumn[] = []; + public allColumns:SortColumn[] = []; + public sortationObjects:SortModalObject[] = []; + public emptyColumn:SortColumn = { name: this.text.placeholder, href: null }; public sortingMode:SortingMode = 'automatic'; + public manualSortColumn:SortColumn; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableSortBy:WorkPackageViewSortByService) { + readonly I18n:I18nService, + readonly wpTableSortBy:WorkPackageViewSortByService) { } public onSave() { let sortElements; if (this.sortingMode === 'automatic') { - sortElements = this.sortationObjects.filter(object => object.column !== null); + sortElements = this.sortationObjects.filter((object) => object.column !== null); } else { - sortElements = [ new SortModalObject(this.manualSortColumn, QUERY_SORT_BY_ASC) ]; + sortElements = [new SortModalObject(this.manualSortColumn, QUERY_SORT_BY_ASC)]; } - sortElements = sortElements.map(object => this.getMatchingSort(object.column.href!, object.direction)); + sortElements = sortElements.map((object) => this.getMatchingSort(object.column.href!, object.direction)); this.wpTableSortBy.update(_.compact(sortElements)); } @@ -76,13 +79,9 @@ export class WpTableConfigurationSortByTab implements TabComponent { .onReadyWithAvailable() .subscribe(() => { const allColumns:SortColumn[] = this.wpTableSortBy.available.filter( - (sort:QuerySortByResource) => { - return !sort.column.href!.endsWith('/parent'); - } + (sort:QuerySortByResource) => !sort.column.href!.endsWith('/parent'), ).map( - (sort:QuerySortByResource) => { - return { name: sort.column.name, href: sort.column.href }; - } + (sort:QuerySortByResource) => ({ name: sort.column.name, href: sort.column.href }), ); // For whatever reason, even though the UI doesnt implement it, @@ -91,11 +90,11 @@ export class WpTableConfigurationSortByTab implements TabComponent { this.getManualSortingOption(); - _.each(this.wpTableSortBy.current, sort => { + _.each(this.wpTableSortBy.current, (sort) => { if (!sort.column.href!.endsWith('/parent')) { this.sortationObjects.push( new SortModalObject({ name: sort.column.name, href: sort.column.href }, - sort.direction.href!) + sort.direction.href!), ); if (sort.column.href === this.manualSortColumn.href) { this.updateSortingMode('manual'); @@ -115,7 +114,7 @@ export class WpTableConfigurationSortByTab implements TabComponent { public updateUsedColumns() { const usedColumns = this.sortationObjects - .filter(o => o.column !== null) + .filter((o) => o.column !== null) .map((object:SortModalObject) => object.column); this.availableColumns = _.sortBy(_.differenceBy(this.allColumns, usedColumns, 'href'), 'name'); @@ -126,9 +125,7 @@ export class WpTableConfigurationSortByTab implements TabComponent { } private getMatchingSort(column:string, direction:string) { - return _.find(this.wpTableSortBy.available, sort => { - return sort.column.href === column && sort.direction.href === direction; - }); + return _.find(this.wpTableSortBy.available, (sort) => sort.column.href === column && sort.direction.href === direction); } private fillUpSortElements() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts index ba21e98821..0d5f62e2ca 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts @@ -4,18 +4,19 @@ import { TabComponent } from 'core-app/features/work-packages/components/wp-tabl import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { zoomLevelOrder } from "core-app/features/work-packages/components/wp-table/timeline/wp-timeline"; -import { TimelineLabels, TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; +import { zoomLevelOrder } from 'core-app/features/work-packages/components/wp-table/timeline/wp-timeline'; +import { TimelineLabels, TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; @Component({ - templateUrl: './timelines-tab.component.html' + templateUrl: './timelines-tab.component.html', }) -export class WpTableConfigurationTimelinesTab implements TabComponent { - +export class WpTableConfigurationTimelinesTabComponent implements TabComponent { public timelineVisible = false; + public availableAttributes:{ id:string, name:string }[]; public labels:TimelineLabels; + public availableLabels:string[]; public zoomLevel:TimelineZoomLevel; @@ -36,7 +37,7 @@ export class WpTableConfigurationTimelinesTab implements TabComponent { months: this.I18n.t('js.timelines.zoom.months'), quarters: this.I18n.t('js.timelines.zoom.quarters'), years: this.I18n.t('js.timelines.zoom.years'), - auto: this.I18n.t('js.timelines.zoom.auto') + auto: this.I18n.t('js.timelines.zoom.auto'), }, labels: { title: this.I18n.t('js.timelines.labels.title'), @@ -45,14 +46,14 @@ export class WpTableConfigurationTimelinesTab implements TabComponent { none: this.I18n.t('js.timelines.filter.noneSelection'), left: this.I18n.t('js.timelines.labels.left'), right: this.I18n.t('js.timelines.labels.right'), - farRight: this.I18n.t('js.timelines.labels.farRight') - } + farRight: this.I18n.t('js.timelines.labels.farRight'), + }, }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableTimeline:WorkPackageViewTimelineService, - readonly wpTableColumns:WorkPackageViewColumnsService) { + readonly I18n:I18nService, + readonly wpTableTimeline:WorkPackageViewTimelineService, + readonly wpTableColumns:WorkPackageViewColumnsService) { } public onSave() { @@ -60,7 +61,7 @@ export class WpTableConfigurationTimelinesTab implements TabComponent { ...this.wpTableTimeline.current, visible: this.timelineVisible, labels: this.labels, - zoomLevel: this.zoomLevel + zoomLevel: this.zoomLevel, }); } @@ -79,7 +80,7 @@ export class WpTableConfigurationTimelinesTab implements TabComponent { this.zoomLevel = this.wpTableTimeline.zoomLevel; // Current label models - const labels = this.wpTableTimeline.labels; + const { labels } = this.wpTableTimeline; this.labels = _.clone(labels); this.availableLabels = Object.keys(this.labels); diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts index d2357a76da..3ae0a3cb9a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts @@ -4,19 +4,19 @@ import { OnInit, } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; -import { QueryOperatorResource } from "core-app/features/hal/resources/query-operator-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; +import { QueryOperatorResource } from 'core-app/features/hal/resources/query-operator-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; @Component({ templateUrl: './wp-table-configuration-relation-selector.html', - selector: 'wp-table-configuration-relation-selector' + selector: 'wp-table-configuration-relation-selector', }) -export class WpTableConfigurationRelationSelectorComponent implements OnInit { +export class WpTableConfigurationRelationSelectorComponent implements OnInit { private relationFilterIds:string[] = [ 'parent', 'precedes', @@ -29,10 +29,11 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { 'partof', 'includes', 'requires', - 'required' + 'required', ]; public availableRelationFilters:QueryFilterResource[] = []; + public selectedRelationFilter:QueryFilterResource|undefined = undefined; public text = { @@ -40,25 +41,25 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { please_select: this.I18n.t('js.placeholders.selection'), // We need to inverse the translation strings, as the filters's are named the other way around than what // a user knows from the relations tab: - parent: this.I18n.t('js.relation_labels.children'), - precedes: this.I18n.t('js.relation_labels.follows'), - follows: this.I18n.t('js.relation_labels.precedes'), - relates: this.I18n.t('js.relation_labels.relates'), - duplicates: this.I18n.t('js.relation_labels.duplicated'), - duplicated: this.I18n.t('js.relation_labels.duplicates'), - blocks: this.I18n.t('js.relation_labels.blocked'), - blocked: this.I18n.t('js.relation_labels.blocks'), - requires: this.I18n.t('js.relation_labels.required'), - required: this.I18n.t('js.relation_labels.requires'), - partof: this.I18n.t('js.relation_labels.includes'), - includes: this.I18n.t('js.relation_labels.partof') + parent: this.I18n.t('js.relation_labels.children'), + precedes: this.I18n.t('js.relation_labels.follows'), + follows: this.I18n.t('js.relation_labels.precedes'), + relates: this.I18n.t('js.relation_labels.relates'), + duplicates: this.I18n.t('js.relation_labels.duplicated'), + duplicated: this.I18n.t('js.relation_labels.duplicates'), + blocks: this.I18n.t('js.relation_labels.blocked'), + blocked: this.I18n.t('js.relation_labels.blocks'), + requires: this.I18n.t('js.relation_labels.required'), + required: this.I18n.t('js.relation_labels.requires'), + partof: this.I18n.t('js.relation_labels.includes'), + includes: this.I18n.t('js.relation_labels.partof'), }; constructor(readonly injector:Injector, - readonly I18n:I18nService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly ConfigurationService:ConfigurationService, - readonly schemaCache:SchemaCacheService) { + readonly I18n:I18nService, + readonly wpTableFilters:WorkPackageViewFiltersService, + readonly ConfigurationService:ConfigurationService, + readonly schemaCache:SchemaCacheService) { } ngOnInit() { @@ -85,7 +86,7 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { public onRelationFilterSelected() { if (this.selectedRelationFilter) { this.removeRelationFiltersFromCurrentState(); - this.addFilterToCurrentState(this.selectedRelationFilter as QueryFilterResource); + this.addFilterToCurrentState(this.selectedRelationFilter); } } @@ -108,7 +109,7 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { } private getOperatorForId(filter:QueryFilterResource, id:string):QueryOperatorResource { - return _.find(this.schemaCache.of(filter).availableOperators, { 'id': id }) as QueryOperatorResource; + return _.find(this.schemaCache.of(filter).availableOperators, { id }) as QueryOperatorResource; } public compareRelationFilters(f1:undefined|QueryFilterResource, f2:undefined|QueryFilterResource):boolean { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts index e5d306e8c1..036a06f022 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts @@ -11,7 +11,7 @@ import { OnDestroy, OnInit, Optional, - ViewChild + ViewChild, } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; @@ -20,26 +20,25 @@ import { ActiveTabInterface, TabComponent, TabInterface, - TabPortalOutlet + TabPortalOutlet, } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; -import { ComponentType } from "@angular/cdk/portal"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { ComponentType } from '@angular/cdk/portal'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export const WpTableConfigurationModalPrependToken = new InjectionToken>('WpTableConfigurationModalPrependComponent'); @Component({ - templateUrl: './wp-table-configuration.modal.html' + templateUrl: './wp-table-configuration.modal.html', }) -export class WpTableConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy { - +export class WpTableConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy { /* Close on escape? */ public closeOnEscape = false; @@ -59,36 +58,38 @@ export class WpTableConfigurationModalComponent extends OpModalComponent impleme cancelButton: this.I18n.t('js.modals.button_cancel'), upsaleRelationColumns: this.I18n.t('js.modals.upsale_relation_columns'), - upsaleRelationColumnsLink: this.I18n.t('js.modals.upsale_relation_columns_link') + upsaleRelationColumnsLink: this.I18n.t('js.modals.upsale_relation_columns_link'), }; public onDataUpdated = new EventEmitter(); + public selectedColumnMap:{ [id:string]:boolean } = {}; // Get the view child we'll use as the portal host @ViewChild('tabContentOutlet', { static: true }) tabContentOutlet:ElementRef; + // And a reference to the actual portal host interface public tabPortalHost:TabPortalOutlet; // Try to load an optional provided configuration service, and fall back to the default one private wpTableConfigurationService:WpTableConfigurationService = - this.injector.get(WpTableConfigurationService, new WpTableConfigurationService(this.I18n)); + this.injector.get(WpTableConfigurationService, new WpTableConfigurationService(this.I18n)); constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - @Optional() @Inject(WpTableConfigurationModalPrependToken) public prependModalComponent:ComponentType|null, - readonly I18n:I18nService, - readonly injector:Injector, - readonly appRef:ApplicationRef, - readonly componentFactoryResolver:ComponentFactoryResolver, - readonly loadingIndicator:LoadingIndicatorService, - readonly querySpace:IsolatedQuerySpace, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly apiV3Service:APIV3Service, - readonly notificationService:WorkPackageNotificationService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly cdRef:ChangeDetectorRef, - readonly ConfigurationService:ConfigurationService, - readonly elementRef:ElementRef) { + @Optional() @Inject(WpTableConfigurationModalPrependToken) public prependModalComponent:ComponentType|null, + readonly I18n:I18nService, + readonly injector:Injector, + readonly appRef:ApplicationRef, + readonly componentFactoryResolver:ComponentFactoryResolver, + readonly loadingIndicator:LoadingIndicatorService, + readonly querySpace:IsolatedQuerySpace, + readonly wpStatesInitialization:WorkPackageStatesInitializationService, + readonly apiV3Service:APIV3Service, + readonly notificationService:WorkPackageNotificationService, + readonly wpTableColumns:WorkPackageViewColumnsService, + readonly cdRef:ChangeDetectorRef, + readonly ConfigurationService:ConfigurationService, + readonly elementRef:ElementRef) { super(locals, cdRef, elementRef); } @@ -100,13 +101,13 @@ export class WpTableConfigurationModalComponent extends OpModalComponent impleme this.tabContentOutlet.nativeElement, this.componentFactoryResolver, this.appRef, - this.injector + this.injector, ); this.loadingIndicator.indicator('modal').promise = this.loadForm() .then(() => { - const initialTabName = this.locals['initialTab']; - const initialTab = this.availableTabs.find(el => el.id === initialTabName); + const initialTabName = this.locals.initialTab; + const initialTab = this.availableTabs.find((el) => el.id === initialTabName); this.switchTo(initialTab || this.availableTabs[0]); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts index 612bb4680b..7fbfb50aaa 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service.ts @@ -1,21 +1,20 @@ import { Injectable } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WpTableConfigurationDisplaySettingsTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component'; -import { TabInterface } from "core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet"; -import { WpTableConfigurationColumnsTab } from "core-app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component"; -import { WpTableConfigurationFiltersTab } from "core-app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component"; -import { WpTableConfigurationSortByTab } from "core-app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component"; -import { WpTableConfigurationTimelinesTab } from "core-app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component"; -import { WpTableConfigurationHighlightingTab } from "core-app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component"; +import { WpTableConfigurationDisplaySettingsTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component'; +import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; +import { WpTableConfigurationColumnsTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component'; +import { WpTableConfigurationFiltersTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component'; +import { WpTableConfigurationSortByTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component'; +import { WpTableConfigurationTimelinesTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component'; +import { WpTableConfigurationHighlightingTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component'; @Injectable() export class WpTableConfigurationService { - protected _tabs:TabInterface[] = [ { id: 'columns', name: this.I18n.t('js.label_columns'), - componentClass: WpTableConfigurationColumnsTab, + componentClass: WpTableConfigurationColumnsTabComponent, }, { id: 'filters', @@ -25,23 +24,23 @@ export class WpTableConfigurationService { { id: 'sort-by', name: this.I18n.t('js.label_sort_by'), - componentClass: WpTableConfigurationSortByTab, + componentClass: WpTableConfigurationSortByTabComponent, }, { id: 'display-settings', name: this.I18n.t('js.work_packages.table_configuration.display_settings'), - componentClass: WpTableConfigurationDisplaySettingsTab, + componentClass: WpTableConfigurationDisplaySettingsTabComponent, }, { id: 'highlighting', name: this.I18n.t('js.work_packages.table_configuration.highlighting'), - componentClass: WpTableConfigurationHighlightingTab, + componentClass: WpTableConfigurationHighlightingTabComponent, }, { id: 'timelines', name: this.I18n.t('js.timelines.gantt_chart'), - componentClass: WpTableConfigurationTimelinesTab - } + componentClass: WpTableConfigurationTimelinesTabComponent, + }, ]; constructor(readonly I18n:I18nService) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts b/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts index 3f9daf89d1..9a268bc89e 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,14 +25,14 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Injectable } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Injectable } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -import { HookService } from "core-app/features/plugins/hook-service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageViewHierarchyIdentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageViewHierarchyIdentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; export type WorkPackageAction = { text:string; @@ -45,40 +45,39 @@ export type WorkPackageAction = { @Injectable() export class WorkPackageContextMenuHelperService { - private BULK_ACTIONS = [ { text: I18n.t('js.work_packages.bulk_actions.edit'), key: 'edit', link: 'update', - href: this.PathHelper.staticBase + '/work_packages/bulk/edit' + href: `${this.PathHelper.staticBase}/work_packages/bulk/edit`, }, { text: I18n.t('js.work_packages.bulk_actions.move'), key: 'move', link: 'move', - href: this.PathHelper.staticBase + '/work_packages/move/new' + href: `${this.PathHelper.staticBase}/work_packages/move/new`, }, { text: I18n.t('js.work_packages.bulk_actions.copy'), key: 'copy', link: 'copy', - href: this.PathHelper.staticBase + '/work_packages/move/new?copy=true' + href: `${this.PathHelper.staticBase}/work_packages/move/new?copy=true`, }, { text: I18n.t('js.work_packages.bulk_actions.delete'), key: 'delete', link: 'delete', - href: this.PathHelper.staticBase + '/work_packages/bulk?_method=delete' - } + href: `${this.PathHelper.staticBase}/work_packages/bulk?_method=delete`, + }, ]; constructor(private HookService:HookService, - private UrlParamsHelper:UrlParamsHelperService, - private wpViewRepresentation:WorkPackageViewDisplayRepresentationService, - private wpViewTimeline:WorkPackageViewTimelineService, - private wpViewIndent:WorkPackageViewHierarchyIdentationService, - private PathHelper:PathHelperService) { + private UrlParamsHelper:UrlParamsHelperService, + private wpViewRepresentation:WorkPackageViewDisplayRepresentationService, + private wpViewTimeline:WorkPackageViewTimelineService, + private wpViewIndent:WorkPackageViewHierarchyIdentationService, + private PathHelper:PathHelperService) { } public getPermittedActionLinks(workPackage:WorkPackageResource, permittedActionConstants:any, allowSplitScreenActions:boolean):WorkPackageAction[] { @@ -95,7 +94,7 @@ export class WorkPackageContextMenuHelperService { key: allowedAction.key, text: allowedAction.text, icon: allowedAction.icon, - link: allowedAction.link ? workPackage[allowedAction.link].href : undefined + link: allowedAction.link ? workPackage[allowedAction.link].href : undefined, }); }); @@ -105,17 +104,13 @@ export class WorkPackageContextMenuHelperService { public getIntersectOfPermittedActions(workPackages:any) { const bulkPermittedActions:any = []; - const permittedActions = _.filter(this.BULK_ACTIONS, (action:any) => { - return _.every(workPackages, (workPackage:WorkPackageResource) => { - return this.getAllowedActions(workPackage, [action]).length >= 1; - }); - }); + const permittedActions = _.filter(this.BULK_ACTIONS, (action:any) => _.every(workPackages, (workPackage:WorkPackageResource) => this.getAllowedActions(workPackage, [action]).length >= 1)); _.each(permittedActions, (permittedAction:any) => { bulkPermittedActions.push({ key: permittedAction.key, text: permittedAction.text, - link: this.getBulkActionLink(permittedAction, workPackages) + link: this.getBulkActionLink(permittedAction, workPackages), }); }); @@ -124,9 +119,7 @@ export class WorkPackageContextMenuHelperService { public getBulkActionLink(action:any, workPackages:any) { const workPackageIdParams = { - 'ids[]': workPackages.map(function(wp:any) { - return wp.id; - }) + 'ids[]': workPackages.map((wp:any) => wp.id), }; const serializedIdParams = this.UrlParamsHelper.buildQueryString(workPackageIdParams); @@ -134,7 +127,7 @@ export class WorkPackageContextMenuHelperService { const link = linkAndQueryString.shift(); const queryParts = linkAndQueryString.concat(new Array(serializedIdParams)); - return link + '?' + queryParts.join('&'); + return `${link}?${queryParts.join('&')}`; } private getAllowedActions(workPackage:WorkPackageResource, actions:WorkPackageAction[]):WorkPackageAction[] { @@ -142,7 +135,7 @@ export class WorkPackageContextMenuHelperService { _.each(actions, (action) => { if (action.link && workPackage.hasOwnProperty(action.link)) { - action.text = action.text || I18n.t('js.button_' + action.key); + action.text = action.text || I18n.t(`js.button_${action.key}`); allowedActions.push(action); } }); @@ -170,7 +163,7 @@ export class WorkPackageContextMenuHelperService { actions.push({ key: 'hierarchy-outdent', icon: 'icon-paragraph-left', - text: I18n.t("js.relation_buttons.hierarchy_outdent") + text: I18n.t('js.relation_buttons.hierarchy_outdent'), }); } @@ -179,7 +172,7 @@ export class WorkPackageContextMenuHelperService { actions.push({ key: 'hierarchy-indent', icon: 'icon-paragraph-right', - text: I18n.t("js.relation_buttons.hierarchy_indent") + text: I18n.t('js.relation_buttons.hierarchy_indent'), }); } @@ -191,34 +184,32 @@ export class WorkPackageContextMenuHelperService { if (!!workPackage.addRelation && this.wpViewTimeline.isVisible) { allowedActions.push({ - key: "relation-precedes", - text: I18n.t("js.relation_buttons.add_predecessor"), - link: "addRelation" + key: 'relation-precedes', + text: I18n.t('js.relation_buttons.add_predecessor'), + link: 'addRelation', }); allowedActions.push({ - key: "relation-follows", - text: I18n.t("js.relation_buttons.add_follower"), - link: "addRelation" + key: 'relation-follows', + text: I18n.t('js.relation_buttons.add_follower'), + link: 'addRelation', }); } if (!!workPackage.addChild && allowSplitScreenActions) { allowedActions.push({ - key: "relation-new-child", - text: I18n.t("js.relation_buttons.add_new_child"), - link: "addChild" + key: 'relation-new-child', + text: I18n.t('js.relation_buttons.add_new_child'), + link: 'addChild', }); } return allowedActions; } - public getPermittedActions(workPackages:WorkPackageResource[], permittedActionConstants:any, allowSplitScreenActions:boolean):WorkPackageAction[] { if (workPackages.length === 1) { return this.getPermittedActionLinks(workPackages[0], permittedActionConstants, allowSplitScreenActions); - } else { - return this.getIntersectOfPermittedActions(workPackages); } + return this.getIntersectOfPermittedActions(workPackages); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service.ts b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service.ts index ca2b2a0c4b..dd21cd43f1 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service.ts @@ -1,22 +1,25 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { TableDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; - -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { rowGroupClassName } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants"; -import { locatePredecessorBySelector } from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers"; -import { groupIdentifier } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { TableDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; -export class GroupByDragActionService extends TableDragActionService { +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { rowGroupClassName } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-classes.constants'; +import { locatePredecessorBySelector } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers'; +import { groupIdentifier } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +export class GroupByDragActionService extends TableDragActionService { @InjectField() wpTableGroupBy:WorkPackageViewGroupByService; + @InjectField() halEditing:HalResourceEditingService; + @InjectField() halEvents:HalEventsService; + @InjectField() halNotification:HalResourceNotificationService; + @InjectField() schemaCache:SchemaCacheService; public get applies() { @@ -39,12 +42,12 @@ export class GroupByDragActionService extends TableDragActionService { return this.halEditing .save(changeset) .then((saved) => this.halEvents.push(saved.resource, { eventType: 'updated' })) - .catch(e => this.halNotification.handleRawError(e, workPackage)); + .catch((e) => this.halNotification.handleRawError(e, workPackage)); } private getValueForGroup(el:HTMLElement):unknown|null { const groupHeader = locatePredecessorBySelector(el, `.${rowGroupClassName}`)!; - const match = this.groups.find(group => groupIdentifier(group) === groupHeader.dataset.groupIdentifier); + const match = this.groups.find((group) => groupIdentifier(group) === groupHeader.dataset.groupIdentifier); if (!match) { return null; @@ -55,16 +58,15 @@ export class GroupByDragActionService extends TableDragActionService { // Unwrap single links to properly use them return links.length === 1 ? links[0] : links; - } else { - return match.value; } + return match.value; } /** * Get the attribute we're grouping by */ private get groupedAttribute():string|null { - const current = this.wpTableGroupBy.current; + const { current } = this.wpTableGroupBy; return current ? current.id : null; } diff --git a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service.ts b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service.ts index 5dfd0fe0ca..1f027af2be 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service.ts @@ -1,19 +1,20 @@ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { TableDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { TableDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; import { hierarchyGroupClass, - hierarchyRootClass -} from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers"; -import { relationRowClass, isInsideCollapsedGroup } from "core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; + hierarchyRootClass, +} from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-hierarchy-helpers'; +import { relationRowClass, isInsideCollapsedGroup } from 'core-app/features/work-packages/components/wp-fast-table/helpers/wp-table-row-helpers'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; export class HierarchyDragActionService extends TableDragActionService { - @InjectField() private wpTableHierarchies:WorkPackageViewHierarchiesService; + @InjectField() private relationHierarchyService:WorkPackageRelationsHierarchyService; + @InjectField() private apiV3Service:APIV3Service; public get applies() { @@ -28,9 +29,7 @@ export class HierarchyDragActionService extends TableDragActionService { } public handleDrop(workPackage:WorkPackageResource, el:HTMLElement):Promise { - return this.determineParent(el).then((parentId:string|null) => { - return this.relationHierarchyService.changeParent(workPackage, parentId); - }); + return this.determineParent(el).then((parentId:string|null) => this.relationHierarchyService.changeParent(workPackage, parentId)); } /** @@ -93,13 +92,13 @@ export class HierarchyDragActionService extends TableDragActionService { let skipDroppedIntoGroup; if (inGroup || isRoot) { - const elementGroups = Array.from(element.classList).filter(listClass => listClass.includes('__hierarchy-group-')) || []; - const previousGroups = Array.from(previous.classList).filter(listClass => listClass.includes('__hierarchy-group-')) || []; - const nextGroups = next && Array.from(next.classList).filter(listClass => listClass.includes('__hierarchy-group-')) || []; + const elementGroups = Array.from(element.classList).filter((listClass) => listClass.includes('__hierarchy-group-')) || []; + const previousGroups = Array.from(previous.classList).filter((listClass) => listClass.includes('__hierarchy-group-')) || []; + const nextGroups = next && Array.from(next.classList).filter((listClass) => listClass.includes('__hierarchy-group-')) || []; const previousWpId = (previous as HTMLElement).dataset.workPackageId!; - const isLastElementOfGroup = !nextGroups.some(nextGroup => previousGroups.includes(nextGroup)) && !nextGroups.includes(hierarchyGroupClass(previousWpId)); - const elementAlreadyBelongsToGroup = elementGroups.some(elementGroup => previousGroups.includes(elementGroup)) || - elementGroups.includes(hierarchyGroupClass(previousWpId)); + const isLastElementOfGroup = !nextGroups.some((nextGroup) => previousGroups.includes(nextGroup)) && !nextGroups.includes(hierarchyGroupClass(previousWpId)); + const elementAlreadyBelongsToGroup = elementGroups.some((elementGroup) => previousGroups.includes(elementGroup)) + || elementGroups.includes(hierarchyGroupClass(previousWpId)); skipDroppedIntoGroup = isLastElementOfGroup && !elementAlreadyBelongsToGroup; } @@ -118,8 +117,6 @@ export class HierarchyDragActionService extends TableDragActionService { .id(wpId) .get() .toPromise() - .then((wp:WorkPackageResource) => { - return Promise.resolve(wp.parent?.id || null); - }); + .then((wp:WorkPackageResource) => Promise.resolve(wp.parent?.id || null)); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service.ts b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service.ts index cb8ece1a1f..f8b9d23f5f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service.ts @@ -1,16 +1,15 @@ -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { Injector } from "@angular/core"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { Injector } from '@angular/core'; export class TableDragActionService { - /** * Initialize an action service in the given isolated query space * @param querySpace The isolated query space for this table * @param injector The hierarchical injector for this table */ constructor(readonly querySpace:IsolatedQuerySpace, - readonly injector:Injector) { + readonly injector:Injector) { } /** diff --git a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service.ts b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service.ts index 0b43dff087..09319675dc 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service.ts @@ -1,8 +1,8 @@ -import { Injectable, Injector } from "@angular/core"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { TableDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service"; -import { HierarchyDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service"; -import { GroupByDragActionService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service"; +import { Injectable, Injector } from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { TableDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-action.service'; +import { HierarchyDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/hierarchy-drag-action.service'; +import { GroupByDragActionService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/group-by-drag-action.service'; interface ITableDragActionService { new(querySpace:IsolatedQuerySpace, injector:Injector):TableDragActionService; @@ -10,7 +10,6 @@ interface ITableDragActionService { @Injectable() export class TableDragActionsRegistryService { - private register:ITableDragActionService[] = [ HierarchyDragActionService, GroupByDragActionService, @@ -24,8 +23,8 @@ export class TableDragActionsRegistryService { const querySpace = injector.get(IsolatedQuerySpace); const match = this.register - .map(cls => new cls(querySpace, injector)) - .find(instance => instance.applies); + .map((cls) => new cls(querySpace, injector)) + .find((instance) => instance.applies); return match || new TableDragActionService(querySpace, injector); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts index 10bff14332..4d38858f0f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/embedded-tables-macro.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -24,10 +24,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See docs/COPYRIGHT.rdoc for more details. -//++ Ng1FieldControlsWrapper, +// ++ Ng1FieldControlsWrapper, -import { Component, ElementRef } from "@angular/core"; -import { WorkPackageTableConfigurationObject } from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; +import { Component, ElementRef } from '@angular/core'; +import { WorkPackageTableConfigurationObject } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; export const wpEmbeddedTableMacroSelector = 'macro.embedded-table'; @@ -37,15 +37,16 @@ export const wpEmbeddedTableMacroSelector = 'macro.embedded-table'; - ` + `, }) export class EmbeddedTablesMacroComponent { // noinspection JSUnusedGlobalSymbols public queryProps:any; + public configuration:WorkPackageTableConfigurationObject = { actionsColumnEnabled: false, columnMenuEnabled: false, - contextMenuEnabled: false + contextMenuEnabled: false, }; constructor(readonly elementRef:ElementRef) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component.ts index 8ee0f08e0b..ef212969ca 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component.ts @@ -1,39 +1,53 @@ -import { AfterViewInit, ChangeDetectorRef, Directive, Input, SimpleChanges } from '@angular/core'; -import { WorkPackageStatesInitializationService } from '../../wp-list/wp-states-initialization.service'; +import { + AfterViewInit, ChangeDetectorRef, Directive, Input, SimpleChanges, +} from '@angular/core'; import { WorkPackageTableConfiguration, - WorkPackageTableConfigurationObject + WorkPackageTableConfigurationObject, } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackagesViewBase } from "core-app/features/work-packages/routing/wp-view-base/work-packages-view.base"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackagesViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-packages-view.base'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { WorkPackageStatesInitializationService } from '../../wp-list/wp-states-initialization.service'; @Directive() export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewBase implements AfterViewInit { @Input('configuration') protected providedConfiguration:WorkPackageTableConfigurationObject; + @Input() public uniqueEmbeddedTableName = `embedded-table-${Date.now()}`; + @Input() public initialLoadingIndicator = true; public renderTable = false; + public showTablePagination = false; + public configuration:WorkPackageTableConfiguration; + public error:string|null = null; protected initialized = false; @InjectField() apiV3Service:APIV3Service; + @InjectField() querySpace:IsolatedQuerySpace; + @InjectField() I18n!:I18nService; + @InjectField() urlParamsHelper:UrlParamsHelperService; + @InjectField() loadingIndicatorService:LoadingIndicatorService; + @InjectField() wpStatesInitialization:WorkPackageStatesInitializationService; + @InjectField() currentProject:CurrentProjectService; + @InjectField() cdRef:ChangeDetectorRef; ngOnInit() { @@ -59,9 +73,8 @@ export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewB public get projectIdentifier() { if (this.configuration.projectContext) { return this.currentProject.identifier || undefined; - } else { - return this.configuration.projectIdentifier || undefined; } + return this.configuration.projectIdentifier || undefined; } public buildQueryProps() { @@ -92,12 +105,11 @@ export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewB } const params = this.urlParamsHelper.buildV3GetQueryFromQueryResource(query, pagination); - const promise = - this - .wpListService - .loadQueryFromExisting(query, params, this.queryProjectScope) - .toPromise() - .then((query) => this.wpStatesInitialization.updateQuerySpace(query, query.results)); + const promise = this + .wpListService + .loadQueryFromExisting(query, params, this.queryProjectScope) + .toPromise() + .then((query) => this.wpStatesInitialization.updateQuerySpace(query, query.results)); if (visible) { this.loadingIndicator = promise; @@ -122,9 +134,8 @@ export abstract class WorkPackageEmbeddedBaseComponent extends WorkPackagesViewB protected get queryProjectScope() { if (!this.configuration.projectContext) { return undefined; - } else { - return this.projectIdentifier; } + return this.projectIdentifier; } protected initializeStates(query:QueryResource) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts index 6c626b9538..ebecc1651d 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts @@ -1,4 +1,6 @@ -import { Component, ElementRef, Input, OnInit } from '@angular/core'; +import { + Component, ElementRef, Input, OnInit, +} from '@angular/core'; export const wpTableEntrySelector = 'wp-embedded-table-entry'; @@ -11,11 +13,13 @@ export const wpTableEntrySelector = 'wp-embedded-table-entry'; [configuration]="configuration"> - ` + `, }) export class WorkPackageEmbeddedTableEntryComponent implements OnInit { @Input() public queryProps:any; + @Input() public configuration:any; + @Input() public initialLoadingIndicator = true; constructor(readonly elementRef:ElementRef) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts index 56cbfc0ba4..7d4c153c71 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts @@ -1,26 +1,33 @@ -import { AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { + AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output, +} from '@angular/core'; import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; import { OpTableActionFactory } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; import { OpTableActionsService } from 'core-app/features/work-packages/components/wp-table/table-actions/table-actions.service'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; -import { WorkPackageEmbeddedBaseComponent } from "core-app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { distinctUntilChanged, map, take, withLatestFrom } from "rxjs/operators"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageEmbeddedBaseComponent } from 'core-app/features/work-packages/components/wp-table/embedded/wp-embedded-base.component'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { + distinctUntilChanged, map, take, withLatestFrom, +} from 'rxjs/operators'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ selector: 'wp-embedded-table', - templateUrl: './wp-embedded-table.html' + templateUrl: './wp-embedded-table.html', }) export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseComponent implements OnInit, AfterViewInit, OnDestroy { @Input('queryId') public queryId?:string; + @Input('queryProps') public queryProps:any = {}; + @Input() public tableActions:OpTableActionFactory[] = []; + @Input() public externalHeight = false; /** Inform about loading errors */ @@ -30,10 +37,15 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo @Output() public onQueryLoaded = new EventEmitter(); @InjectField() apiv3Service:APIV3Service; + @InjectField() opModalService:OpModalService; + @InjectField() tableActionsService:OpTableActionsService; + @InjectField() wpTableTimeline:WorkPackageViewTimelineService; + @InjectField() wpTablePagination:WorkPackageViewPaginationService; + @InjectField() keepTab:KeepTabService; // Cache the form promise @@ -60,21 +72,20 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo this.wpTablePagination .updates$() .pipe( - map(pagination => [pagination.page, pagination.perPage]), + map((pagination) => [pagination.page, pagination.perPage]), distinctUntilChanged(), this.untilDestroyed(), - withLatestFrom(this.querySpace.query.values$()) + withLatestFrom(this.querySpace.query.values$()), ).subscribe(([_, query]) => { - const pagination = this.wpTablePagination.paginationObject; - const params = this.urlParamsHelper.buildV3GetQueryFromQueryResource(query, pagination); + const pagination = this.wpTablePagination.paginationObject; + const params = this.urlParamsHelper.buildV3GetQueryFromQueryResource(query, pagination); - this.loadingIndicator = - this + this.loadingIndicator = this .wpListService .loadQueryFromExisting(query, params, this.queryProjectScope) .toPromise() .then((query) => this.initializeStates(query)); - }); + }); } public openConfigurationModal(onUpdated:() => void) { @@ -97,7 +108,6 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo super.initializeStates(query); - this.querySpace .initialized .values$() @@ -118,19 +128,18 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo return this.formPromise; } - return this.formPromise = - this - .apiv3Service - .withOptionalProject(this.projectIdentifier) - .queries - .form - .load(query) - .toPromise() - .then(([form, _]) => { - this.wpStatesInitialization.updateStatesFromForm(query, form); - return form; - }) - .catch(() => this.formPromise = undefined); + return this.formPromise = this + .apiv3Service + .withOptionalProject(this.projectIdentifier) + .queries + .form + .load(query) + .toPromise() + .then(([form, _]) => { + this.wpStatesInitialization.updateStatesFromForm(query, form); + return form; + }) + .catch(() => this.formPromise = undefined); } public loadQuery(visible = true, firstPage = false):Promise { @@ -162,7 +171,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo .find( this.queryProps, this.queryId, - this.queryProjectScope + this.queryProjectScope, ) .toPromise() .then((query:QueryResource) => { @@ -173,7 +182,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo .catch((error) => { this.error = this.I18n.t( 'js.error.embedded_table_loading', - { message: _.get(error, 'message', error) } + { message: _.get(error, 'message', error) }, ); this.onError.emit(error); }); @@ -189,7 +198,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo if (event.double) { this.$state.go( 'work-packages.show', - { workPackageId: event.workPackageId } + { workPackageId: event.workPackageId }, ); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts index 178c5b953a..d37f855048 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts @@ -1,9 +1,11 @@ -import { AfterViewInit, ChangeDetectorRef, Component, Inject, OnInit, ViewChild } from '@angular/core'; +import { + AfterViewInit, ChangeDetectorRef, Component, Inject, OnInit, ViewChild, +} from '@angular/core'; import { WorkPackageEmbeddedTableComponent } from 'core-app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; import { RestrictedWpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service'; -import { OpQueryConfigurationLocalsToken } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; +import { OpQueryConfigurationLocalsToken } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; export interface QueryConfigurationLocals { service:any; @@ -15,17 +17,16 @@ export interface QueryConfigurationLocals { @Component({ templateUrl: './external-query-configuration.template.html', - providers: [[{ provide: WpTableConfigurationService, useClass: RestrictedWpTableConfigurationService }]] + providers: [[{ provide: WpTableConfigurationService, useClass: RestrictedWpTableConfigurationService }]], }) export class ExternalQueryConfigurationComponent implements OnInit, AfterViewInit { - @ViewChild('embeddedTableForConfiguration', { static: true }) private embeddedTable:WorkPackageEmbeddedTableComponent; queryProps:string; constructor(@Inject(OpQueryConfigurationLocalsToken) readonly locals:QueryConfigurationLocals, - readonly urlParamsHelper:UrlParamsHelperService, - readonly cdRef:ChangeDetectorRef) { + readonly urlParamsHelper:UrlParamsHelperService, + readonly cdRef:ChangeDetectorRef) { } ngOnInit() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants.ts index 1cb2916817..f645a8346e 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants.ts @@ -1,3 +1,3 @@ -import { InjectionToken } from "@angular/core"; +import { InjectionToken } from '@angular/core'; export const OpQueryConfigurationLocalsToken = new InjectionToken('OpQueryConfigurationLocalsToken'); diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts index 265dadd9e3..5dbe38ce21 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service.ts @@ -1,28 +1,30 @@ -import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core'; +import { + ApplicationRef, ComponentFactoryResolver, Injectable, Injector, +} from '@angular/core'; import { ComponentPortal, DomPortalOutlet, PortalInjector } from '@angular/cdk/portal'; import { TransitionService } from '@uirouter/core'; import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; import { ExternalQueryConfigurationComponent, - QueryConfigurationLocals -} from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component"; -import { OpQueryConfigurationLocalsToken } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants"; + QueryConfigurationLocals, +} from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component'; +import { OpQueryConfigurationLocalsToken } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants'; export type Class = { new(...args:any[]):any; }; @Injectable() export class ExternalQueryConfigurationService { - // Hold a reference to the DOM node we're using as a host private _portalHostElement:HTMLElement; + // And a reference to the actual portal host interface on top of the element private _bodyPortalHost:DomPortalOutlet; constructor(private componentFactoryResolver:ComponentFactoryResolver, - readonly FocusHelper:FocusHelperService, - private appRef:ApplicationRef, - private $transitions:TransitionService, - private injector:Injector) { + readonly FocusHelper:FocusHelperService, + private appRef:ApplicationRef, + private $transitions:TransitionService, + private injector:Injector) { } /** @@ -38,7 +40,7 @@ export class ExternalQueryConfigurationService { hostElement, this.componentFactoryResolver, this.appRef, - this.injector + this.injector, ); } @@ -55,7 +57,7 @@ export class ExternalQueryConfigurationService { const portal = new ComponentPortal( this.externalQueryConfigurationComponent(), null, - this.injectorFor(data) + this.injectorFor(data), ); this.bodyPortalHost.attach(portal); this._portalHostElement.style.display = 'block'; diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts index 0d8995bae2..73b605ea76 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts @@ -3,17 +3,17 @@ import { } from '@angular/core'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; import { RestrictedWpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service'; -import { WpTableConfigurationRelationSelectorComponent } from "core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector"; -import { WpTableConfigurationModalPrependToken } from "core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal"; -import { ExternalQueryConfigurationComponent } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component"; +import { WpTableConfigurationRelationSelectorComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector'; +import { WpTableConfigurationModalPrependToken } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; +import { ExternalQueryConfigurationComponent } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component'; @Component({ templateUrl: './external-query-configuration.template.html', providers: [ [ - { provide: WpTableConfigurationService, useClass: RestrictedWpTableConfigurationService } + { provide: WpTableConfigurationService, useClass: RestrictedWpTableConfigurationService }, ], - { provide: WpTableConfigurationModalPrependToken, useValue: WpTableConfigurationRelationSelectorComponent } + { provide: WpTableConfigurationModalPrependToken, useValue: WpTableConfigurationRelationSelectorComponent }, ], }) export class ExternalRelationQueryConfigurationComponent extends ExternalQueryConfigurationComponent { diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service.ts index 2362c442b7..8e9b8e515d 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { Class, - ExternalQueryConfigurationService -} from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service"; -import { ExternalRelationQueryConfigurationComponent } from "core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component"; + ExternalQueryConfigurationService, +} from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; +import { ExternalRelationQueryConfigurationComponent } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component'; @Injectable() export class ExternalRelationQueryConfigurationService extends ExternalQueryConfigurationService { diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts index a074eaba8a..788576f6da 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/restricted-wp-table-configuration.service.ts @@ -3,13 +3,12 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabInterface } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; import { QueryConfigurationLocals } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component'; -import { OpQueryConfigurationLocalsToken } from "core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants"; +import { OpQueryConfigurationLocalsToken } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.constants'; @Injectable() export class RestrictedWpTableConfigurationService extends WpTableConfigurationService { - constructor(@Inject(OpQueryConfigurationLocalsToken) readonly locals:QueryConfigurationLocals, - readonly I18n:I18nService) { + readonly I18n:I18nService) { super(I18n); } @@ -18,7 +17,7 @@ export class RestrictedWpTableConfigurationService extends WpTableConfigurationS return this ._tabs - .map(el => { + .map((el) => { const reason = disabledTabs[el.id]; if (reason != null) { el.disable = reason; @@ -27,4 +26,4 @@ export class RestrictedWpTableConfigurationService extends WpTableConfigurationS return el; }); } -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts index 12b1c2691b..05586351e4 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/sort-header/sort-header.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input } from '@angular/core'; +import { + AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { RelationQueryColumn, TypeRelationQueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; -import { QUERY_SORT_BY_ASC, QUERY_SORT_BY_DESC } from "core-app/features/hal/resources/query-sort-by-resource"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { combineLatest } from "rxjs"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; - +import { QUERY_SORT_BY_ASC, QUERY_SORT_BY_DESC } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { combineLatest } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Component({ selector: 'sortHeader', - templateUrl: './sort-header.directive.html' + templateUrl: './sort-header.directive.html', }) export class SortHeaderDirective extends UntilDestroyedMixin implements AfterViewInit { - @Input() headerColumn:any; @Input() locale:string; @@ -58,7 +58,7 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie public text = { toggleHierarchy: this.I18n.t('js.work_packages.hierarchy.show'), openMenu: this.I18n.t('js.label_open_menu'), - sortColumn: 'Sorting column' // TODO + sortColumn: 'Sorting column', // TODO }; isHierarchyColumn:boolean; @@ -76,12 +76,12 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie private currentSortDirection:any; constructor(private wpTableHierarchies:WorkPackageViewHierarchiesService, - private wpTableSortBy:WorkPackageViewSortByService, - private wpTableGroupBy:WorkPackageViewGroupByService, - private wpTableRelationColumns:WorkPackageViewRelationColumnsService, - private elementRef:ElementRef, - private cdRef:ChangeDetectorRef, - private I18n:I18nService) { + private wpTableSortBy:WorkPackageViewSortByService, + private wpTableGroupBy:WorkPackageViewGroupByService, + private wpTableRelationColumns:WorkPackageViewRelationColumnsService, + private elementRef:ElementRef, + private cdRef:ChangeDetectorRef, + private I18n:I18nService) { super(); } @@ -94,10 +94,10 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie combineLatest([ this.wpTableSortBy.onReadyWithAvailable(), - this.wpTableSortBy.live$() + this.wpTableSortBy.live$(), ]) .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { const latestSortElement = this.wpTableSortBy.current[0]; @@ -129,10 +129,9 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie this.columnName = (this.headerColumn as TypeRelationQueryColumn).type.name; } else if (this.wpTableRelationColumns.relationColumnType(this.headerColumn) === 'ofType') { this.columnType = 'relation'; - this.columnName = I18n.t('js.relation_labels.' + (this.headerColumn as RelationQueryColumn).relationType); + this.columnName = I18n.t(`js.relation_labels.${(this.headerColumn as RelationQueryColumn).relationType}`); } - if (this.isHierarchyColumn) { this.hierarchyIcon = 'icon-hierarchy'; this.isHierarchyDisabled = this.wpTableGroupBy.isEnabled; @@ -141,7 +140,7 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie this.wpTableGroupBy .live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.isHierarchyDisabled = this.wpTableGroupBy.isEnabled; @@ -152,7 +151,7 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie this.wpTableHierarchies .live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.setHierarchyIcon(); @@ -201,20 +200,16 @@ export class SortHeaderDirective extends UntilDestroyedMixin implements AfterVie } switch (this.currentSortDirection.href) { - case QUERY_SORT_BY_ASC: - return 'asc'; - case QUERY_SORT_BY_DESC: - return 'desc'; - default: - return ''; + case QUERY_SORT_BY_ASC: + return 'asc'; + case QUERY_SORT_BY_DESC: + return 'desc'; + default: + return ''; } } setActiveColumnClass() { this.element.toggleClass('active-column', !!this.currentSortDirection); } - } - - - diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/context-menu-table-action.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/context-menu-table-action.ts index 429bdf010b..988551de1a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/context-menu-table-action.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/context-menu-table-action.ts @@ -1,16 +1,15 @@ import { contextColumnIcon, contextMenuLinkClassName, - OpTableAction + OpTableAction, } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; import { opIconElement } from 'core-app/shared/helpers/op-icon-builder'; export class OpContextMenuTableAction extends OpTableAction { - public readonly identifier = 'open-context-menu-action'; private text = { - linkTitle: this.I18n.t('js.label_open_context_menu') + linkTitle: this.I18n.t('js.label_open_context_menu'), }; public buildElement() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/details-table-action.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/details-table-action.ts index b7828fb7e6..47b3c0a09f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/details-table-action.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/details-table-action.ts @@ -3,16 +3,17 @@ import { opIconElement } from 'core-app/shared/helpers/op-icon-builder'; import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; import { UiStateLinkBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder'; -import { StateService } from "@uirouter/core"; +import { StateService } from '@uirouter/core'; export const detailsLinkClassName = 'wp-table--details-link'; export class OpDetailsTableAction extends OpTableAction { - public readonly identifier = 'open-details-action'; + private uiStatebuilder = new UiStateLinkBuilder(this.injector.get(StateService), this.injector.get(KeepTabService)); + private text = { - button: this.I18n.t('js.button_open_details') + button: this.I18n.t('js.button_open_details'), }; public buildElement() { @@ -20,7 +21,7 @@ export class OpDetailsTableAction extends OpTableAction { const detailsLink = this.uiStatebuilder.linkToDetails( this.workPackage.id!, this.text.button, - '' + '', ); detailsLink.classList.add(detailsLinkClassName, contextColumnIcon, 'hidden-for-mobile'); diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action.ts index af4d5e3b37..9277c8472a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/actions/unlink-table-action.ts @@ -5,18 +5,16 @@ import { } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; import { opIconElement } from 'core-app/shared/helpers/op-icon-builder'; import { Injector } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; export class OpUnlinkTableAction extends OpTableAction { - constructor(public injector:Injector, - public workPackage:WorkPackageResource, - public readonly identifier:string, - private title:string, - readonly applicable:(workPackage:WorkPackageResource) => boolean, - readonly onClick:(workPackage:WorkPackageResource) => void) { + public workPackage:WorkPackageResource, + public readonly identifier:string, + private title:string, + readonly applicable:(workPackage:WorkPackageResource) => boolean, + readonly onClick:(workPackage:WorkPackageResource) => void) { super(injector, workPackage); - } /** @@ -30,14 +28,12 @@ export class OpUnlinkTableAction extends OpTableAction { title:string, onClick:(workPackage:WorkPackageResource) => void, applicable:(workPackage:WorkPackageResource) => boolean = () => true):OpTableActionFactory { - return (injector:Injector, workPackage:WorkPackageResource) => { - return new OpUnlinkTableAction(injector, - workPackage, - identifier, - title, - applicable, - onClick) as OpTableAction; - }; + return (injector:Injector, workPackage:WorkPackageResource) => new OpUnlinkTableAction(injector, + workPackage, + identifier, + title, + applicable, + onClick) as OpTableAction; } public buildElement() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-action.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-action.ts index f55b60b08e..5c9120c0d0 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-action.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-action.ts @@ -1,7 +1,7 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export type OpTableActionFactory = (i:Injector, wp:WorkPackageResource) => OpTableAction; export const contextMenuTdClassName = 'wp-table--context-menu-td'; @@ -10,11 +10,10 @@ export const contextMenuLinkClassName = 'wp-table-context-menu-link'; export const contextColumnIcon = 'wp-table-context-menu-icon'; export abstract class OpTableAction { - @InjectField() I18n!:I18nService; constructor(readonly injector:Injector, - readonly workPackage:WorkPackageResource) { + readonly workPackage:WorkPackageResource) { } /** Identifier to uniquely identify the action */ diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts index e993b68548..2c927c6bad 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-actions/table-actions.service.ts @@ -4,11 +4,10 @@ import { } from 'core-app/features/work-packages/components/wp-table/table-actions/table-action'; import { OpDetailsTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/details-table-action'; import { OpContextMenuTableAction } from 'core-app/features/work-packages/components/wp-table/table-actions/actions/context-menu-table-action'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Injectable() export class OpTableActionsService { - constructor(private readonly injector:Injector) { } diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts index 8ee4b20425..f5f59f4d6a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,40 +27,34 @@ //++ import { HttpClientModule } from '@angular/common/http'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { inject, TestBed, waitForAsync } from '@angular/core/testing'; import { States } from 'core-app/core/states/states.service'; import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; import { WorkPackageTablePaginationComponent } from 'core-app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { OpenProject } from "core-app/core/setup/globals/openproject"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { OpIconComponent } from "core-app/shared/components/icon/icon.component"; -import { IPaginationOptions, PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; -import { PaginationInstance } from "core-app/shared/components/table-pagination/pagination-instance"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { OpenProject } from 'core-app/core/setup/globals/openproject'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { OpIconComponent } from 'core-app/shared/components/icon/icon.component'; +import { IPaginationOptions, PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; +import { PaginationInstance } from 'core-app/shared/components/table-pagination/pagination-instance'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; function setupMocks(paginationService:PaginationService) { const options:IPaginationOptions = { perPage: 0, perPageOptions: [10, 50], maxVisiblePageOptions: 1, - optionsTruncationSize: 6 + optionsTruncationSize: 6, }; - spyOn(paginationService, 'getMaxVisiblePageOptions').and.callFake(() => { - return options.maxVisiblePageOptions; - }); + spyOn(paginationService, 'getMaxVisiblePageOptions').and.callFake(() => options.maxVisiblePageOptions); - spyOn(paginationService, 'getOptionsTruncationSize').and.callFake(() => { - return options.optionsTruncationSize; - }); + spyOn(paginationService, 'getOptionsTruncationSize').and.callFake(() => options.optionsTruncationSize); - spyOn(paginationService, 'loadPaginationOptions').and.callFake(() => { - return Promise.resolve(options); - }); + spyOn(paginationService, 'loadPaginationOptions').and.callFake(() => Promise.resolve(options)); } function pageString(element:JQuery) { @@ -68,18 +62,17 @@ function pageString(element:JQuery) { } describe('wpTablePagination Directive', () => { - beforeEach(waitForAsync(() => { window.OpenProject = new OpenProject(); // noinspection JSIgnoredPromiseFromCall TestBed.configureTestingModule({ imports: [ - HttpClientModule + HttpClientModule, ], declarations: [ WorkPackageTablePaginationComponent, - OpIconComponent + OpIconComponent, ], providers: [ States, @@ -90,13 +83,12 @@ describe('wpTablePagination Directive', () => { HalResourceService, ConfigurationService, IsolatedQuerySpace, - I18nService - ] + I18nService, + ], }).compileComponents(); })); - describe('page ranges and links', function() { - + describe('page ranges and links', () => { it('should display the correct page range', inject([PaginationService], (paginationService:PaginationService) => { setupMocks(paginationService); @@ -115,7 +107,7 @@ describe('wpTablePagination Directive', () => { expect(pageString(element)).toEqual('(1 - 10/11)'); })); - describe('"next" link', function() { + describe('"next" link', () => { it('hidden on the last page', inject([PaginationService], (paginationService:PaginationService) => { setupMocks(paginationService); @@ -161,7 +153,3 @@ describe('wpTablePagination Directive', () => { })); }); }); - - - - diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts index 3d2da50c22..cd2275e5aa 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,32 +26,32 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageViewPaginationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { WorkPackageViewPagination } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { WorkPackageViewPagination } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { combineLatest } from 'rxjs'; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { TablePaginationComponent } from "core-app/shared/components/table-pagination/table-pagination.component"; -import { IPaginationOptions, PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { TablePaginationComponent } from 'core-app/shared/components/table-pagination/table-pagination.component'; +import { IPaginationOptions, PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; @Component({ templateUrl: '../../../../../shared/components/table-pagination/table-pagination.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - selector: 'wp-table-pagination' + selector: 'wp-table-pagination', }) export class WorkPackageTablePaginationComponent extends TablePaginationComponent implements OnInit, OnDestroy { - constructor(protected paginationService:PaginationService, - protected cdRef:ChangeDetectorRef, - protected wpTablePagination:WorkPackageViewPaginationService, - readonly querySpace:IsolatedQuerySpace, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly I18n:I18nService) { + protected cdRef:ChangeDetectorRef, + protected wpTablePagination:WorkPackageViewPaginationService, + readonly querySpace:IsolatedQuerySpace, + readonly wpTableSortBy:WorkPackageViewSortByService, + readonly I18n:I18nService) { super(paginationService, cdRef, I18n); - } ngOnInit() { @@ -65,7 +65,7 @@ export class WorkPackageTablePaginationComponent extends TablePaginationComponen this.wpTablePagination .live$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wpPagination:WorkPackageViewPagination) => { this.pagination = wpPagination.current; @@ -75,9 +75,9 @@ export class WorkPackageTablePaginationComponent extends TablePaginationComponen // hide/show pagination options depending on the sort mode combineLatest([ this.querySpace.query.values$(), - this.wpTableSortBy.live$() + this.wpTableSortBy.live$(), ]).pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe(([query, sort]) => { this.showPerPage = this.showPageSelections = !this.isManualSortingMode; this.infoText = this.paginationInfoText(query.results); @@ -88,7 +88,7 @@ export class WorkPackageTablePaginationComponent extends TablePaginationComponen public selectPerPage(perPage:number) { this.paginationService.setPerPage(perPage); - this.wpTablePagination.updateFromObject({ page: 1, perPage: perPage }); + this.wpTablePagination.updateFromObject({ page: 1, perPage }); } public showPage(pageNumber:number) { @@ -103,8 +103,7 @@ export class WorkPackageTablePaginationComponent extends TablePaginationComponen if (this.isManualSortingMode && (work_packages.count < work_packages.total)) { return I18n.t('js.work_packages.limited_results', { count: work_packages.count }); - } else { - return undefined; } + return undefined; } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-cell-renderer.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-cell-renderer.ts index 88f11a0d0e..9271b1f38f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-cell-renderer.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-cell-renderer.ts @@ -1,13 +1,16 @@ import * as moment from 'moment'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { - calculatePositionValueForDayCount, - calculatePositionValueForDayCountingPx, - RenderInfo, - timelineBackgroundElementClass, - timelineElementCssClass, - timelineMarkerSelectionStartClass -} from '../wp-timeline'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { DisplayFieldRenderer } from 'core-app/shared/components/fields/display/display-field-renderer'; +import { Injector } from '@angular/core'; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { HierarchyRenderPass } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; +import { classNameBarLabel, classNameLeftHandle, classNameRightHandle } from './wp-timeline-cell-mouse-handler'; import { classNameFarRightLabel, classNameHideOnHover, @@ -18,20 +21,17 @@ import { classNameRightHoverLabel, classNameRightLabel, classNameShowOnHover, - WorkPackageCellLabels + WorkPackageCellLabels, } from './wp-timeline-cell'; -import { classNameBarLabel, classNameLeftHandle, classNameRightHandle } from './wp-timeline-cell-mouse-handler'; -import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; -import { DisplayFieldRenderer } from 'core-app/shared/components/fields/display/display-field-renderer'; -import { Injector } from '@angular/core'; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { HierarchyRenderPass } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/hierarchy/hierarchy-render-pass"; +import { + calculatePositionValueForDayCount, + calculatePositionValueForDayCountingPx, + RenderInfo, + timelineBackgroundElementClass, + timelineElementCssClass, + timelineMarkerSelectionStartClass, +} from '../wp-timeline'; import Moment = moment.Moment; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; export interface CellDateMovement { // Target values to move work package to @@ -48,12 +48,15 @@ class TimezoneService { export class TimelineCellRenderer { @InjectField() wpTableTimeline:WorkPackageViewTimelineService; + @InjectField() TimezoneService:TimezoneService; + @InjectField() schemaCache:SchemaCacheService; + @InjectField() I18n!:I18nService; public text = { - label_children_derived_duration: this.I18n.t('js.label_children_derived_duration') + label_children_derived_duration: this.I18n.t('js.label_children_derived_duration'), }; public ganttChartRowHeight:number; @@ -63,7 +66,7 @@ export class TimelineCellRenderer { protected dateDisplaysOnMouseMove:{ left?:HTMLElement; right?:HTMLElement } = {}; constructor(readonly injector:Injector, - readonly workPackageTimeline:WorkPackageTimelineTableController) { + readonly workPackageTimeline:WorkPackageTimelineTableController) { this.ganttChartRowHeight = +getComputedStyle(document.documentElement) .getPropertyValue('--table-timeline--row-height') .replace('px', ''); @@ -94,7 +97,7 @@ export class TimelineCellRenderer { placeholder.style.height = '1em'; placeholder.style.width = '30px'; placeholder.style.zIndex = '9999'; - placeholder.style.left = (days * renderInfo.viewParams.pixelPerDay) + 'px'; + placeholder.style.left = `${days * renderInfo.viewParams.pixelPerDay}px`; this.applyTypeColor(renderInfo, placeholder); @@ -109,7 +112,6 @@ export class TimelineCellRenderer { public assignDateValues(change:WorkPackageChangeset, labels:WorkPackageCellLabels, dates:any):void { - this.assignDate(change, 'startDate', dates.startDate); this.assignDate(change, 'dueDate', dates.dueDate); @@ -124,7 +126,6 @@ export class TimelineCellRenderer { dayUnderCursor:Moment, delta:number, direction:'left'|'right'|'both'|'create'|'dragright'):CellDateMovement { - const initialStartDate = change.pristineResource.startDate; const initialDueDate = change.pristineResource.dueDate; @@ -167,7 +168,6 @@ export class TimelineCellRenderer { renderInfo:RenderInfo, labels:WorkPackageCellLabels, elem:HTMLElement):'left'|'right'|'both'|'dragright'|'create' { - // check for active selection mode if (renderInfo.viewParams.activeSelectionMode) { renderInfo.viewParams.activeSelectionMode(renderInfo.workPackage); @@ -184,7 +184,7 @@ export class TimelineCellRenderer { direction = 'left'; this.workPackageTimeline.forceCursor('col-resize'); if (projection.startDate === null) { - projection.startDate = projection['dueDate']; + projection.startDate = projection.dueDate; } } else if (jQuery(ev.target!).hasClass(classNameRightHandle) || dateForCreate) { // only right @@ -216,7 +216,7 @@ export class TimelineCellRenderer { * false, if the element must be removed from the timeline. */ public update(element:HTMLDivElement, labels:WorkPackageCellLabels|null, renderInfo:RenderInfo):boolean { - const change = renderInfo.change; + const { change } = renderInfo; const bar = element.querySelector(`.${timelineBackgroundElementClass}`) as HTMLElement; let start = moment(change.projectedResource.startDate); let due = moment(change.projectedResource.dueDate); @@ -231,13 +231,13 @@ export class TimelineCellRenderer { if (_.isNaN(due.valueOf()) && !_.isNaN(start.valueOf())) { // Set due date to today due = moment(); - bar.style.backgroundImage = `linear-gradient(90deg, rgba(255,255,255,0) 0%, #F1F1F1 100%)`; + bar.style.backgroundImage = 'linear-gradient(90deg, rgba(255,255,255,0) 0%, #F1F1F1 100%)'; } // only finish date, fade out bar to the left if (_.isNaN(start.valueOf()) && !_.isNaN(due.valueOf())) { start = due.clone(); - bar.style.backgroundImage = `linear-gradient(90deg, #F1F1F1 0%, rgba(255,255,255,0) 80%)`; + bar.style.backgroundImage = 'linear-gradient(90deg, #F1F1F1 0%, rgba(255,255,255,0) 80%)'; } this.setElementPositionAndSize(element, renderInfo, start, due); @@ -258,7 +258,7 @@ export class TimelineCellRenderer { if (renderInfo.viewParams.activeSelectionMode) { element.style.backgroundImage = ''; // required! unable to disable "fade out bar" with css - if (renderInfo.viewParams.selectionModeStart === '' + renderInfo.workPackage.id!) { + if (renderInfo.viewParams.selectionModeStart === `${renderInfo.workPackage.id!}`) { jQuery(element).addClass(timelineMarkerSelectionStartClass); element.style.background = 'none'; } @@ -310,7 +310,7 @@ export class TimelineCellRenderer { const left = document.createElement('div'); const right = document.createElement('div'); - container.className = timelineElementCssClass + ' ' + this.type; + container.className = `${timelineElementCssClass} ${this.type}`; bar.className = timelineBackgroundElementClass; left.className = classNameLeftHandle; right.className = classNameRightHandle; @@ -366,11 +366,11 @@ export class TimelineCellRenderer { protected applyTypeColor(renderInfo:RenderInfo, bg:HTMLElement):void { const wp = renderInfo.workPackage; - const type = wp.type; + const { type } = wp; const selectionMode = renderInfo.viewParams.activeSelectionMode; // Don't apply the class in selection mode - const id = type.id; + const { id } = type; if (selectionMode) { bg.classList.remove(Highlighting.backgroundClass('type', id!)); } else { @@ -385,7 +385,7 @@ export class TimelineCellRenderer { } setElementPositionAndSize(element:HTMLElement, renderInfo:RenderInfo, start:moment.Moment, due:moment.Moment) { - const viewParams = renderInfo.viewParams; + const { viewParams } = renderInfo; // offset left const offsetStart = start.diff(viewParams.dateDisplayStart, 'days'); element.style.left = calculatePositionValueForDayCount(viewParams, offsetStart); @@ -397,7 +397,7 @@ export class TimelineCellRenderer { // ensure minimum width if (!_.isNaN(start.valueOf()) || !_.isNaN(due.valueOf())) { const minWidth = _.max([renderInfo.viewParams.pixelPerDay, 2]); - element.style.minWidth = minWidth + 'px'; + element.style.minWidth = `${minWidth}px`; } } @@ -436,7 +436,7 @@ export class TimelineCellRenderer { childrenDurationBar.classList.add('children-duration-bar', '-clamp-style'); childrenDurationBar.title = this.text.label_children_derived_duration; childrenDurationHoverContainer.classList.add('children-duration-hover-container'); - childrenDurationHoverContainer.style.height = this.ganttChartRowHeight * visibleChildren + 10 + 'px'; + childrenDurationHoverContainer.style.height = `${this.ganttChartRowHeight * visibleChildren + 10}px`; if (derivedStartDate.isBefore(startDate) || derivedDueDate.isAfter(dueDate)) { childrenDurationBar.classList.add('-duration-overflow'); @@ -449,14 +449,13 @@ export class TimelineCellRenderer { } childrenDurationBar.appendChild(childrenDurationHoverContainer); - row!.appendChild(childrenDurationBar); + row.appendChild(childrenDurationBar); } } protected updateLabels(activeDragNDrop:boolean, labels:WorkPackageCellLabels, change:WorkPackageChangeset) { - const labelConfiguration = this.wpTableTimeline.getNormalizedLabels(change.projectedResource); if (!activeDragNDrop) { @@ -479,7 +478,6 @@ export class TimelineCellRenderer { labels:WorkPackageCellLabels, position:LabelPosition|'leftHover'|'rightHover', attribute:string|null) { - // Get the label position // Skip label if it does not exist (milestones) const label = labels[position]; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-milestone-cell-renderer.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-milestone-cell-renderer.ts index e13b452784..052a877d38 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-milestone-cell-renderer.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/timeline-milestone-cell-renderer.ts @@ -1,9 +1,10 @@ import * as moment from 'moment'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; import { calculatePositionValueForDayCountingPx, RenderInfo, - timelineElementCssClass + timelineElementCssClass, } from '../wp-timeline'; import { CellDateMovement, LabelPosition, TimelineCellRenderer } from './timeline-cell-renderer'; import { @@ -16,10 +17,9 @@ import { classNameRightHoverLabel, classNameRightLabel, classNameShowOnHover, - WorkPackageCellLabels + WorkPackageCellLabels, } from './wp-timeline-cell'; import Moment = moment.Moment; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { public get type():string { @@ -44,7 +44,7 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { placeholder.style.pointerEvents = 'none'; placeholder.style.height = '1em'; placeholder.style.width = '1em'; - placeholder.style.left = (days * renderInfo.viewParams.pixelPerDay) + 'px'; + placeholder.style.left = `${days * renderInfo.viewParams.pixelPerDay}px`; const diamond = document.createElement('div'); diamond.className = 'diamond'; @@ -66,7 +66,6 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { public assignDateValues(change:WorkPackageChangeset, labels:WorkPackageCellLabels, dates:any):void { - this.assignDate(change, 'date', dates.date); this.updateLabels(true, labels, change); } @@ -78,7 +77,6 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { dayUnderCursor:Moment, delta:number, direction:'left' | 'right' | 'both' | 'create' | 'dragright') { - const initialDate = change.pristineResource.date; const dates:CellDateMovement = {}; @@ -94,7 +92,6 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { renderInfo:RenderInfo, labels:WorkPackageCellLabels, elem:HTMLElement):'left' | 'right' | 'both' | 'create' | 'dragright' { - // check for active selection mode if (renderInfo.viewParams.activeSelectionMode) { renderInfo.viewParams.activeSelectionMode(renderInfo.workPackage); @@ -117,7 +114,7 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { } public update(element:HTMLDivElement, labels:WorkPackageCellLabels|null, renderInfo:RenderInfo):boolean { - const viewParams = renderInfo.viewParams; + const { viewParams } = renderInfo; const date = moment(renderInfo.change.projectedResource.date); // abort if no date @@ -127,16 +124,16 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { const diamond = jQuery('.diamond', element)[0]; - diamond.style.width = 15 + 'px'; - diamond.style.height = 15 + 'px'; - diamond.style.width = 15 + 'px'; - diamond.style.height = 15 + 'px'; - diamond.style.marginLeft = -(15 / 2) + (renderInfo.viewParams.pixelPerDay / 2) + 'px'; + diamond.style.width = `${15}px`; + diamond.style.height = `${15}px`; + diamond.style.width = `${15}px`; + diamond.style.height = `${15}px`; + diamond.style.marginLeft = `${-(15 / 2) + (renderInfo.viewParams.pixelPerDay / 2)}px`; this.applyTypeColor(renderInfo, diamond); // offset left const offsetStart = date.diff(viewParams.dateDisplayStart, 'days'); - element.style.left = calculatePositionValueForDayCountingPx(viewParams, offsetStart) + 'px'; + element.style.left = `${calculatePositionValueForDayCountingPx(viewParams, offsetStart)}px`; // Update labels if any if (labels) { @@ -149,7 +146,7 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { } getMarginLeftOfLeftSide(renderInfo:RenderInfo):number { - const change = renderInfo.change; + const { change } = renderInfo; const start = moment(change.projectedResource.date); const offsetStart = start.diff(renderInfo.viewParams.dateDisplayStart, 'days'); return calculatePositionValueForDayCountingPx(renderInfo.viewParams, offsetStart); @@ -173,7 +170,7 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { */ public render(renderInfo:RenderInfo):HTMLDivElement { const element = document.createElement('div'); - element.className = timelineElementCssClass + ' ' + this.type; + element.className = `${timelineElementCssClass} ${this.type}`; const diamond = document.createElement('div'); diamond.className = 'diamond'; @@ -231,7 +228,6 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { protected updateLabels(activeDragNDrop:boolean, labels:WorkPackageCellLabels, change:WorkPackageChangeset) { - const labelConfiguration = this.wpTableTimeline.getNormalizedLabels(change.projectedResource); if (!activeDragNDrop) { @@ -268,5 +264,4 @@ export class TimelineMilestoneCellRenderer extends TimelineCellRenderer { super.renderLabel(change, labels, position, attribute); } - } diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell-mouse-handler.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell-mouse-handler.ts index 51844cc2af..631f7a5d57 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell-mouse-handler.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell-mouse-handler.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,29 +28,28 @@ import { Injector } from '@angular/core'; import * as moment from 'moment'; -import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; -import { RenderInfo } from '../wp-timeline'; -import { TimelineCellRenderer } from './timeline-cell-renderer'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; + +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { take } from 'rxjs/operators'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { WorkPackageCellLabels } from './wp-timeline-cell'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; - -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; +import { TimelineCellRenderer } from './timeline-cell-renderer'; +import { RenderInfo } from '../wp-timeline'; +import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import Moment = moment.Moment; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { take } from "rxjs/operators"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; export const classNameBar = 'bar'; export const classNameLeftHandle = 'leftHandle'; export const classNameRightHandle = 'rightHandle'; export const classNameBarLabel = 'bar-label'; - export function registerWorkPackageMouseHandler(this:void, injector:Injector, getRenderInfo:() => RenderInfo, @@ -64,11 +63,10 @@ export function registerWorkPackageMouseHandler(this:void, labels:WorkPackageCellLabels, renderer:TimelineCellRenderer, renderInfo:RenderInfo) { - const querySpace:IsolatedQuerySpace = injector.get(IsolatedQuerySpace); let mouseDownStartDay:number|null = null; // also flag to signal active drag'n'drop - renderInfo.change = halEditing.changeFor(renderInfo.workPackage) as WorkPackageChangeset; + renderInfo.change = halEditing.changeFor(renderInfo.workPackage); let dateStates:any; let placeholderForEmptyCell:HTMLElement; @@ -137,7 +135,7 @@ export function registerWorkPackageMouseHandler(this:void, function keyPressFn(ev:JQuery.TriggeredEvent) { const kev:KeyboardEvent = ev as any; - if (kev.keyCode === keyCodes.ESCAPE) { + if (kev.keyCode === KeyCodes.ESCAPE) { deactivate(true); } } @@ -233,7 +231,7 @@ export function registerWorkPackageMouseHandler(this:void, // Persist the changes saveWorkPackage(renderInfo.change) .then(stopAndRefresh) - .catch(error => { + .catch((error) => { notificationService.handleRawError(error, renderInfo.workPackage); cancelChange(); }); @@ -258,7 +256,7 @@ export function registerWorkPackageMouseHandler(this:void, .save(change) .then((result) => { notificationService.showSave(result.resource); - const ids = _.map(querySpace.tableRendered.value!, row => row.workPackageId); + const ids = _.map(querySpace.tableRendered.value, (row) => row.workPackageId); return apiv3Service .work_packages .filterUpdatedSince(ids, updatedAt) @@ -271,4 +269,3 @@ export function registerWorkPackageMouseHandler(this:void, }); } } - diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell.ts index 8166524ee1..1e83e6e210 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cell.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -25,21 +25,21 @@ // // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; -import { RenderInfo } from '../wp-timeline'; -import { TimelineCellRenderer } from './timeline-cell-renderer'; -import { TimelineMilestoneCellRenderer } from './timeline-milestone-cell-renderer'; -import { registerWorkPackageMouseHandler } from './wp-timeline-cell-mouse-handler'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; import { Injector } from '@angular/core'; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { registerWorkPackageMouseHandler } from './wp-timeline-cell-mouse-handler'; +import { TimelineMilestoneCellRenderer } from './timeline-milestone-cell-renderer'; +import { TimelineCellRenderer } from './timeline-cell-renderer'; +import { RenderInfo } from '../wp-timeline'; +import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; export const classNameLeftLabel = 'labelLeft'; export const classNameRightContainer = 'containerRight'; @@ -52,24 +52,27 @@ export const classNameShowOnHover = 'show-on-hover'; export const classNameHideOnHover = 'hide-on-hover'; export class WorkPackageCellLabels { - constructor(public readonly center:HTMLDivElement|null, - public readonly left:HTMLDivElement, - public readonly leftHover:HTMLDivElement|null, - public readonly right:HTMLDivElement, - public readonly rightHover:HTMLDivElement|null, - public readonly farRight:HTMLDivElement, - public readonly withAlternativeLabels?:boolean) { + public readonly left:HTMLDivElement, + public readonly leftHover:HTMLDivElement|null, + public readonly right:HTMLDivElement, + public readonly rightHover:HTMLDivElement|null, + public readonly farRight:HTMLDivElement, + public readonly withAlternativeLabels?:boolean) { } - } export class WorkPackageTimelineCell { @InjectField() halEditing:HalResourceEditingService; + @InjectField() halEvents:HalEventsService; + @InjectField() notificationService:WorkPackageNotificationService; + @InjectField() states:States; + @InjectField() loadingIndicator:LoadingIndicatorService; + @InjectField() schemaCache:SchemaCacheService; private wpElement:HTMLDivElement|null = null; @@ -79,11 +82,11 @@ export class WorkPackageTimelineCell { private labels:WorkPackageCellLabels; constructor(public readonly injector:Injector, - public workPackageTimeline:WorkPackageTimelineTableController, - public renderers:{ milestone:TimelineMilestoneCellRenderer, generic:TimelineCellRenderer }, - public latestRenderInfo:RenderInfo, - public classIdentifier:string, - public workPackageId:string) { + public workPackageTimeline:WorkPackageTimelineTableController, + public renderers:{ milestone:TimelineMilestoneCellRenderer, generic:TimelineCellRenderer }, + public latestRenderInfo:RenderInfo, + public classIdentifier:string, + public workPackageId:string) { } getMarginLeftOfLeftSide():number { @@ -172,7 +175,8 @@ export class WorkPackageTimelineCell { this.wpElement, this.labels, renderer, - renderInfo); + renderInfo, + ); } return Promise.resolve(); @@ -198,7 +202,8 @@ export class WorkPackageTimelineCell { const shouldBeDisplayed = renderer.update( this.wpElement as HTMLDivElement, this.labels, - renderInfo); + renderInfo, + ); if (!shouldBeDisplayed) { this.clear(); @@ -206,5 +211,4 @@ export class WorkPackageTimelineCell { }) .catch(() => null); } - } diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cells-renderer.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cells-renderer.ts index 9d79f96757..5f36844840 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cells-renderer.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cells-renderer.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,21 +27,19 @@ //++ import { Injector } from '@angular/core'; -import { States } from "core-app/core/states/states.service"; +import { States } from 'core-app/core/states/states.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import { RenderInfo } from '../wp-timeline'; import { TimelineCellRenderer } from './timeline-cell-renderer'; import { TimelineMilestoneCellRenderer } from './timeline-milestone-cell-renderer'; import { WorkPackageTimelineCell } from './wp-timeline-cell'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; - export class WorkPackageTimelineCellsRenderer { - // Injections @InjectField() public states:States; + @InjectField() public halEditing:HalResourceEditingService; public cells:{ [classIdentifier:string]:WorkPackageTimelineCell } = {}; @@ -49,10 +47,10 @@ export class WorkPackageTimelineCellsRenderer { private cellRenderers:{ milestone:TimelineMilestoneCellRenderer, generic:TimelineCellRenderer }; constructor(readonly injector:Injector, - readonly wpTimeline:WorkPackageTimelineTableController) { + readonly wpTimeline:WorkPackageTimelineTableController) { this.cellRenderers = { milestone: new TimelineMilestoneCellRenderer(this.injector, wpTimeline), - generic: new TimelineCellRenderer(this.injector, wpTimeline) + generic: new TimelineCellRenderer(this.injector, wpTimeline), }; } @@ -135,7 +133,7 @@ export class WorkPackageTimelineCellsRenderer { this.cellRenderers, this.renderInfoFor(workPackageId), classIdentifier, - workPackageId + workPackageId, ); } @@ -144,18 +142,17 @@ export class WorkPackageTimelineCellsRenderer { return { viewParams: this.wpTimeline.viewParameters, workPackage: wp, - change: this.halEditing.changeFor(wp) as WorkPackageChangeset, + change: this.halEditing.changeFor(wp), isDuplicatedCell, withAlternativeLabels, }; } public buildCellsAndRenderOnRow(workPackageIds:string[], rowClassIdentifier:string, isDuplicatedCell?:boolean):WorkPackageTimelineCell[] { - const cells = workPackageIds.map(workPackageId => this.buildCell(rowClassIdentifier, workPackageId!)); + const cells = workPackageIds.map((workPackageId) => this.buildCell(rowClassIdentifier, workPackageId)); cells.forEach((cell:WorkPackageTimelineCell) => this.refreshSingleCell(cell, isDuplicatedCell, true)); return cells; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts index 9ea893e9d8..5c0d4e66ff 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,23 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, Component, ElementRef, Injector } from '@angular/core'; +import { + AfterViewInit, Component, ElementRef, Injector, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { INotification, NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import * as moment from 'moment'; import { Moment } from 'moment'; import { filter, takeUntil } from 'rxjs/operators'; -import { - calculateDaySpan, - getPixelPerDayForZoomLevel, - requiredPixelMarginLeft, - timelineElementCssClass, - timelineHeaderSelector, - timelineMarkerSelectionStartClass, - TimelineViewParameters, - zoomLevelOrder -} from '../wp-timeline'; import { input, InputState } from 'reactivestates'; import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; import { WorkPackageTimelineCellsRenderer } from 'core-app/features/work-packages/components/wp-table/timeline/cells/wp-timeline-cells-renderer'; @@ -59,16 +51,25 @@ import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destr import { WorkPackagesTableComponent } from 'core-app/features/work-packages/components/wp-table/wp-table.component'; import { groupIdFromIdentifier, - groupTypeFromIdentifier + groupTypeFromIdentifier, } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/grouped-rows-helpers'; import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; - +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { + calculateDaySpan, + getPixelPerDayForZoomLevel, + requiredPixelMarginLeft, + timelineElementCssClass, + timelineHeaderSelector, + timelineMarkerSelectionStartClass, + TimelineViewParameters, + zoomLevelOrder, +} from '../wp-timeline'; @Component({ selector: 'wp-timeline-container', - templateUrl: './wp-timeline-container.html' + templateUrl: './wp-timeline-container.html', }) export class WorkPackageTimelineTableController extends UntilDestroyedMixin implements AfterViewInit { private $element:JQuery; @@ -90,7 +91,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl public timelineBody:JQuery; private selectionParams:{ notification:INotification|null } = { - notification: null + notification: null, }; private text:{ selectionMode:string }; @@ -102,38 +103,36 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl private orderedRows:RenderedWorkPackage[] = []; get commonPipes() { - return (source:Observable) => { - return source.pipe( - this.untilDestroyed(), - takeUntil(this.querySpace.stopAllSubscriptions), - filter(() => this.initialized && this.wpTableTimeline.isVisible), - ); - }; + return (source:Observable) => source.pipe( + this.untilDestroyed(), + takeUntil(this.querySpace.stopAllSubscriptions), + filter(() => this.initialized && this.wpTableTimeline.isVisible), + ); } get workPackagesWithGroupHeaderCell():RenderedWorkPackage[] { const tableWorkPackages = this.querySpace.results.value!.elements; const wpsWithGroupHeaderCell = tableWorkPackages - .filter(tableWorkPackage => this.shouldBeShownInCollapsedGroupHeaders(tableWorkPackage)) - .map(tableWorkPackage => tableWorkPackage.id); - const workPackagesWithGroupHeaderCell = this.orderedRows.filter(row => wpsWithGroupHeaderCell.includes(row.workPackageId!) && !this.workPackageIdOrder.includes(row)); + .filter((tableWorkPackage) => this.shouldBeShownInCollapsedGroupHeaders(tableWorkPackage)) + .map((tableWorkPackage) => tableWorkPackage.id); + const workPackagesWithGroupHeaderCell = this.orderedRows.filter((row) => wpsWithGroupHeaderCell.includes(row.workPackageId) && !this.workPackageIdOrder.includes(row)); return workPackagesWithGroupHeaderCell; } constructor(public readonly injector:Injector, - private elementRef:ElementRef, - private states:States, - public wpTableComponent:WorkPackagesTableComponent, - private NotificationsService:NotificationsService, - private wpTableTimeline:WorkPackageViewTimelineService, - private notificationService:WorkPackageNotificationService, - private wpRelations:WorkPackageRelationsService, - private wpTableHierarchies:WorkPackageViewHierarchiesService, - private halEvents:HalEventsService, - private querySpace:IsolatedQuerySpace, - readonly I18n:I18nService, - private workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService) { + private elementRef:ElementRef, + private states:States, + public wpTableComponent:WorkPackagesTableComponent, + private NotificationsService:NotificationsService, + private wpTableTimeline:WorkPackageViewTimelineService, + private notificationService:WorkPackageNotificationService, + private wpRelations:WorkPackageRelationsService, + private wpTableHierarchies:WorkPackageViewHierarchiesService, + private halEvents:HalEventsService, + private querySpace:IsolatedQuerySpace, + readonly I18n:I18nService, + private workPackageViewCollapsedGroupsService:WorkPackageViewCollapsedGroupsService) { super(); } @@ -141,7 +140,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl this.$element = jQuery(this.elementRef.nativeElement); this.text = { - selectionMode: this.I18n.t('js.timelines.selection_mode.notification') + selectionMode: this.I18n.t('js.timelines.selection_mode.notification'), }; // Get the outer container for width computation @@ -157,7 +156,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl combineLatest([ this.querySpace.tableRendered.values$(), this.refreshRequest.changes$(), - this.wpTableTimeline.live$() + this.wpTableTimeline.live$(), ]).pipe( this.commonPipes, ) @@ -250,14 +249,14 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } startAddRelationPredecessor(start:WorkPackageResource) { - this.activateSelectionMode(start.id!, end => { + this.activateSelectionMode(start.id!, (end) => { this.wpRelations .addCommonRelation(start.id!, 'follows', end.id!) .then(() => { this.halEvents.push(start, { eventType: 'association', relatedWorkPackage: end.id!, - relationType: 'follows' + relationType: 'follows', }); }) .catch((error:any) => this.notificationService.handleRawError(error, end)); @@ -265,14 +264,14 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } startAddRelationFollower(start:WorkPackageResource) { - this.activateSelectionMode(start.id!, end => { + this.activateSelectionMode(start.id!, (end) => { this.wpRelations .addCommonRelation(start.id!, 'precedes', end.id!) .then(() => { this.halEvents.push(start, { eventType: 'association', relatedWorkPackage: end.id!, - relationType: 'precedes' + relationType: 'precedes', }); }) .catch((error:any) => this.notificationService.handleRawError(error, end)); @@ -281,14 +280,14 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl getFirstDayInViewport() { const outerContainer = this.getParentScrollContainer(); - const scrollLeft = outerContainer.scrollLeft; + const { scrollLeft } = outerContainer; const nonVisibleDaysLeft = Math.floor(scrollLeft / this.viewParameters.pixelPerDay); return this.viewParameters.dateDisplayStart.clone().add(nonVisibleDaysLeft, 'days'); } getLastDayInViewport() { const outerContainer = this.getParentScrollContainer(); - const scrollLeft = outerContainer.scrollLeft; + const { scrollLeft } = outerContainer; const width = outerContainer.offsetWidth; const viewPortRight = scrollLeft + width; const daysUntilViewPortEnds = Math.ceil(viewPortRight / this.viewParameters.pixelPerDay) + 1; @@ -296,7 +295,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } forceCursor(cursor:string) { - jQuery('.' + timelineElementCssClass).css('cursor', cursor); + jQuery(`.${timelineElementCssClass}`).css('cursor', cursor); jQuery('.wp-timeline-cell').css('cursor', cursor); jQuery('.hascontextmenu').css('cursor', cursor); jQuery('.leftHandle').css('cursor', cursor); @@ -304,7 +303,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } resetCursor() { - jQuery('.' + timelineElementCssClass).css('cursor', ''); + jQuery(`.${timelineElementCssClass}`).css('cursor', ''); jQuery('.wp-timeline-cell').css('cursor', ''); jQuery('.hascontextmenu').css('cursor', ''); jQuery('.leftHandle').css('cursor', ''); @@ -322,7 +321,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl Mousetrap.unbind('esc'); this.$element.removeClass('active-selection-mode'); - jQuery('.' + timelineMarkerSelectionStartClass).removeClass(timelineMarkerSelectionStartClass); + jQuery(`.${timelineMarkerSelectionStartClass}`).removeClass(timelineMarkerSelectionStartClass); this.refreshView(); } @@ -375,14 +374,16 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl newParams.dateDisplayStart, currentParams.now, startDate, - date); + date, + ); // finish date newParams.dateDisplayEnd = moment.max( newParams.dateDisplayEnd, currentParams.now, dueDate, - date); + date, + ); }); // left spacing @@ -395,7 +396,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl const width = this.$element.children().width()!; // A // const width = jQuery('body').width(); // B - const pixelPerDay = currentParams.pixelPerDay; + const { pixelPerDay } = currentParams; const visibleDays = Math.ceil((width / pixelPerDay) * 1.5); newParams.dateDisplayEnd = newParams.dateDisplayEnd.add(visibleDays, 'days'); @@ -427,7 +428,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl return; } - const workPackagesToCalculateWidthFrom = this.getWorkPackagesToCalculateTimelineWidthFrom(); + const workPackagesToCalculateWidthFrom = this.getWorkPackagesToCalculateTimelineWidthFrom(); const daysSpan = calculateDaySpan(workPackagesToCalculateWidthFrom, this.states.workPackages, this._viewParameters); const timelineWidthInPx = this.$element.parent().width()! - (2 * requiredPixelMarginLeft); @@ -459,8 +460,8 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl .subscribe((groupsCollapseEvent:IGroupsCollapseEvent) => { this.manageCollapsedGroupHeaderCells( groupsCollapseEvent, - this.querySpace.results.value!.elements, - this.collapsedGroupsCellsMap, + this.querySpace.results.value!.elements, + this.collapsedGroupsCellsMap, ); }); } @@ -478,7 +479,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } if (refreshAllGroupHeaderCells) { - groupsToUpdate = collapsedGroupsChangeArray.filter(groupIdentifier => this.shouldManageCollapsedGroupHeaderCells(groupIdentifier, groupsCollapseConfig)); + groupsToUpdate = collapsedGroupsChangeArray.filter((groupIdentifier) => this.shouldManageCollapsedGroupHeaderCells(groupIdentifier, groupsCollapseConfig)); } else { const groupIdentifier = groupsCollapseConfig.lastChangedGroup!; if (this.shouldManageCollapsedGroupHeaderCells(groupIdentifier, groupsCollapseConfig)) { @@ -486,7 +487,7 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl } } - groupsToUpdate.forEach(groupIdentifier => { + groupsToUpdate.forEach((groupIdentifier) => { const groupIsCollapsed = collapsedGroupsChange[groupIdentifier]; if (groupIsCollapsed) { @@ -500,8 +501,8 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl shouldManageCollapsedGroupHeaderCells(groupIdentifier:string, groupsCollapseConfig:IGroupsCollapseEvent) { const keyGroupType = groupTypeFromIdentifier(groupIdentifier); - return this.workPackageViewCollapsedGroupsService.groupTypesWithHeaderCellsWhenCollapsed.includes(keyGroupType) && - this.workPackageViewCollapsedGroupsService.groupTypesWithHeaderCellsWhenCollapsed.includes(groupsCollapseConfig.groupedBy!); + return this.workPackageViewCollapsedGroupsService.groupTypesWithHeaderCellsWhenCollapsed.includes(keyGroupType) + && this.workPackageViewCollapsedGroupsService.groupTypesWithHeaderCellsWhenCollapsed.includes(groupsCollapseConfig.groupedBy!); } createCollapsedGroupHeaderCells(groupIdentifier:string, tableWorkPackages:WorkPackageResource[], collapsedGroupsCellsMap:IGroupCellsMap) { @@ -509,33 +510,33 @@ export class WorkPackageTimelineTableController extends UntilDestroyedMixin impl const changedGroupId = groupIdFromIdentifier(groupIdentifier); const changedGroupType = groupTypeFromIdentifier(groupIdentifier); - const changedGroupTableWorkPackages = tableWorkPackages.filter(tableWorkPackage => tableWorkPackage[changedGroupType].id === changedGroupId); - const changedGroupWpsWithHeaderCells = changedGroupTableWorkPackages.filter(tableWorkPackage => this.shouldBeShownInCollapsedGroupHeaders(tableWorkPackage) && - (tableWorkPackage.date || tableWorkPackage.startDate)); - const changedGroupWpsWithHeaderCellsIds = changedGroupWpsWithHeaderCells.map(workPackage => workPackage.id!); + const changedGroupTableWorkPackages = tableWorkPackages.filter((tableWorkPackage) => tableWorkPackage[changedGroupType].id === changedGroupId); + const changedGroupWpsWithHeaderCells = changedGroupTableWorkPackages.filter((tableWorkPackage) => this.shouldBeShownInCollapsedGroupHeaders(tableWorkPackage) + && (tableWorkPackage.date || tableWorkPackage.startDate)); + const changedGroupWpsWithHeaderCellsIds = changedGroupWpsWithHeaderCells.map((workPackage) => workPackage.id!); - this.collapsedGroupsCellsMap[groupIdentifier!] = this.cellsRenderer.buildCellsAndRenderOnRow(changedGroupWpsWithHeaderCellsIds, `group-${groupIdentifier}-timeline`, true); + this.collapsedGroupsCellsMap[groupIdentifier] = this.cellsRenderer.buildCellsAndRenderOnRow(changedGroupWpsWithHeaderCellsIds, `group-${groupIdentifier}-timeline`, true); } removeCollapsedGroupHeaderCells(groupIdentifier:string, collapsedGroupsCellsMap:IGroupCellsMap) { - if (collapsedGroupsCellsMap[groupIdentifier!]) { - collapsedGroupsCellsMap[groupIdentifier!].forEach((cell:WorkPackageTimelineCell) => cell.clear()); - collapsedGroupsCellsMap[groupIdentifier!] = []; + if (collapsedGroupsCellsMap[groupIdentifier]) { + collapsedGroupsCellsMap[groupIdentifier].forEach((cell:WorkPackageTimelineCell) => cell.clear()); + collapsedGroupsCellsMap[groupIdentifier] = []; } } refreshCollapsedGroupsHeaderCells(collapsedGroupsCellsMap:IGroupCellsMap, cellsRenderer:WorkPackageTimelineCellsRenderer) { - Object.keys(collapsedGroupsCellsMap).forEach(collapsedGroupKey => { + Object.keys(collapsedGroupsCellsMap).forEach((collapsedGroupKey) => { const collapsedGroupCells = collapsedGroupsCellsMap[collapsedGroupKey]; - collapsedGroupCells.forEach(cell => cellsRenderer.refreshSingleCell(cell, false, true)); + collapsedGroupCells.forEach((cell) => cellsRenderer.refreshSingleCell(cell, false, true)); }); } shouldBeShownInCollapsedGroupHeaders(workPackage:WorkPackageResource) { return this.workPackageViewCollapsedGroupsService .wpTypesToShowInCollapsedGroupHeaders - .some(wpTypeFunction => wpTypeFunction(workPackage)); + .some((wpTypeFunction) => wpTypeFunction(workPackage)); } getWorkPackagesToCalculateTimelineWidthFrom() { diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-relation-element.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-relation-element.ts index 11cdbe1396..5e20a30197 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-relation-element.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-relation-element.ts @@ -1,19 +1,17 @@ -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; export function workPackagePrefix(workPackageId:string) { return `__tl-relation-${workPackageId}`; } export class TimelineRelationElement { - constructor(public belongsToId:string, public relation:RelationResource) { } public get classNames():string[] { return [ workPackagePrefix(this.relation.ids.from), - workPackagePrefix(this.relation.ids.to) + workPackagePrefix(this.relation.ids.to), ]; } - } diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-static-element.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-static-element.ts index 4d61f2ce88..88ac46f014 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-static-element.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/timeline-static-element.ts @@ -1,5 +1,6 @@ -import { TimelineViewParameters } from "../wp-timeline"; -export const timelineStaticElementCssClassname = "wp-timeline--static-element"; +import { TimelineViewParameters } from '../wp-timeline'; + +export const timelineStaticElementCssClassname = 'wp-timeline--static-element'; export abstract class TimelineStaticElement { constructor() { @@ -11,7 +12,7 @@ export abstract class TimelineStaticElement { * @returns {HTMLElement} The finished static element */ public render(vp:TimelineViewParameters):HTMLElement { - const elem = document.createElement("div"); + const elem = document.createElement('div'); elem.id = this.identifier; elem.classList.add(...this.classNames); diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts index 40c7cdabad..adb9b8e296 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, Injector, OnInit } from '@angular/core'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { + Component, ElementRef, Injector, OnInit, +} from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { State } from 'reactivestates'; import { combineLatest } from 'rxjs'; import { filter, map, take } from 'rxjs/operators'; -import { States } from "core-app/core/states/states.service"; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { RelationsStateValue, WorkPackageRelationsService } from '../../../wp-relations/wp-relations.service'; import { WorkPackageTimelineCell } from '../cells/wp-timeline-cell'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import { timelineElementCssClass, TimelineViewParameters } from '../wp-timeline'; import { TimelineRelationElement, workPackagePrefix } from './timeline-relation-element'; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; const DEBUG_DRAW_RELATION_LINES_WITH_COLOR = false; @@ -53,19 +55,18 @@ function newSegment(vp:TimelineViewParameters, width:number, height:number, color?:string):HTMLElement { - const segment = document.createElement('div'); segment.classList.add( timelineElementCssClass, timelineGlobalElementCssClassname, - ...classNames + ...classNames, ); // segment.style.backgroundColor = color; - segment.style.top = ((yPosition * 40) + top) + 'px'; - segment.style.left = left + 'px'; - segment.style.width = width + 'px'; - segment.style.height = height + 'px'; + segment.style.top = `${(yPosition * 40) + top}px`; + segment.style.left = `${left}px`; + segment.style.width = `${width}px`; + segment.style.height = `${height}px`; if (DEBUG_DRAW_RELATION_LINES_WITH_COLOR && color !== undefined) { segment.style.zIndex = '9999999'; @@ -76,10 +77,9 @@ function newSegment(vp:TimelineViewParameters, @Component({ selector: 'wp-timeline-relations', - template: '
    ' + template: '
    ', }) export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin implements OnInit { - @InjectField() querySpace:IsolatedQuerySpace; private container:JQuery; @@ -87,11 +87,11 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple private workPackagesWithRelations:{ [workPackageId:string]:State } = {}; constructor(public readonly injector:Injector, - public elementRef:ElementRef, - public states:States, - public workPackageTimelineTableController:WorkPackageTimelineTableController, - public wpTableTimeline:WorkPackageViewTimelineService, - public wpRelations:WorkPackageRelationsService) { + public elementRef:ElementRef, + public states:States, + public workPackageTimelineTableController:WorkPackageTimelineTableController, + public wpTableTimeline:WorkPackageViewTimelineService, + public wpRelations:WorkPackageRelationsService) { super(); } @@ -119,26 +119,26 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple // for all visible WorkPackage rows... combineLatest([ this.querySpace.renderedWorkPackages.values$(), - this.wpTableTimeline.live$() + this.wpTableTimeline.live$(), ]) .pipe( filter(([_, timeline]) => timeline.visible), this.untilDestroyed(), - map(([rendered, _]) => rendered) + map(([rendered, _]) => rendered), ) - .subscribe(list => { + .subscribe((list) => { // ... make sure that the corresponding relations are loaded ... - const wps = _.compact(list.map(row => row.workPackageId) as string[]); + const wps = _.compact(list.map((row) => row.workPackageId) as string[]); this.wpRelations.requireAll(wps); - wps.forEach(wpId => { + wps.forEach((wpId) => { const relationsForWorkPackage = this.wpRelations.state(wpId); this.workPackagesWithRelations[wpId] = relationsForWorkPackage; // ... once they are loaded, display them. relationsForWorkPackage.values$() .pipe( - take(1) + take(1), ) .subscribe(() => { this.renderWorkPackagesRelations([wpId]); @@ -150,26 +150,24 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple this.states.workPackages.observeChange() .pipe( this.untilDestroyed(), - filter(() => this.wpTableTimeline.isVisible) + filter(() => this.wpTableTimeline.isVisible), ) .subscribe(([workPackageId]) => { this.renderWorkPackagesRelations([workPackageId]); }); - } private renderWorkPackagesRelations(workPackageIds:string[]) { - workPackageIds.forEach(workPackageId => { + workPackageIds.forEach((workPackageId) => { const workPackageWithRelation = this.workPackagesWithRelations[workPackageId]; if (_.isNil(workPackageWithRelation)) { return; } this.removeRelationElementsForWorkPackage(workPackageId); - const relations = _.values(workPackageWithRelation.value!); + const relations = _.values(workPackageWithRelation.value); const relationsList = _.values(relations); - relationsList.forEach(relation => { - + relationsList.forEach((relation) => { if (!(relation.type === 'precedes' || relation.type === 'follows')) { return; @@ -178,7 +176,6 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple const elem = new TimelineRelationElement(relation.ids.from, relation); this.renderElement(this.workPackageTimelineTableController.viewParameters, elem); }); - }); } @@ -189,18 +186,17 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple private removeRelationElementsForWorkPackage(workPackageId:string) { const className = workPackagePrefix(workPackageId); - const found = this.container.find('.' + className); + const found = this.container.find(`.${className}`); found.remove(); } private removeAllVisibleElements() { - this.container.find('.' + timelineGlobalElementCssClassname).remove(); + this.container.find(`.${timelineGlobalElementCssClassname}`).remove(); } private renderElements() { const wpIdsWithRelations:string[] = _.keys(this.workPackagesWithRelations); this.renderWorkPackagesRelations(wpIdsWithRelations); - } /** @@ -236,7 +232,6 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple idxTo:number, startCell:WorkPackageTimelineCell, endCell:WorkPackageTimelineCell) { - const rowFrom = this.workPackageIdOrder[idxFrom]; const rowTo = this.workPackageIdOrder[idxTo]; @@ -259,8 +254,7 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple const directionY:'toUp'|'toDown' = idxFrom < idxTo ? 'toDown' : 'toUp'; // Horizontal direction - const directionX:'toLeft'|'beneath'|'toRight' = - targetX > startX ? 'toRight' : targetX < startX ? 'toLeft' : 'beneath'; + const directionX:'toLeft'|'beneath'|'toRight' = targetX > startX ? 'toRight' : targetX < startX ? 'toLeft' : 'beneath'; // start if (!startCell) { @@ -305,7 +299,5 @@ export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin imple this.container.append(newSegment(vp, e.classNames, idxTo, 19, targetX + 1, 1, 11, 'blue')); } } - } } - diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts index 5d3f9224cf..41c565d7e1 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ import { Component, ElementRef, OnInit } from '@angular/core'; -import { States } from "core-app/core/states/states.service"; +import { States } from 'core-app/core/states/states.service'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import { TimelineViewParameters } from '../wp-timeline'; import { TimelineStaticElement, timelineStaticElementCssClassname } from './timeline-static-element'; @@ -34,10 +34,9 @@ import { TodayLineElement } from './wp-timeline.today-line'; @Component({ selector: 'wp-timeline-static-elements', - template: '
    ' + template: '
    ', }) export class WorkPackageTableTimelineStaticElements implements OnInit { - public $element:JQuery; private container:JQuery; @@ -45,13 +44,12 @@ export class WorkPackageTableTimelineStaticElements implements OnInit { private elements:TimelineStaticElement[]; constructor(elementRef:ElementRef, - public states:States, - public workPackageTimelineTableController:WorkPackageTimelineTableController) { - + public states:States, + public workPackageTimelineTableController:WorkPackageTimelineTableController) { this.$element = jQuery(elementRef.nativeElement); this.elements = [ - new TodayLineElement() + new TodayLineElement(), ]; } @@ -67,7 +65,7 @@ export class WorkPackageTableTimelineStaticElements implements OnInit { } private removeAllVisibleElements() { - jQuery('.' + timelineStaticElementCssClassname).remove(); + jQuery(`.${timelineStaticElementCssClassname}`).remove(); } private renderElements(vp:TimelineViewParameters) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts index e7772f34a1..f685967775 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline.today-line.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,9 +30,7 @@ import * as moment from 'moment'; import { calculatePositionValueForDayCount, TimelineViewParameters } from '../wp-timeline'; import { TimelineStaticElement } from './timeline-static-element'; - export class TodayLineElement extends TimelineStaticElement { - protected finishElement(elem:HTMLElement, vp:TimelineViewParameters):HTMLElement { const offsetToday = vp.now.diff(vp.dateDisplayStart, 'days'); const dayProgress = moment().hour() / 24; @@ -45,4 +43,3 @@ export class TodayLineElement extends TimelineStaticElement { return 'wp-timeline-static-element-today-line'; } } - diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts index d4ae8c08d9..d742e4345f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,16 +27,16 @@ //++ import { AfterViewInit, Component, ElementRef } from '@angular/core'; import * as moment from 'moment'; +import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; import { WorkPackageTimelineTableController } from '../container/wp-timeline-container.directive'; import { calculatePositionValueForDayCount, getTimeSlicesForHeader, timelineElementCssClass, timelineGridElementCssClass, - TimelineViewParameters + TimelineViewParameters, } from '../wp-timeline'; import Moment = moment.Moment; -import { TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; function checkForWeekendHighlight(date:Moment, cell:HTMLElement) { const day = date.day(); @@ -50,16 +50,15 @@ function checkForWeekendHighlight(date:Moment, cell:HTMLElement) { @Component({ selector: 'wp-timeline-grid', - template: '
    ' + template: '
    ', }) export class WorkPackageTableTimelineGrid implements AfterViewInit { - private activeZoomLevel:TimelineZoomLevel; private gridContainer:JQuery; constructor(private elementRef:ElementRef, - public wpTimeline:WorkPackageTimelineTableController) { + public wpTimeline:WorkPackageTimelineTableController) { } ngAfterViewInit() { @@ -80,16 +79,16 @@ export class WorkPackageTableTimelineGrid implements AfterViewInit { this.gridContainer.empty(); switch (vp.settings.zoomLevel) { - case 'days': - return this.renderLabelsDays(vp); - case 'weeks': - return this.renderLabelsWeeks(vp); - case 'months': - return this.renderLabelsMonths(vp); - case 'quarters': - return this.renderLabelsQuarters(vp); - case 'years': - return this.renderLabelsYears(vp); + case 'days': + return this.renderLabelsDays(vp); + case 'weeks': + return this.renderLabelsWeeks(vp); + case 'months': + return this.renderLabelsMonths(vp); + case 'quarters': + return this.renderLabelsQuarters(vp); + case 'years': + return this.renderLabelsYears(vp); } this.activeZoomLevel = vp.settings.zoomLevel; @@ -164,7 +163,6 @@ export class WorkPackageTableTimelineGrid implements AfterViewInit { startView:Moment, endView:Moment, cellCallback:(start:Moment, cell:HTMLElement) => void) { - const { inViewportAndBoundaries, rest } = getTimeSlicesForHeader(vp, unit, startView, endView); for (const [start, end] of inViewportAndBoundaries) { diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts index a839710109..c120b5e4b4 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,24 +29,23 @@ import { Component, ElementRef, OnInit } from '@angular/core'; import { WorkPackageTimelineTableController } from 'core-app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive'; import * as moment from 'moment'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; import { calculatePositionValueForDayCount, getTimeSlicesForHeader, timelineHeaderCSSClass, timelineHeaderSelector, - TimelineViewParameters + TimelineViewParameters, } from '../wp-timeline'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; import Moment = moment.Moment; -import { TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; @Component({ selector: timelineHeaderSelector, - templateUrl: './wp-timeline-header.html' + templateUrl: './wp-timeline-header.html', }) export class WorkPackageTimelineHeaderController implements OnInit { - public $element:JQuery; private activeZoomLevel:TimelineZoomLevel; @@ -54,10 +53,9 @@ export class WorkPackageTimelineHeaderController implements OnInit { private innerHeader:JQuery; constructor(elementRef:ElementRef, - readonly I18n:I18nService, - readonly wpTimelineService:WorkPackageViewTimelineService, - readonly workPackageTimelineTableController:WorkPackageTimelineTableController) { - + readonly I18n:I18nService, + readonly wpTimelineService:WorkPackageViewTimelineService, + readonly workPackageTimelineTableController:WorkPackageTimelineTableController) { this.$element = jQuery(elementRef.nativeElement); } @@ -80,16 +78,16 @@ export class WorkPackageTimelineHeaderController implements OnInit { this.innerHeader.attr('data-current-zoom-level', this.wpTimelineService.zoomLevel); switch (vp.settings.zoomLevel) { - case 'days': - return this.renderLabelsDays(vp); - case 'weeks': - return this.renderLabelsWeeks(vp); - case 'months': - return this.renderLabelsMonths(vp); - case 'quarters': - return this.renderLabelsQuarters(vp); - case 'years': - return this.renderLabelsYears(vp); + case 'days': + return this.renderLabelsDays(vp); + case 'weeks': + return this.renderLabelsWeeks(vp); + case 'months': + return this.renderLabelsMonths(vp); + case 'quarters': + return this.renderLabelsQuarters(vp); + case 'years': + return this.renderLabelsYears(vp); } this.activeZoomLevel = vp.settings.zoomLevel; @@ -179,7 +177,6 @@ export class WorkPackageTimelineHeaderController implements OnInit { this.renderTimeSlices(vp, 'year', 0, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { cell.innerHTML = start.format('YYYY'); cell.classList.add('wp-timeline--header-top-bold-element'); - }); this.renderTimeSlices(vp, 'quarter', 15, vp.dateDisplayStart, vp.dateDisplayEnd, (start, cell) => { @@ -201,12 +198,11 @@ export class WorkPackageTimelineHeaderController implements OnInit { startView:Moment, endView:Moment, cellCallback:(start:Moment, cell:HTMLElement) => void) { - const { inViewportAndBoundaries, rest } = getTimeSlicesForHeader(vp, unit, startView, endView); for (const [start, end] of inViewportAndBoundaries) { const cell = this.addLabelCell(); - cell.style.top = marginTop + 'px'; + cell.style.top = `${marginTop}px`; cell.style.left = calculatePositionValueForDayCount(vp, start.diff(startView, 'days')); cell.style.width = calculatePositionValueForDayCount(vp, end.diff(start, 'days') + 1); cellCallback(start, cell); @@ -214,7 +210,7 @@ export class WorkPackageTimelineHeaderController implements OnInit { setTimeout(() => { for (const [start, end] of rest) { const cell = this.addLabelCell(); - cell.style.top = marginTop + 'px'; + cell.style.top = `${marginTop}px`; cell.style.left = calculatePositionValueForDayCount(vp, start.diff(startView, 'days')); cell.style.width = calculatePositionValueForDayCount(vp, end.diff(start, 'days') + 1); cellCallback(start, cell); diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/wp-timeline.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/wp-timeline.ts index 63f18c937e..f2b508c3b0 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/wp-timeline.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/wp-timeline.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import * as moment from 'moment'; import { InputState, MultiInputState } from 'reactivestates'; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; import Moment = moment.Moment; -import { TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; export const timelineElementCssClass = 'timeline-element'; export const timelineBackgroundElementClass = 'timeline-element--bg'; @@ -43,30 +43,28 @@ export const timelineHeaderSelector = 'wp-timeline-header'; * */ export class TimelineViewParametersSettings { - zoomLevel:TimelineZoomLevel = 'days'; - } // Can't properly map the enum to a string aray export const zoomLevelOrder:TimelineZoomLevel[] = [ - 'days', 'weeks', 'months', 'quarters', 'years' + 'days', 'weeks', 'months', 'quarters', 'years', ]; export function getPixelPerDayForZoomLevel(zoomLevel:TimelineZoomLevel) { switch (zoomLevel) { - case 'days': - return 30; - case 'weeks': - return 15; - case 'months': - return 6; - case 'quarters': - return 2; - case 'years': - return 0.5; + case 'days': + return 30; + case 'weeks': + return 15; + case 'months': + return 6; + case 'quarters': + return 2; + case 'years': + return 0.5; } - throw new Error('invalid zoom level: ' + zoomLevel); + throw new Error(`invalid zoom level: ${zoomLevel}`); } /** @@ -78,7 +76,6 @@ export const requiredPixelMarginLeft = 120; * */ export class TimelineViewParameters { - readonly now:Moment = moment({ hour: 0, minute: 0, seconds: 0 }); dateDisplayStart:Moment = moment({ hour: 0, minute: 0, seconds: 0 }); @@ -111,7 +108,6 @@ export class TimelineViewParameters { get dayCountForMarginLeft():number { return Math.ceil(requiredPixelMarginLeft / this.pixelPerDay); } - } /** @@ -138,14 +134,13 @@ export function calculatePositionValueForDayCountingPx(viewParams:TimelineViewPa */ export function calculatePositionValueForDayCount(viewParams:TimelineViewParameters, days:number):string { const value = calculatePositionValueForDayCountingPx(viewParams, days); - return value + 'px'; + return `${value}px`; } export function getTimeSlicesForHeader(vp:TimelineViewParameters, unit:moment.unitOfTime.DurationConstructor, startView:Moment, endView:Moment) { - const inViewport:[Moment, Moment][] = []; const rest:[Moment, Moment][] = []; @@ -160,7 +155,6 @@ export function getTimeSlicesForHeader(vp:TimelineViewParameters, const viewport = vp.visibleViewportAtCalculationTime; if ((sliceStart.isSameOrAfter(viewport[0]) && sliceStart.isSameOrBefore(viewport[1])) || (sliceEnd.isSameOrAfter(viewport[0]) && sliceEnd.isSameOrBefore(viewport[1]))) { - inViewport.push([sliceStart, sliceEnd]); } else { rest.push([sliceStart, sliceEnd]); @@ -170,16 +164,15 @@ export function getTimeSlicesForHeader(vp:TimelineViewParameters, const firstRest:[Moment, Moment] = rest.splice(0, 1)[0]; const lastRest:[Moment, Moment] = rest.pop()!; const inViewportAndBoundaries = _.concat( - [firstRest].filter(e => !_.isNil(e)), + [firstRest].filter((e) => !_.isNil(e)), inViewport, - [lastRest].filter(e => !_.isNil(e)) + [lastRest].filter((e) => !_.isNil(e)), ); return { inViewportAndBoundaries, - rest + rest, }; - } export function calculateDaySpan(visibleWorkPackages:RenderedWorkPackage[], diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table-configuration.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table-configuration.ts index d7e0464a50..5351736cec 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table-configuration.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table-configuration.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,6 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - export type WorkPackageTableConfigurationObject = Partial<{ [field in keyof WorkPackageTableConfiguration]:string|boolean }>; export class WorkPackageTableConfiguration { @@ -39,10 +38,10 @@ export class WorkPackageTableConfiguration { /** Render the action column (last column) with the actions defined in the TableActionsService */ public actionsColumnEnabled = true; - /** Whether the work package context menu is enabled*/ + /** Whether the work package context menu is enabled */ public contextMenuEnabled = true; - /** Whether the column dropdown menu is enabled*/ + /** Whether the column dropdown menu is enabled */ public columnMenuEnabled = true; /** Whether the query should be resolved using the current project identifier */ @@ -51,7 +50,7 @@ export class WorkPackageTableConfiguration { /** Whether the embedded table should live within a specific project context (e.g., given by its parent) */ public projectIdentifier:string|null = null; - /** Whether inline create is enabled*/ + /** Whether inline create is enabled */ public inlineCreateEnabled = true; /** Whether the hierarchy toggler item in the subject column is enabled */ @@ -60,23 +59,23 @@ export class WorkPackageTableConfiguration { /** Whether this table supports drag and drop */ public dragAndDropEnabled = false; - /** Whether this table is in an embedded context*/ + /** Whether this table is in an embedded context */ public isEmbedded = false; /** Whether the work packages shall be shown in cards instead of a table */ public isCardView = false; - /** Whether this table provides a UI for filters*/ + /** Whether this table provides a UI for filters */ public withFilters = false; /** Whether the filters are expanded */ public filtersExpanded = false; - /** Whether the button to open filters shall be visible*/ + /** Whether the button to open filters shall be visible */ public showFilterButton = false; - /** Whether this table provides a UI for filters*/ - public filterButtonText:string = I18n.t("js.button_filter"); + /** Whether this table provides a UI for filters */ + public filterButtonText:string = I18n.t('js.button_filter'); constructor(providedConfig:WorkPackageTableConfigurationObject) { _.each(providedConfig, (value, k) => { diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table-hover-sync.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table-hover-sync.ts index 528896467a..4a7cbdef6f 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table-hover-sync.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table-hover-sync.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,7 +29,6 @@ const cssClassRowHovered = 'row-hovered'; export class WpTableHoverSync { - private lastHoveredElement:Element | null = null; private eventListener = (evt:MouseEvent) => { @@ -90,10 +89,10 @@ export class WpTableHoverSync { const hovered = parentTableRow !== null ? parentTableRow : parentTimelineRow; const wpId = this.extractWorkPackageId(hovered!); - const tableRow:JQuery = this.tableAndTimeline.find('tr.wp-row-' + wpId).first(); - const timelineRow:JQuery = this.tableAndTimeline.find('div.wp-row-' + wpId).length ? - this.tableAndTimeline.find('div.wp-row-' + wpId).first() : - this.tableAndTimeline.find('div.wp-ancestor-row-' + wpId).first(); + const tableRow:JQuery = this.tableAndTimeline.find(`tr.wp-row-${wpId}`).first(); + const timelineRow:JQuery = this.tableAndTimeline.find(`div.wp-row-${wpId}`).length + ? this.tableAndTimeline.find(`div.wp-row-${wpId}`).first() + : this.tableAndTimeline.find(`div.wp-ancestor-row-${wpId}`).first(); requestAnimationFrame(() => { this.removeAllHoverClasses(); diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table-scroll-sync.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table-scroll-sync.ts index 94bc4419ad..0ee218c36a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table-scroll-sync.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table-scroll-sync.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -export const selectorTableSide = ".work-packages-tabletimeline--table-side"; -export const selectorTimelineSide = ".work-packages-tabletimeline--timeline-side"; -const jQueryScrollSyncEventNamespace = ".scroll-sync"; +export const selectorTableSide = '.work-packages-tabletimeline--table-side'; +export const selectorTimelineSide = '.work-packages-tabletimeline--timeline-side'; +const jQueryScrollSyncEventNamespace = '.scroll-sync'; const scrollStep = 15; - function getXandYScrollDeltas(ev:WheelEvent):[number, number] { let x = ev.deltaX; let y = ev.deltaY; @@ -72,11 +71,11 @@ function syncWheelEvent(jev:JQuery.TriggeredEvent, elementTable:JQuery, elementT deltaX = getPlattformAgnosticScrollAmount(deltaX); // apply only in target div deltaY = getPlattformAgnosticScrollAmount(deltaY); // apply in both divs - window.requestAnimationFrame(function () { + window.requestAnimationFrame(() => { elementTable[0].scrollTop = elementTable[0].scrollTop + deltaY; elementTimeline[0].scrollTop = elementTable[0].scrollTop + deltaY; - scrollTarget.scrollLeft = scrollTarget.scrollLeft + deltaX; + scrollTarget.scrollLeft += deltaX; }); } @@ -86,22 +85,20 @@ function syncWheelEvent(jev:JQuery.TriggeredEvent, elementTable:JQuery, elementT * @param $element true if the timeline is visible, false otherwise. */ export function createScrollSync($element:JQuery) { - - var elTable = jQuery($element).find(selectorTableSide); - var elTimeline = jQuery($element).find(selectorTimelineSide); + const elTable = jQuery($element).find(selectorTableSide); + const elTimeline = jQuery($element).find(selectorTimelineSide); return (timelineVisible:boolean) => { - // state vars - var syncedLeft = false; - var syncedRight = false; + let syncedLeft = false; + let syncedRight = false; if (timelineVisible) { // setup event listener for table - elTable.on("wheel" + jQueryScrollSyncEventNamespace, (jev:JQuery.TriggeredEvent) => { + elTable.on(`wheel${jQueryScrollSyncEventNamespace}`, (jev:JQuery.TriggeredEvent) => { syncWheelEvent(jev, elTable, elTimeline); }); - elTable.on("scroll" + jQueryScrollSyncEventNamespace, (ev:JQuery.TriggeredEvent) => { + elTable.on(`scroll${jQueryScrollSyncEventNamespace}`, (ev:JQuery.TriggeredEvent) => { syncedLeft = true; if (!syncedRight) { elTimeline[0].scrollTop = ev.target.scrollTop; @@ -113,10 +110,10 @@ export function createScrollSync($element:JQuery) { }); // setup event listener for timeline - elTimeline.on("wheel" + jQueryScrollSyncEventNamespace, (jev:JQuery.TriggeredEvent) => { + elTimeline.on(`wheel${jQueryScrollSyncEventNamespace}`, (jev:JQuery.TriggeredEvent) => { syncWheelEvent(jev, elTable, elTimeline); }); - elTimeline.on("scroll" + jQueryScrollSyncEventNamespace, (ev:JQuery.TriggeredEvent) => { + elTimeline.on(`scroll${jQueryScrollSyncEventNamespace}`, (ev:JQuery.TriggeredEvent) => { syncedRight = true; if (!syncedLeft) { elTable[0].scrollTop = ev.target.scrollTop; @@ -130,5 +127,4 @@ export function createScrollSync($element:JQuery) { elTable.off(jQueryScrollSyncEventNamespace); } }; - } diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts index 528d52f56d..af026f26da 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,29 +26,30 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, Directive, ElementRef, Injector, Input } from '@angular/core'; +import { + AfterViewInit, Directive, ElementRef, Injector, Input, +} from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { States } from "core-app/core/states/states.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageViewSumService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; -import { combineLatest } from "rxjs"; -import { GroupSumsBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { States } from 'core-app/core/states/states.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; +import { combineLatest } from 'rxjs'; +import { GroupSumsBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/modes/grouped/group-sums-builder'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; @Directive({ selector: '[wpTableSumsRow]', host: { - '[class.-hidden]': 'isHidden' + '[class.-hidden]': 'isHidden', }, }) export class WorkPackageTableSumsRowController implements AfterViewInit { - @Input('wpTableSumsRow-table') workPackageTable:WorkPackageTable; public isHidden = true; @@ -60,16 +61,15 @@ export class WorkPackageTableSumsRowController implements AfterViewInit { private groupSumsBuilder:GroupSumsBuilder; constructor(readonly injector:Injector, - readonly elementRef:ElementRef, - readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly schemaCache:SchemaCacheService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly wpTableSums:WorkPackageViewSumService, - readonly I18n:I18nService) { - + readonly elementRef:ElementRef, + readonly querySpace:IsolatedQuerySpace, + readonly states:States, + readonly schemaCache:SchemaCacheService, + readonly wpTableColumns:WorkPackageViewColumnsService, + readonly wpTableSums:WorkPackageViewSumService, + readonly I18n:I18nService) { this.text = { - sum: I18n.t('js.label_total_sum') + sum: I18n.t('js.label_total_sum'), }; } @@ -82,7 +82,7 @@ export class WorkPackageTableSumsRowController implements AfterViewInit { this.querySpace.results.values$(), ]) .pipe( - takeUntil(this.querySpace.stopAllSubscriptions) + takeUntil(this.querySpace.stopAllSubscriptions), ) .subscribe(([columns, sum, resource]) => { this.isHidden = !sum; diff --git a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts index 3872a1b20a..c3a4ae4632 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -35,32 +35,32 @@ import { Input, NgZone, OnInit, Output, - ViewEncapsulation + ViewEncapsulation, } from '@angular/core'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TableEventComponent, TableHandlerRegistry } from 'core-app/features/work-packages/components/wp-fast-table/handlers/table-handler-registry'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { combineLatest } from 'rxjs'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { createScrollSync } from "core-app/features/work-packages/components/wp-table/wp-table-scroll-sync"; -import { WpTableHoverSync } from "core-app/features/work-packages/components/wp-table/wp-table-hover-sync"; -import { WorkPackageTimelineTableController } from "core-app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive"; -import { WorkPackageTable } from "core-app/features/work-packages/components/wp-fast-table/wp-fast-table"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import {WorkPackageViewSumService} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { createScrollSync } from 'core-app/features/work-packages/components/wp-table/wp-table-scroll-sync'; +import { WpTableHoverSync } from 'core-app/features/work-packages/components/wp-table/wp-table-hover-sync'; +import { WorkPackageTimelineTableController } from 'core-app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive'; +import { WorkPackageTable } from 'core-app/features/work-packages/components/wp-fast-table/wp-fast-table'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; import { WorkPackageTableConfiguration, WorkPackageTableConfigurationObject, -} from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; -import { States } from "core-app/core/states/states.service"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +} from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; +import { States } from 'core-app/core/states/states.service'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; export interface WorkPackageFocusContext { /** Work package that was focused */ @@ -77,12 +77,14 @@ export interface WorkPackageFocusContext { selector: 'wp-table', }) export class WorkPackagesTableComponent extends UntilDestroyedMixin implements OnInit, TableEventComponent { - @Input() projectIdentifier:string; + @Input('configuration') configurationObject:WorkPackageTableConfigurationObject; @Output() selectionChanged = new EventEmitter(); + @Output() itemClicked = new EventEmitter<{ workPackageId:string, double:boolean }>(); + @Output() stateLinkClicked = new EventEmitter<{ workPackageId:string, requestedState:string }>(); public trackByHref = AngularTrackingHelpers.trackByHref; @@ -126,21 +128,21 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O // We need to sync certain height difference to the timeline // depending on whether inline create or sums rows are being shown public inlineCreateVisible = false; + public sumVisible = false; constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly states:States, - readonly querySpace:IsolatedQuerySpace, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly zone:NgZone, - readonly wpTableGroupBy:WorkPackageViewGroupByService, - readonly wpTableTimeline:WorkPackageViewTimelineService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly wpTableSortBy:WorkPackageViewSortByService, - readonly wpTableSums:WorkPackageViewSumService, - ) { + readonly injector:Injector, + readonly states:States, + readonly querySpace:IsolatedQuerySpace, + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef, + readonly zone:NgZone, + readonly wpTableGroupBy:WorkPackageViewGroupByService, + readonly wpTableTimeline:WorkPackageViewTimelineService, + readonly wpTableColumns:WorkPackageViewColumnsService, + readonly wpTableSortBy:WorkPackageViewSortByService, + readonly wpTableSums:WorkPackageViewSumService) { super(); } @@ -157,17 +159,15 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O cancel: I18n.t('js.button_cancel'), noResults: { title: I18n.t('js.work_packages.no_results.title'), - description: I18n.t('js.work_packages.no_results.description') - }, - limitedResults: (count:number, total:number) => { - return I18n.t('js.work_packages.limited_results', { count: count, total: total }); + description: I18n.t('js.work_packages.no_results.description'), }, + limitedResults: (count:number, total:number) => I18n.t('js.work_packages.limited_results', { count, total }), tableSummary: I18n.t('js.work_packages.table.summary'), tableSummaryHints: [ I18n.t('js.work_packages.table.text_inline_edit'), I18n.t('js.work_packages.table.text_select_hint'), - I18n.t('js.work_packages.table.text_sort_hint') - ].join(' ') + I18n.t('js.work_packages.table.text_sort_hint'), + ].join(' '), }; const statesCombined = combineLatest([ @@ -176,11 +176,11 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O this.wpTableColumns.live$(), this.wpTableTimeline.live$(), this.wpTableSortBy.live$(), - this.wpTableSums.live$() + this.wpTableSums.live$(), ]); statesCombined.pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe(([results, groupBy, columns, timelines, sort, sums]) => { this.query = this.querySpace.query.value!; @@ -228,7 +228,7 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O // Timeline controller controller, // Table configuration - this.configuration + this.configuration, ); this.tbody = tbody; controller.workPackageTable = this.workPackageTable; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab.ts index dc98e85af8..19b10b60db 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab.ts @@ -1,8 +1,8 @@ import { Component, Injector, Type } from '@angular/core'; -import { Observable } from "rxjs"; -import { StateService } from "@uirouter/angular"; -import { TabDefinition } from "core-app/shared/components/tabs/tab.interface"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { Observable } from 'rxjs'; +import { StateService } from '@uirouter/angular'; +import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; export interface TabComponent extends Component { workPackage:WorkPackageResource; @@ -12,4 +12,4 @@ export interface WpTabDefinition extends TabDefinition { component:Type; displayable?:(workPackage:WorkPackageResource, $state:StateService) => boolean; count?:(workPackage:WorkPackageResource, injector:Injector) => Observable; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts index db522659b3..254819f218 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts @@ -26,15 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. // ++ -import {Transition} from '@uirouter/core'; -import {Component, OnInit} from '@angular/core'; -import {I18nService} from 'core-app/core/i18n/i18n.service'; -import {APIV3Service} from 'core-app/core/apiv3/api-v3.service'; -import {Observable} from "rxjs"; -import {map} from "rxjs/operators"; -import { WpTabDefinition } from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab"; -import { WorkPackageTabsService } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { Transition } from '@uirouter/core'; +import { Component, OnInit } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { WpTabDefinition } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab'; +import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ templateUrl: './wp-tab-wrapper.html', @@ -42,19 +42,20 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-packag }) export class WpTabWrapperComponent implements OnInit { workPackage:WorkPackageResource; + ndcDynamicInputs$:Observable<{ workPackage:WorkPackageResource; tab:WpTabDefinition | undefined; }>; get workPackageId() { - return(this.$transition.params('to').workPackageId); + return (this.$transition.params('to').workPackageId); } constructor(readonly I18n:I18nService, - readonly $transition:Transition, - readonly apiV3Service:APIV3Service, - readonly wpTabsService:WorkPackageTabsService) {} + readonly $transition:Transition, + readonly apiV3Service:APIV3Service, + readonly wpTabsService:WorkPackageTabsService) {} ngOnInit() { this.ndcDynamicInputs$ = this @@ -63,15 +64,15 @@ export class WpTabWrapperComponent implements OnInit { .id(this.workPackageId) .requireAndStream() .pipe( - map(wp => ({ + map((wp) => ({ workPackage: wp, tab: this.findTab(wp), - })) + })), ); } findTab(workPackage:WorkPackageResource):WpTabDefinition | undefined { - const tabIdentifier = this.$transition.params('to').tabIdentifier; + const { tabIdentifier } = this.$transition.params('to'); return this.wpTabsService.getTab(tabIdentifier, workPackage); } diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts index 29687e4d32..b8e81693fd 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts @@ -1,13 +1,13 @@ -import {Input} from '@angular/core'; +import { Input } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { TabComponent } from '../wp-tab-wrapper/tab'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { StateService, UIRouterGlobals } from '@uirouter/core'; +import { ScrollableTabsComponent } from 'core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component'; +import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; import { WpTabsComponent } from './wp-tabs.component'; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { StateService, UIRouterGlobals } from "@uirouter/core"; -import { ScrollableTabsComponent } from "core-app/shared/components/tabs/scrollable-tabs/scrollable-tabs.component"; -import { WorkPackageTabsService } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service"; +import { TabComponent } from '../wp-tab-wrapper/tab'; describe('WpTabsComponent', () => { class TestComponent implements TabComponent { @@ -18,14 +18,14 @@ describe('WpTabsComponent', () => { component: TestComponent, name: 'Displayable TestTab', id: 'displayable-test-tab', - displayable: () => true + displayable: () => true, }; const notDisplayableTab = { component: TestComponent, name: 'NotDisplayable TestTab', id: 'not-displayable-test-tab', - displayable: () => false + displayable: () => false, }; let component:WpTabsComponent; @@ -60,4 +60,4 @@ describe('WpTabsComponent', () => { const tabLink:HTMLElement = fixture.debugElement.query(By.css('[data-qa-tab-id="displayable-test-tab"]')).nativeElement; expect(tabLink.innerText).toContain('Displayable TestTab'); })); -}); \ No newline at end of file +}); diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts index 47aa8ad99f..cc3e50b49c 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.ts @@ -1,10 +1,12 @@ -import { ChangeDetectionStrategy, Component, Injector, Input, OnInit } from '@angular/core'; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { StateService, UIRouterGlobals } from "@uirouter/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { TabDefinition } from "core-app/shared/components/tabs/tab.interface"; -import { WorkPackageTabsService } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { + ChangeDetectionStrategy, Component, Injector, Input, OnInit, +} from '@angular/core'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { StateService, UIRouterGlobals } from '@uirouter/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { TabDefinition } from 'core-app/shared/components/tabs/tab.interface'; +import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @Component({ selector: 'op-wp-tabs', @@ -14,10 +16,13 @@ import { WorkPackageResource } from "core-app/features/hal/resources/work-packag }) export class WpTabsComponent implements OnInit { @Input() workPackage:WorkPackageResource; + @Input() view:'full'|'split'; public tabs:TabDefinition[]; + public uiSrefBase:string; + public canViewWatchers = false; text = { @@ -47,13 +52,11 @@ export class WpTabsComponent implements OnInit { return this .wpTabsService .getDisplayableTabs(this.workPackage) - .map(tab => { - return { - ...tab, - route: this.uiSrefBase + '.tabs', - routeParams: { workPackageId: this.workPackage.id, tabIdentifier: tab.id } - }; - }); + .map((tab) => ({ + ...tab, + route: `${this.uiSrefBase}.tabs`, + routeParams: { workPackageId: this.workPackage.id, tabIdentifier: tab.id }, + })); } public switchToFullscreen():void { @@ -63,7 +66,7 @@ export class WpTabsComponent implements OnInit { public close():void { this.$state.go( this.uiRouterGlobals.current.data.baseRoute, - this.uiRouterGlobals.params + this.uiRouterGlobals.params, ); } } diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function.ts index f2bc33fa94..fadc0e4144 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function.ts @@ -1,9 +1,9 @@ import { Injector } from '@angular/core'; import { combineLatest, Observable } from 'rxjs'; -import { map } from "rxjs/operators"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; +import { map } from 'rxjs/operators'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; export function workPackageRelationsCount( workPackage:WorkPackageResource, diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts index 9f7c4f63a3..352971167b 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.spec.ts @@ -1,16 +1,16 @@ import { HttpClientModule } from '@angular/common/http'; -import { Injector } from '@angular/core'; -import { Input } from '@angular/core'; +import { Injector, Input } from '@angular/core'; + import { TestBed } from '@angular/core/testing'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { StateService } from '@uirouter/angular'; +import { WorkPackageTabsService } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service'; import { TabComponent } from '../../components/wp-tab-wrapper/tab'; -import { StateService } from "@uirouter/angular"; -import { WorkPackageTabsService } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service"; describe('WpTabsService', () => { let service:WorkPackageTabsService; - let workPackage:any = { id: 1234 }; + const workPackage:any = { id: 1234 }; let injector:Injector; let halResourceService:HalResourceService; @@ -38,8 +38,8 @@ describe('WpTabsService', () => { HttpClientModule, ], providers: [ - { provide: StateService, useValue: { includes: () => false } } - ] + { provide: StateService, useValue: { includes: () => false } }, + ], }); service = TestBed.inject(WorkPackageTabsService); (service as any).registeredTabs = []; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts index f10eb53eb3..86cff21411 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-tabs.service.ts @@ -1,14 +1,14 @@ import { Injectable, Injector } from '@angular/core'; -import {WorkPackageResource} from "core-app/features/hal/resources/work-package-resource"; -import { WpTabDefinition } from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab"; -import { WorkPackageRelationsTabComponent } from "core-app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component"; -import { StateService } from "@uirouter/core"; -import { WorkPackageOverviewTabComponent } from "core-app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component"; -import { WorkPackageActivityTabComponent } from "core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component"; -import { WorkPackageWatchersTabComponent } from "core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { workPackageWatchersCount } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function"; -import { workPackageRelationsCount } from "core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WpTabDefinition } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/tab'; +import { WorkPackageRelationsTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component'; +import { StateService } from '@uirouter/core'; +import { WorkPackageOverviewTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component'; +import { WorkPackageActivityTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/activity-tab.component'; +import { WorkPackageWatchersTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { workPackageWatchersCount } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function'; +import { workPackageRelationsCount } from 'core-app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-relations-count.function'; @Injectable({ providedIn: 'root', @@ -39,10 +39,10 @@ export class WorkPackageTabsService { return this .tabs .filter( - tab => !tab.displayable || tab.displayable(workPackage, this.$state), + (tab) => !tab.displayable || tab.displayable(workPackage, this.$state), ) .map( - tab => ({ + (tab) => ({ ...tab, ...!!tab.count && { counter: tab.count(workPackage, this.injector) }, }), @@ -50,7 +50,7 @@ export class WorkPackageTabsService { } getTab(tabId:string, workPackage:WorkPackageResource):WpTabDefinition|undefined { - return this.getDisplayableTabs(workPackage).find(({ id: id }) => id === tabId); + return this.getDisplayableTabs(workPackage).find(({ id }) => id === tabId); } private buildDefaultTabs():WpTabDefinition[] { diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function.ts b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function.ts index 1b9b33b72e..a92fc60ea6 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/services/wp-tabs/wp-watchers-count.function.ts @@ -1,9 +1,9 @@ import { Injector } from '@angular/core'; import { Observable } from 'rxjs'; -import { WorkPackageWatchersService } from "core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { map } from "rxjs/operators"; +import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { map } from 'rxjs/operators'; export function workPackageWatchersCount( workPackage:WorkPackageResource, diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/wp-tabs.module.ts b/frontend/src/app/features/work-packages/components/wp-tabs/wp-tabs.module.ts index 565114d4bf..1c3dfe0251 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/wp-tabs.module.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/wp-tabs.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { UIRouterModule } from '@uirouter/angular'; +import { WpTabWrapperComponent } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component'; +import { DynamicModule } from 'ng-dynamic-component'; +import { OpenprojectAccessibilityModule } from 'core-app/shared/directives/a11y/openproject-a11y.module'; +import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; +import { IconModule } from 'core-app/shared/components/icon/icon.module'; import { WpTabsComponent } from './components/wp-tabs/wp-tabs.component'; -import {UIRouterModule} from "@uirouter/angular"; -import {WpTabWrapperComponent} from "core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component"; -import {DynamicModule} from "ng-dynamic-component"; -import { OpenprojectAccessibilityModule } from "core-app/shared/directives/a11y/openproject-a11y.module"; -import { OpenprojectTabsModule } from "core-app/shared/components/tabs/openproject-tabs.module"; -import { IconModule } from "core-app/shared/components/icon/icon.module"; @NgModule({ declarations: [ @@ -19,7 +19,7 @@ import { IconModule } from "core-app/shared/components/icon/icon.module"; DynamicModule, OpenprojectAccessibilityModule, OpenprojectTabsModule, - IconModule + IconModule, ], exports: [ WpTabsComponent, diff --git a/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts b/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts index 4bb9b781ee..7dbe70c467 100644 --- a/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { Component, Input } from '@angular/core'; @Component({ selector: 'wp-type-status', - templateUrl: './wp-type-status.html' + templateUrl: './wp-type-status.html', }) export class WorkPackageTypeStatusComponent { @Input('workPackage') workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts b/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts index 0a9a7d5820..691b60f9e5 100644 --- a/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,33 +26,41 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Component({ selector: 'wp-watcher-button', templateUrl: './wp-watcher-button.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin implements OnInit { @Input('workPackage') public workPackage:WorkPackageResource; + @Input('showText') public showText = false; + @Input('disabled') public disabled = false; public buttonText:string; + public buttonTitle:string; + public buttonClass:string; + public buttonId:string; + public watchIconClass:string; constructor(readonly I18n:I18nService, - readonly wpWatchersService:WorkPackageWatchersService, - readonly apiV3Service:APIV3Service, - readonly cdRef:ChangeDetectorRef) { + readonly wpWatchersService:WorkPackageWatchersService, + readonly apiV3Service:APIV3Service, + readonly cdRef:ChangeDetectorRef) { super(); } @@ -63,7 +71,7 @@ export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin imple .id(this.workPackage) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { this.workPackage = wp; @@ -84,7 +92,7 @@ export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin imple const toggleLink = this.nextStateLink(); toggleLink(toggleLink.$link.payload).then(() => { - this.wpWatchersService.clear(this.workPackage.id!); + this.wpWatchersService.clear(this.workPackage.id); this .apiV3Service .work_packages @@ -105,7 +113,6 @@ export class WorkPackageWatcherButtonComponent extends UntilDestroyedMixin imple this.buttonClass = '-active'; this.buttonId = 'unwatch-button'; this.watchIconClass = 'icon-watched'; - } else { this.buttonTitle = this.I18n.t('js.label_watch_work_package'); this.buttonText = this.I18n.t('js.label_watch'); diff --git a/frontend/src/app/features/work-packages/directives/query-space/isolated-graph-query-space.ts b/frontend/src/app/features/work-packages/directives/query-space/isolated-graph-query-space.ts index 1c53f0c091..fc3a3e7537 100644 --- a/frontend/src/app/features/work-packages/directives/query-space/isolated-graph-query-space.ts +++ b/frontend/src/app/features/work-packages/directives/query-space/isolated-graph-query-space.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { input } from 'reactivestates'; import { ChartType } from 'chart.js'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @Injectable() export class IsolatedGraphQuerySpace extends IsolatedQuerySpace { diff --git a/frontend/src/app/features/work-packages/directives/query-space/isolated-query-space.ts b/frontend/src/app/features/work-packages/directives/query-space/isolated-query-space.ts index 4f3ea6518e..4291e82f04 100644 --- a/frontend/src/app/features/work-packages/directives/query-space/isolated-query-space.ts +++ b/frontend/src/app/features/work-packages/directives/query-space/isolated-query-space.ts @@ -1,15 +1,16 @@ -import { derive, input, InputState, State, StatesGroup } from 'reactivestates'; +import { + derive, input, InputState, State, StatesGroup, +} from 'reactivestates'; import { Subject } from 'rxjs'; import { Injectable } from '@angular/core'; import { map } from 'rxjs/operators'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { QueryFormResource } from "core-app/features/hal/resources/query-form-resource"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; -import { GroupObject, WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { GroupObject, WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; @Injectable() export class IsolatedQuerySpace extends StatesGroup { - constructor() { super(); } @@ -24,8 +25,10 @@ export class IsolatedQuerySpace extends StatesGroup { // the results associated with the table/time-entry-changeset results = input(); + // all groups returned as results groups = input(); + // Set of columns in strict order of appearance columns = input(); @@ -38,13 +41,13 @@ export class IsolatedQuerySpace extends StatesGroup { // Event to be raised when the timeline is up to date timelineRendered = new Subject(); - renderedWorkPackages:State = derive(this.tableRendered, $ => $.pipe( - map(rows => rows.filter(row => !!row.workPackageId))) - ); + renderedWorkPackages:State = derive(this.tableRendered, ($) => $.pipe( + map((rows) => rows.filter((row) => !!row.workPackageId)), + )); - renderedWorkPackageIds:State = derive(this.renderedWorkPackages, $ => $.pipe( - map(rows => rows.map(row => row.workPackageId!.toString()))) - ); + renderedWorkPackageIds:State = derive(this.renderedWorkPackages, ($) => $.pipe( + map((rows) => rows.map((row) => row.workPackageId!.toString())), + )); // Subject used to unregister all listeners of states above. stopAllSubscriptions = new Subject(); diff --git a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive.ts b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive.ts index 502b53edab..031b4e4883 100644 --- a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive.ts +++ b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,38 +27,38 @@ //++ import { Directive } from '@angular/core'; -import { OpTableActionsService } from "core-app/features/work-packages/components/wp-table/table-actions/table-actions.service"; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { WorkPackageViewPaginationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageViewSumService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; -import { WorkPackageViewAdditionalElementsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service"; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { WorkPackageStatesInitializationService } from "core-app/features/work-packages/components/wp-list/wp-states-initialization.service"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; +import { OpTableActionsService } from 'core-app/features/work-packages/components/wp-table/table-actions/table-actions.service'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; +import { WorkPackageViewAdditionalElementsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service'; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; -import { WorkPackageContextMenuHelperService } from "core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WpChildrenInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service"; -import { WpRelationInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service"; -import { WorkPackagesListChecksumService } from "core-app/features/work-packages/components/wp-list/wp-list-checksum.service"; -import { TableDragActionsRegistryService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service"; -import { WorkPackageViewHierarchyIdentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service"; -import { WorkPackageService } from "core-app/features/work-packages/services/work-package.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageIsolatedQuerySpaceDirective } from "core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive"; -import { IsolatedGraphQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-graph-query-space"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; +import { WorkPackageContextMenuHelperService } from 'core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WpChildrenInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service'; +import { WpRelationInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service'; +import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; +import { TableDragActionsRegistryService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service'; +import { WorkPackageViewHierarchyIdentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service'; +import { WorkPackageService } from 'core-app/features/work-packages/services/work-package.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageIsolatedQuerySpaceDirective } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; +import { IsolatedGraphQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-graph-query-space'; export const WpIsolatedGraphQuerySpaceProviders = [ // Open the isolated space first, order is important here @@ -102,7 +102,6 @@ export const WpIsolatedGraphQuerySpaceProviders = [ TableDragActionsRegistryService, ]; - /** * Directive to open a work package query 'space', an isolated injector hierarchy * that provides access to query-bound data and services, especially around the querySpace services. @@ -112,7 +111,7 @@ export const WpIsolatedGraphQuerySpaceProviders = [ */ @Directive({ selector: '[wp-isolated-graph-query-space]', - providers: WpIsolatedGraphQuerySpaceProviders + providers: WpIsolatedGraphQuerySpaceProviders, }) export class WorkPackageIsolatedGraphQuerySpaceDirective extends WorkPackageIsolatedQuerySpaceDirective { } diff --git a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts index 4716c23896..fb0f1aecd3 100644 --- a/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts +++ b/frontend/src/app/features/work-packages/directives/query-space/wp-isolated-query-space.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,44 +27,44 @@ //++ import { Directive, ElementRef, Injector } from '@angular/core'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { OpTableActionsService } from "core-app/features/work-packages/components/wp-table/table-actions/table-actions.service"; -import { WorkPackageViewRelationColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service"; -import { WorkPackageViewPaginationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageViewSumService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; -import { WorkPackageViewAdditionalElementsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service"; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { WorkPackageCreateService } from "core-app/features/work-packages/components/wp-new/wp-create.service"; -import { WorkPackageStatesInitializationService } from "core-app/features/work-packages/components/wp-list/wp-states-initialization.service"; -import { WorkPackageViewFocusService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { WorkPackageFiltersService } from "core-app/features/work-packages/components/filters/wp-filters/wp-filters.service"; -import { WorkPackageContextMenuHelperService } from "core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service"; -import { WorkPackageInlineCreateService } from "core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service"; -import { WpChildrenInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service"; -import { WpRelationInlineCreateService } from "core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service"; -import { WorkPackagesListChecksumService } from "core-app/features/work-packages/components/wp-list/wp-list-checksum.service"; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { TableDragActionsRegistryService } from "core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { WorkPackageCardViewService } from "core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { WorkPackageViewHierarchyIdentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { TimeEntryCreateService } from "core-app/shared/components/time_entries/create/create.service"; -import { WorkPackageViewCollapsedGroupsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service"; -import { WorkPackageService } from "core-app/features/work-packages/services/work-package.service"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { OpTableActionsService } from 'core-app/features/work-packages/components/wp-table/table-actions/table-actions.service'; +import { WorkPackageViewRelationColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service'; +import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; +import { WorkPackageViewAdditionalElementsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service'; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { WorkPackageCreateService } from 'core-app/features/work-packages/components/wp-new/wp-create.service'; +import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; +import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { WorkPackageFiltersService } from 'core-app/features/work-packages/components/filters/wp-filters/wp-filters.service'; +import { WorkPackageContextMenuHelperService } from 'core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service'; +import { WorkPackageInlineCreateService } from 'core-app/features/work-packages/components/wp-inline-create/wp-inline-create.service'; +import { WpChildrenInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-inline-create.service'; +import { WpRelationInlineCreateService } from 'core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-inline-create.service'; +import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { TableDragActionsRegistryService } from 'core-app/features/work-packages/components/wp-table/drag-and-drop/actions/table-drag-actions-registry.service'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { WorkPackageCardViewService } from 'core-app/features/work-packages/components/wp-card-view/services/wp-card-view.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { WorkPackageViewHierarchyIdentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { TimeEntryCreateService } from 'core-app/shared/components/time_entries/create/create.service'; +import { WorkPackageViewCollapsedGroupsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service'; +import { WorkPackageService } from 'core-app/features/work-packages/services/work-package.service'; /** * Directive to open a work package query 'space', an isolated injector hierarchy @@ -126,13 +126,12 @@ import { WorkPackageService } from "core-app/features/work-packages/services/wor // Table Drag & Drop actions TableDragActionsRegistryService, - ] + ], }) export class WorkPackageIsolatedQuerySpaceDirective { - constructor(private elementRef:ElementRef, - public querySpace:IsolatedQuerySpace, - private injector:Injector) { - debugLog("Opening isolated query space %O in %O", injector, elementRef.nativeElement); + public querySpace:IsolatedQuerySpace, + private injector:Injector) { + debugLog('Opening isolated query space %O in %O', injector, elementRef.nativeElement); } } diff --git a/frontend/src/app/features/work-packages/helpers/time-entries/time-entry-changeset.ts b/frontend/src/app/features/work-packages/helpers/time-entries/time-entry-changeset.ts index bfd17ce691..edb3e7e2c9 100644 --- a/frontend/src/app/features/work-packages/helpers/time-entries/time-entry-changeset.ts +++ b/frontend/src/app/features/work-packages/helpers/time-entries/time-entry-changeset.ts @@ -1,8 +1,7 @@ -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; export class TimeEntryChangeset extends ResourceChangeset { - public setValue(key:string, val:any) { super.setValue(key, val); @@ -16,7 +15,7 @@ export class TimeEntryChangeset extends ResourceChangeset { const payload = super.buildPayloadFromChanges(); // we ignore the project and instead rely completely on the work package. - delete payload['_links']['project']; + delete payload._links.project; return payload; } diff --git a/frontend/src/app/features/work-packages/openproject-work-package-routes.module.ts b/frontend/src/app/features/work-packages/openproject-work-package-routes.module.ts index 0c15018513..c034d31db1 100644 --- a/frontend/src/app/features/work-packages/openproject-work-package-routes.module.ts +++ b/frontend/src/app/features/work-packages/openproject-work-package-routes.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,9 +27,9 @@ //++ import { NgModule } from '@angular/core'; -import { UIRouterModule } from "@uirouter/angular"; -import { WORK_PACKAGES_ROUTES } from "core-app/features/work-packages/routing/work-packages-routes"; -import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/openproject-work-packages.module"; +import { UIRouterModule } from '@uirouter/angular'; +import { WORK_PACKAGES_ROUTES } from 'core-app/features/work-packages/routing/work-packages-routes'; +import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; /** * Separate module for work package routes because WP modules @@ -45,7 +45,7 @@ import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/o // Routes for /work_packages UIRouterModule.forChild({ states: WORK_PACKAGES_ROUTES }), - ] + ], }) export class OpenprojectWorkPackageRoutesModule { } diff --git a/frontend/src/app/features/work-packages/openproject-work-packages.module.ts b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts index 45124aff20..863999a6c9 100644 --- a/frontend/src/app/features/work-packages/openproject-work-packages.module.ts +++ b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -67,13 +67,13 @@ import { WorkPackageFilterButtonComponent } from 'core-app/features/work-package import { WorkPackageDetailsViewButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component'; import { WorkPackageFoldToggleButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component'; import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; -import { WpTableConfigurationColumnsTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component'; -import { WpTableConfigurationDisplaySettingsTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component'; +import { WpTableConfigurationColumnsTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component'; +import { WpTableConfigurationDisplaySettingsTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component'; import { WpTableConfigurationFiltersTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/filters-tab.component'; -import { WpTableConfigurationSortByTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component'; -import { WpTableConfigurationTimelinesTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component'; -import { WpTableConfigurationHighlightingTab } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component'; -import { WpTableConfigurationRelationSelectorComponent } from "core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector"; +import { WpTableConfigurationSortByTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component'; +import { WpTableConfigurationTimelinesTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component'; +import { WpTableConfigurationHighlightingTabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tabs/highlighting-tab.component'; +import { WpTableConfigurationRelationSelectorComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector'; import { WorkPackageWatchersTabComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/watchers-tab.component'; import { WorkPackageWatcherEntryComponent } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watcher-entry.component'; import { WorkPackageNewSplitViewComponent } from 'core-app/features/work-packages/components/wp-new/wp-new-split-view.component'; @@ -83,95 +83,94 @@ import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openp import { WorkPackageTableSumsRowController } from 'core-app/features/work-packages/components/wp-table/wp-table-sums-row/wp-table-sums-row.directive'; import { ExternalQueryConfigurationComponent } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component'; import { ExternalQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.service'; -import { ExternalRelationQueryConfigurationComponent } from "core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component"; -import { ExternalRelationQueryConfigurationService } from "core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service"; +import { ExternalRelationQueryConfigurationComponent } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component'; +import { ExternalRelationQueryConfigurationService } from 'core-app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.service'; import { WorkPackageStaticQueriesService } from 'core-app/features/work-packages/components/wp-query-select/wp-static-queries.service'; import { WorkPackagesListInvalidQueryService } from 'core-app/features/work-packages/components/wp-list/wp-list-invalid-query.service'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { WorkPackageWatchersService } from 'core-app/features/work-packages/components/wp-single-view-tabs/watchers-tab/wp-watchers.service'; import { WorkPackagesActivityService } from 'core-app/features/work-packages/components/wp-single-view-tabs/activity-panel/wp-activity.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageChildrenQueryComponent } from "core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component"; -import { WpRelationInlineAddExistingComponent } from "core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component"; -import { WorkPackageRelationQueryComponent } from "core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component"; -import { WorkPackagesBaseComponent } from "core-app/features/work-packages/routing/wp-base/wp--base.component"; -import { WorkPackageSplitViewComponent } from "core-app/features/work-packages/routing/wp-split-view/wp-split-view.component"; -import { WorkPackagesFullViewComponent } from "core-app/features/work-packages/routing/wp-full-view/wp-full-view.component"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageChildrenQueryComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component'; +import { WpRelationInlineAddExistingComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component'; +import { WorkPackageRelationQueryComponent } from 'core-app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component'; +import { WorkPackagesBaseComponent } from 'core-app/features/work-packages/routing/wp-base/wp--base.component'; +import { WorkPackageSplitViewComponent } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view.component'; +import { WorkPackagesFullViewComponent } from 'core-app/features/work-packages/routing/wp-full-view/wp-full-view.component'; import { AttachmentsUploadComponent } from 'core-app/shared/components/attachments/attachments-upload/attachments-upload.component'; import { AttachmentListComponent } from 'core-app/shared/components/attachments/attachment-list/attachment-list.component'; -import { QueryFiltersService } from "core-app/features/work-packages/components/wp-query/query-filters.service"; -import { WorkPackageCardViewComponent } from "core-app/features/work-packages/components/wp-card-view/wp-card-view.component"; -import { WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { OpenprojectBcfModule } from "core-app/features/bim/bcf/openproject-bcf.module"; -import { WorkPackageRelationsAutocomplete } from "core-app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component"; +import { QueryFiltersService } from 'core-app/features/work-packages/components/wp-query/query-filters.service'; +import { WorkPackageCardViewComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-card-view.component'; +import { WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { OpenprojectBcfModule } from 'core-app/features/bim/bcf/openproject-bcf.module'; +import { WorkPackageRelationsAutocompleteComponent } from 'core-app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component'; import { CustomDateActionAdminComponent } from 'core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component'; -import { WorkPackagesTableConfigMenu } from "core-app/features/work-packages/components/wp-table/config-menu/config-menu.component"; -import { WorkPackageViewToggleButton } from "core-app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component"; -import { WorkPackageViewDropdownMenuDirective } from "core-app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { OpenprojectProjectsModule } from "core-app/features/projects/openproject-projects.module"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackageEditActionsBarComponent } from "core-app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { WorkPackageChangeset } from "core-app/features/work-packages/components/wp-edit/work-package-changeset"; -import { WorkPackageSingleCardComponent } from "core-app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component"; -import { WorkPackageListViewComponent } from "core-app/features/work-packages/routing/wp-list-view/wp-list-view.component"; -import { PartitionedQuerySpacePageComponent } from "core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component"; -import { WorkPackageViewPageComponent } from "core-app/features/work-packages/routing/wp-view-page/wp-view-page.component"; -import { WorkPackageSettingsButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component"; -import { BackButtonComponent } from "core-app/features/work-packages/components/back-routing/back-button.component"; -import { WorkPackagesTableComponent } from "core-app/features/work-packages/components/wp-table/wp-table.component"; -import { WorkPackageGroupToggleDropdownMenuDirective } from "core-app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; -import { OpWpTabsModule } from "core-app/features/work-packages/components/wp-tabs/wp-tabs.module"; -import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/field-controls/edit-field-controls.module"; -import { DatePickerModal } from "core-app/shared/components/datepicker/datepicker.modal"; -import { WpTableExportModal } from "core-app/shared/components/modals/export-modal/wp-table-export.modal"; -import { WpButtonMacroModal } from "core-app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal"; -import { QuerySharingModal } from "core-app/shared/components/modals/share-modal/query-sharing.modal"; -import { SaveQueryModal } from "core-app/shared/components/modals/save-modal/save-query.modal"; -import { QuerySharingForm } from "core-app/shared/components/modals/share-modal/query-sharing-form.component"; -import { WpDestroyModal } from "core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal"; -import { WorkPackageTypeStatusComponent } from "core-app/features/work-packages/components/wp-type-status/wp-type-status.component"; -import { WorkPackageIsolatedQuerySpaceDirective } from "core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive"; -import { WorkPackageBreadcrumbParentComponent } from "core-app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component"; -import { WorkPackageSubjectComponent } from "core-app/features/work-packages/components/wp-subject/wp-subject.component"; -import { WorkPackageBreadcrumbComponent } from "core-app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component"; -import { UserLinkComponent } from "core-app/shared/components/user-link/user-link.component"; -import { WorkPackageCommentComponent } from "core-app/features/work-packages/components/work-package-comment/work-package-comment.component"; -import { WorkPackageWatcherButtonComponent } from "core-app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component"; -import { WorkPackageCommentFieldComponent } from "core-app/features/work-packages/components/work-package-comment/wp-comment-field.component"; -import { WpResizerDirective } from "core-app/shared/components/resizer/resizer/wp-resizer.component"; +import { WorkPackagesTableConfigMenuComponent } from 'core-app/features/work-packages/components/wp-table/config-menu/config-menu.component'; +import { WorkPackageViewToggleButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component'; +import { WorkPackageViewDropdownMenuDirective } from 'core-app/shared/components/op-context-menu/handlers/wp-view-dropdown-menu.directive'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { OpenprojectProjectsModule } from 'core-app/features/projects/openproject-projects.module'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackageEditActionsBarComponent } from 'core-app/features/work-packages/components/edit-actions-bar/wp-edit-actions-bar.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { WorkPackageChangeset } from 'core-app/features/work-packages/components/wp-edit/work-package-changeset'; +import { WorkPackageSingleCardComponent } from 'core-app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component'; +import { WorkPackageListViewComponent } from 'core-app/features/work-packages/routing/wp-list-view/wp-list-view.component'; +import { PartitionedQuerySpacePageComponent } from 'core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component'; +import { WorkPackageViewPageComponent } from 'core-app/features/work-packages/routing/wp-view-page/wp-view-page.component'; +import { WorkPackageSettingsButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component'; +import { BackButtonComponent } from 'core-app/features/work-packages/components/back-routing/back-button.component'; +import { WorkPackagesTableComponent } from 'core-app/features/work-packages/components/wp-table/wp-table.component'; +import { WorkPackageGroupToggleDropdownMenuDirective } from 'core-app/shared/components/op-context-menu/handlers/wp-group-toggle-dropdown-menu.directive'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; +import { OpWpTabsModule } from 'core-app/features/work-packages/components/wp-tabs/wp-tabs.module'; +import { EditFieldControlsModule } from 'core-app/shared/components/fields/edit/field-controls/edit-field-controls.module'; +import { DatePickerModalComponent } from 'core-app/shared/components/datepicker/datepicker.modal'; +import { WpTableExportModalComponent } from 'core-app/shared/components/modals/export-modal/wp-table-export.modal'; +import { WpButtonMacroModalComponent } from 'core-app/shared/components/modals/editor/macro-wp-button-modal/wp-button-macro.modal'; +import { QuerySharingModalComponent } from 'core-app/shared/components/modals/share-modal/query-sharing.modal'; +import { SaveQueryModalComponent } from 'core-app/shared/components/modals/save-modal/save-query.modal'; +import { QuerySharingFormComponent } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; +import { WpDestroyModalComponent } from 'core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal'; +import { WorkPackageTypeStatusComponent } from 'core-app/features/work-packages/components/wp-type-status/wp-type-status.component'; +import { WorkPackageIsolatedQuerySpaceDirective } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; +import { WorkPackageBreadcrumbParentComponent } from 'core-app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component'; +import { WorkPackageSubjectComponent } from 'core-app/features/work-packages/components/wp-subject/wp-subject.component'; +import { WorkPackageBreadcrumbComponent } from 'core-app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component'; +import { UserLinkComponent } from 'core-app/shared/components/user-link/user-link.component'; +import { WorkPackageCommentComponent } from 'core-app/features/work-packages/components/work-package-comment/work-package-comment.component'; +import { WorkPackageWatcherButtonComponent } from 'core-app/features/work-packages/components/wp-watcher-button/wp-watcher-button.component'; +import { WorkPackageCommentFieldComponent } from 'core-app/features/work-packages/components/work-package-comment/wp-comment-field.component'; +import { WpResizerDirective } from 'core-app/shared/components/resizer/resizer/wp-resizer.component'; import { GroupDescriptor, WorkPackageSingleViewComponent, -} from "core-app/features/work-packages/components/wp-single-view/wp-single-view.component"; -import { WorkPackageIsolatedGraphQuerySpaceDirective } from "core-app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive"; -import { RevisionActivityComponent } from "core-app/features/work-packages/components/wp-activity/revision/revision-activity.component"; -import { WorkPackageCopySplitViewComponent } from "core-app/features/work-packages/components/wp-copy/wp-copy-split-view.component"; -import { WorkPackageFormAttributeGroupComponent } from "core-app/features/work-packages/components/wp-form-group/wp-attribute-group.component"; -import { WorkPackagesGridComponent } from "core-app/features/work-packages/components/wp-grid/wp-grid.component"; -import { ActivityEntryComponent } from "core-app/features/work-packages/components/wp-activity/activity-entry.component"; -import { ActivityLinkComponent } from "core-app/features/work-packages/components/wp-activity/activity-link.component"; -import { UserActivityComponent } from "core-app/features/work-packages/components/wp-activity/user/user-activity.component"; -import { WorkPackageSplitViewToolbarComponent } from "core-app/features/work-packages/components/wp-details/wp-details-toolbar.component"; -import { WorkPackageCopyFullViewComponent } from "core-app/features/work-packages/components/wp-copy/wp-copy-full-view.component"; -import { OpenprojectTabsModule } from "core-app/shared/components/tabs/openproject-tabs.module"; -import { TimeEntryChangeset } from "core-app/features/work-packages/helpers/time-entries/time-entry-changeset"; -import { QueryFiltersComponent } from "core-app/features/work-packages/components/filters/query-filters/query-filters.component"; -import { FilterDateTimesValueComponent } from "core-app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component"; -import { FilterSearchableMultiselectValueComponent } from "core-app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component"; -import { QueryFilterComponent } from "core-app/features/work-packages/components/filters/query-filter/query-filter.component"; -import { FilterDatesValueComponent } from "core-app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component"; -import { FilterStringValueComponent } from "core-app/features/work-packages/components/filters/filter-string-value/filter-string-value.component"; -import { FilterDateValueComponent } from "core-app/features/work-packages/components/filters/filter-date-value/filter-date-value.component"; -import { FilterDateTimeValueComponent } from "core-app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component"; -import { FilterToggledMultiselectValueComponent } from "core-app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component"; -import { WorkPackageFilterByTextInputComponent } from "core-app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component"; -import { FilterIntegerValueComponent } from "core-app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component"; -import { WorkPackageFilterContainerComponent } from "core-app/features/work-packages/components/filters/filter-container/filter-container.directive"; -import { FilterBooleanValueComponent } from "core-app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component"; - +} from 'core-app/features/work-packages/components/wp-single-view/wp-single-view.component'; +import { WorkPackageIsolatedGraphQuerySpaceDirective } from 'core-app/features/work-packages/directives/query-space/wp-isolated-graph-query-space.directive'; +import { RevisionActivityComponent } from 'core-app/features/work-packages/components/wp-activity/revision/revision-activity.component'; +import { WorkPackageCopySplitViewComponent } from 'core-app/features/work-packages/components/wp-copy/wp-copy-split-view.component'; +import { WorkPackageFormAttributeGroupComponent } from 'core-app/features/work-packages/components/wp-form-group/wp-attribute-group.component'; +import { WorkPackagesGridComponent } from 'core-app/features/work-packages/components/wp-grid/wp-grid.component'; +import { ActivityEntryComponent } from 'core-app/features/work-packages/components/wp-activity/activity-entry.component'; +import { ActivityLinkComponent } from 'core-app/features/work-packages/components/wp-activity/activity-link.component'; +import { UserActivityComponent } from 'core-app/features/work-packages/components/wp-activity/user/user-activity.component'; +import { WorkPackageSplitViewToolbarComponent } from 'core-app/features/work-packages/components/wp-details/wp-details-toolbar.component'; +import { WorkPackageCopyFullViewComponent } from 'core-app/features/work-packages/components/wp-copy/wp-copy-full-view.component'; +import { OpenprojectTabsModule } from 'core-app/shared/components/tabs/openproject-tabs.module'; +import { TimeEntryChangeset } from 'core-app/features/work-packages/helpers/time-entries/time-entry-changeset'; +import { QueryFiltersComponent } from 'core-app/features/work-packages/components/filters/query-filters/query-filters.component'; +import { FilterDateTimesValueComponent } from 'core-app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component'; +import { FilterSearchableMultiselectValueComponent } from 'core-app/features/work-packages/components/filters/filter-searchable-multiselect-value/filter-searchable-multiselect-value.component'; +import { QueryFilterComponent } from 'core-app/features/work-packages/components/filters/query-filter/query-filter.component'; +import { FilterDatesValueComponent } from 'core-app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component'; +import { FilterStringValueComponent } from 'core-app/features/work-packages/components/filters/filter-string-value/filter-string-value.component'; +import { FilterDateValueComponent } from 'core-app/features/work-packages/components/filters/filter-date-value/filter-date-value.component'; +import { FilterDateTimeValueComponent } from 'core-app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component'; +import { FilterToggledMultiselectValueComponent } from 'core-app/features/work-packages/components/filters/filter-toggled-multiselect-value/filter-toggled-multiselect-value.component'; +import { WorkPackageFilterByTextInputComponent } from 'core-app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component'; +import { FilterIntegerValueComponent } from 'core-app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component'; +import { WorkPackageFilterContainerComponent } from 'core-app/features/work-packages/components/filters/filter-container/filter-container.directive'; +import { FilterBooleanValueComponent } from 'core-app/features/work-packages/components/filters/filter-boolean-value/filter-boolean-value.component'; @NgModule({ imports: [ @@ -264,7 +263,7 @@ import { FilterBooleanValueComponent } from "core-app/features/work-packages/com WorkPackagesGridComponent, WorkPackagesTableComponent, - WorkPackagesTableConfigMenu, + WorkPackagesTableConfigMenuComponent, WorkPackageTablePaginationComponent, WpResizerDirective, @@ -344,10 +343,9 @@ import { FilterBooleanValueComponent } from "core-app/features/work-packages/com WorkPackageRelationRowComponent, WorkPackageRelationsCreateComponent, WorkPackageRelationsHierarchyComponent, - WorkPackageRelationsAutocomplete, + WorkPackageRelationsAutocompleteComponent, WorkPackageBreadcrumbParentComponent, - // Split view WorkPackageDetailsViewButtonComponent, WorkPackageSplitViewComponent, @@ -361,19 +359,19 @@ import { FilterBooleanValueComponent } from "core-app/features/work-packages/com // Modals WpTableConfigurationModalComponent, - WpTableConfigurationColumnsTab, - WpTableConfigurationDisplaySettingsTab, + WpTableConfigurationColumnsTabComponent, + WpTableConfigurationDisplaySettingsTabComponent, WpTableConfigurationFiltersTab, - WpTableConfigurationSortByTab, - WpTableConfigurationTimelinesTab, - WpTableConfigurationHighlightingTab, + WpTableConfigurationSortByTabComponent, + WpTableConfigurationTimelinesTabComponent, + WpTableConfigurationHighlightingTabComponent, WpTableConfigurationRelationSelectorComponent, - WpTableExportModal, - QuerySharingForm, - QuerySharingModal, - SaveQueryModal, - WpDestroyModal, - DatePickerModal, + WpTableExportModalComponent, + QuerySharingFormComponent, + QuerySharingModalComponent, + SaveQueryModalComponent, + WpDestroyModalComponent, + DatePickerModalComponent, // CustomActions WpCustomActionComponent, @@ -383,13 +381,12 @@ import { FilterBooleanValueComponent } from "core-app/features/work-packages/com // CKEditor macros which could not be included in the // editor module to avoid circular dependencies EmbeddedTablesMacroComponent, - WpButtonMacroModal, + WpButtonMacroModalComponent, // Card view WorkPackageCardViewComponent, WorkPackageSingleCardComponent, - WorkPackageViewToggleButton, - + WorkPackageViewToggleButtonComponent, ], exports: [ @@ -423,7 +420,7 @@ import { FilterBooleanValueComponent } from "core-app/features/work-packages/com WorkPackageSingleViewComponent, WorkPackageSplitViewComponent, BackButtonComponent, - ] + ], }) export class OpenprojectWorkPackagesModule { static bootstrapAttributeGroupsCalled = false; @@ -447,22 +444,17 @@ export class OpenprojectWorkPackagesModule { hookService.register('attributeGroupComponent', (group:GroupDescriptor, workPackage:WorkPackageResource) => { if (group.type === 'WorkPackageFormAttributeGroup') { return WorkPackageFormAttributeGroupComponent; - } else if (!workPackage.isNew && group.type === 'WorkPackageFormChildrenQueryGroup') { + } if (!workPackage.isNew && group.type === 'WorkPackageFormChildrenQueryGroup') { return WorkPackageChildrenQueryComponent; - } else if (!workPackage.isNew && group.type === 'WorkPackageFormRelationQueryGroup') { + } if (!workPackage.isNew && group.type === 'WorkPackageFormRelationQueryGroup') { return WorkPackageRelationQueryComponent; - } else { - return null; } + return null; }); - hookService.register('workPackageAttachmentUploadComponent', (workPackage:WorkPackageResource) => { - return AttachmentsUploadComponent; - }); + hookService.register('workPackageAttachmentUploadComponent', (workPackage:WorkPackageResource) => AttachmentsUploadComponent); - hookService.register('workPackageAttachmentListComponent', (workPackage:WorkPackageResource) => { - return AttachmentListComponent; - }); + hookService.register('workPackageAttachmentListComponent', (workPackage:WorkPackageResource) => AttachmentListComponent); /** Return specialized work package changeset for editing service */ hookService.register('halResourceChangesetClass', (resource:HalResource) => { diff --git a/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.ts b/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.ts index 15815e2e57..a4d115b769 100644 --- a/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.ts +++ b/frontend/src/app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from "@angular/core"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { OpTitleService } from "core-app/core/html/op-title.service"; -import { WorkPackagesViewBase } from "core-app/features/work-packages/routing/wp-view-base/work-packages-view.base"; -import { take } from "rxjs/operators"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { QueryParamListenerService } from "core-app/features/work-packages/components/wp-query/query-param-listener.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { ComponentType } from "@angular/cdk/overlay"; -import { Ng2StateDeclaration } from "@uirouter/angular"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { + ChangeDetectionStrategy, Component, OnDestroy, OnInit, +} from '@angular/core'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { OpTitleService } from 'core-app/core/html/op-title.service'; +import { WorkPackagesViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-packages-view.base'; +import { take } from 'rxjs/operators'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { QueryParamListenerService } from 'core-app/features/work-packages/components/wp-query/query-param-listener.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { ComponentType } from '@angular/cdk/overlay'; +import { Ng2StateDeclaration } from '@uirouter/angular'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { InviteUserModalComponent } from 'core-app/features/invite-user-modal/invite-user.component'; -import { WorkPackageFilterContainerComponent } from "core-app/features/work-packages/components/filters/filter-container/filter-container.directive"; +import { WorkPackageFilterContainerComponent } from 'core-app/features/work-packages/components/filters/filter-container/filter-container.directive'; export interface DynamicComponentDefinition { component:ComponentType; @@ -63,18 +65,21 @@ export type ViewPartitionState = '-split'|'-left-only'|'-right-only'; providers: [ /** We need to provide the wpNotification service here to get correct save notifications for WP resources */ { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService }, - QueryParamListenerService - ] + QueryParamListenerService, + ], }) export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase implements OnInit, OnDestroy { @InjectField() I18n!:I18nService; + @InjectField() titleService:OpTitleService; + @InjectField() queryParamListener:QueryParamListenerService; + @InjectField() opModalService:OpModalService; text:{ [key:string]:string } = { - 'jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.pagination'), - 'text_jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.label_pagination'), + jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.pagination'), + text_jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.label_pagination'), }; /** Whether the title can be edited */ @@ -82,6 +87,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp /** Current query title to render */ selectedTitle?:string; + currentQuery:QueryResource|undefined; /** Whether we're saving the query */ @@ -92,6 +98,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp /** Listener callbacks */ unRegisterTitleListener:Function; + removeTransitionSubscription:Function; /** Determine when query is initially loaded */ @@ -111,7 +118,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp /** Which filter container component to mount */ filterContainerDefinition:DynamicComponentDefinition = { - component: WorkPackageFilterContainerComponent + component: WorkPackageFilterContainerComponent, }; ngOnInit() { @@ -138,7 +145,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp this.queryParamListener .observe$ .pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe(() => { /** Ensure we reload the query from the changed props */ this.currentQuery = undefined; @@ -151,7 +158,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp }); this.querySpace.query.values$().pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((query) => { this.onQueryUpdated(query); }); @@ -214,13 +221,11 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp } updateTitle(query?:QueryResource) { - // Too early for loaded query if (!query) { return; } - if (query.persisted) { this.selectedTitle = query.name; } else { @@ -231,7 +236,7 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp // Update the title if we're in the list state alone if (this.shouldUpdateHtmlTitle()) { - this.titleService.setFirstPart(this.selectedTitle!); + this.titleService.setFirstPart(this.selectedTitle); } } @@ -272,9 +277,8 @@ export class PartitionedQuerySpacePageComponent extends WorkPackagesViewBase imp protected loadFirstPage():Promise { if (this.currentQuery) { return this.wpListService.reloadQuery(this.currentQuery, this.projectIdentifier).toPromise(); - } else { - return this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier); } + return this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier); } protected additionalLoadingTime():Promise { diff --git a/frontend/src/app/features/work-packages/routing/split-view-routes.helper.ts b/frontend/src/app/features/work-packages/routing/split-view-routes.helper.ts index 8cc73c3348..31d734e34f 100644 --- a/frontend/src/app/features/work-packages/routing/split-view-routes.helper.ts +++ b/frontend/src/app/features/work-packages/routing/split-view-routes.helper.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { StateService } from "@uirouter/angular"; +import { StateService } from '@uirouter/angular'; /** * Returns the path to the split view based on the current route @@ -35,5 +35,5 @@ import { StateService } from "@uirouter/angular"; */ export function splitViewRoute(state:StateService):string { const baseRoute = state.current.data.baseRoute || ''; - return baseRoute + '.details'; + return `${baseRoute}.details`; } diff --git a/frontend/src/app/features/work-packages/routing/split-view-routes.template.ts b/frontend/src/app/features/work-packages/routing/split-view-routes.template.ts index 13c6e1d854..bda6218a43 100644 --- a/frontend/src/app/features/work-packages/routing/split-view-routes.template.ts +++ b/frontend/src/app/features/work-packages/routing/split-view-routes.template.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,7 +30,7 @@ import { WorkPackageNewSplitViewComponent } from 'core-app/features/work-package import { Ng2StateDeclaration } from '@uirouter/angular'; import { ComponentType } from '@angular/cdk/overlay'; import { WpTabWrapperComponent } from 'core-app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component'; -import { WorkPackageCopySplitViewComponent } from "core-app/features/work-packages/components/wp-copy/wp-copy-split-view.component"; +import { WorkPackageCopySplitViewComponent } from 'core-app/features/work-packages/components/wp-copy/wp-copy-split-view.component'; /** * Return a set of routes for a split view mounted under the given base route, @@ -52,26 +52,26 @@ import { WorkPackageCopySplitViewComponent } from "core-app/features/work-packag * @param showComponent The split view component to mount */ export function makeSplitViewRoutes(baseRoute:string, - menuItemClass:string|undefined, - showComponent:ComponentType, - newComponent:ComponentType = WorkPackageNewSplitViewComponent, - makeFullWidth?:boolean, - routeName = baseRoute):Ng2StateDeclaration[] { + menuItemClass:string|undefined, + showComponent:ComponentType, + newComponent:ComponentType = WorkPackageNewSplitViewComponent, + makeFullWidth?:boolean, + routeName = baseRoute):Ng2StateDeclaration[] { // makeFullWidth configuration - const views:any = makeFullWidth ? - { 'content-left@^.^': { component: showComponent } } : - { 'content-right@^.^': { component: showComponent } }; + const views:any = makeFullWidth + ? { 'content-left@^.^': { component: showComponent } } + : { 'content-right@^.^': { component: showComponent } }; const partition = makeFullWidth ? '-left-only' : '-split'; return [ { - name: routeName + '.details', + name: `${routeName}.details`, url: '/details/{workPackageId:[0-9]+}', redirectTo: (trans) => { const params = trans.params('to'); return { - state: routeName + '.details.tabs', - params: { ...params, tabIdentifier: 'overview' } + state: `${routeName}.details.tabs`, + params: { ...params, tabIdentifier: 'overview' }, }; }, reloadOnSearch: false, @@ -79,8 +79,8 @@ export function makeSplitViewRoutes(baseRoute:string, bodyClasses: 'router--work-packages-partitioned-split-view-details', menuItem: menuItemClass, // Remember the base route so we can route back to it anywhere - baseRoute: baseRoute, - newRoute: routeName + '.new', + baseRoute, + newRoute: `${routeName}.new`, partition, }, // Retarget and by that override the grandparent views @@ -88,18 +88,18 @@ export function makeSplitViewRoutes(baseRoute:string, views, }, { - name: routeName + '.details.tabs', + name: `${routeName}.details.tabs`, url: '/:tabIdentifier', component: WpTabWrapperComponent, data: { - baseRoute: baseRoute, + baseRoute, menuItem: menuItemClass, - parent: routeName + '.details' - } + parent: `${routeName}.details`, + }, }, // Split create route { - name: routeName + '.new', + name: `${routeName}.new`, url: '/create_new?{type:[0-9]+}&{parent_id:[0-9]+}', reloadOnSearch: false, data: { @@ -107,30 +107,30 @@ export function makeSplitViewRoutes(baseRoute:string, allowMovingInEditMode: true, bodyClasses: 'router--work-packages-partitioned-split-view-new', // Remember the base route so we can route back to it anywhere - baseRoute: baseRoute, - parent: baseRoute + baseRoute, + parent: baseRoute, }, views: { // Retarget and by that override the grandparent views // https://ui-router.github.io/guide/views#relative-parent-state - 'content-right@^.^': { component: newComponent } - } + 'content-right@^.^': { component: newComponent }, + }, }, // Split copy route { - name: routeName + '.copy', + name: `${routeName}.copy`, url: '/details/{copiedFromWorkPackageId:[0-9]+}/copy', views: { - 'content-right@^.^': { component: WorkPackageCopySplitViewComponent } + 'content-right@^.^': { component: WorkPackageCopySplitViewComponent }, }, reloadOnSearch: false, data: { - baseRoute: baseRoute, + baseRoute, parent: baseRoute, allowMovingInEditMode: true, bodyClasses: 'router--work-packages-partitioned-split-view', menuItem: menuItemClass, - partition: '-split' + partition: '-split', }, }, ]; diff --git a/frontend/src/app/features/work-packages/routing/work-packages-routes.ts b/frontend/src/app/features/work-packages/routing/work-packages-routes.ts index e67ff38cad..b3fd246529 100644 --- a/frontend/src/app/features/work-packages/routing/work-packages-routes.ts +++ b/frontend/src/app/features/work-packages/routing/work-packages-routes.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -35,7 +35,7 @@ import { WorkPackagesBaseComponent } from 'core-app/features/work-packages/routi import { WorkPackageListViewComponent } from 'core-app/features/work-packages/routing/wp-list-view/wp-list-view.component'; import { WorkPackageViewPageComponent } from 'core-app/features/work-packages/routing/wp-view-page/wp-view-page.component'; import { makeSplitViewRoutes } from 'core-app/features/work-packages/routing/split-view-routes.template'; -import { WorkPackageCopyFullViewComponent } from "core-app/features/work-packages/components/wp-copy/wp-copy-full-view.component"; +import { WorkPackageCopyFullViewComponent } from 'core-app/features/work-packages/components/wp-copy/wp-copy-full-view.component'; export const menuItemClass = 'work-packages-menu-item'; @@ -48,7 +48,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ redirectTo: 'work-packages.partitioned.list', data: { bodyClasses: 'router--work-packages-base', - menuItem: menuItemClass + menuItem: menuItemClass, }, params: { query_id: { type: 'query', dynamic: true }, @@ -56,7 +56,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ query_props: { type: 'opQueryString' }, // Optional initial tour param start_onboarding_tour: { type: 'query', squash: true, value: undefined }, - } + }, }, { name: 'work-packages.new', @@ -67,7 +67,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ baseRoute: 'work-packages', allowMovingInEditMode: true, bodyClasses: 'router--work-packages-full-create', - menuItem: menuItemClass + menuItem: menuItemClass, }, }, { @@ -79,7 +79,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ baseRoute: 'work-packages', allowMovingInEditMode: true, bodyClasses: 'router--work-packages-full-create', - menuItem: menuItemClass + menuItem: menuItemClass, }, }, { @@ -90,7 +90,7 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ const params = trans.params('to'); return { state: 'work-packages.show.tabs', - params: { ...params, tabIdentifier: 'activity' } + params: { ...params, tabIdentifier: 'activity' }, }; }, component: WorkPackagesFullViewComponent, @@ -98,17 +98,17 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ baseRoute: 'work-packages', bodyClasses: 'router--work-packages-full-view', newRoute: 'work-packages.new', - menuItem: menuItemClass - } + menuItem: menuItemClass, + }, }, { name: 'work-packages.show.tabs', - url: "/:tabIdentifier", + url: '/:tabIdentifier', component: WpTabWrapperComponent, data: { parent: 'work-packages.show', menuItem: menuItemClass, - } + }, }, { name: 'work-packages.partitioned', @@ -116,27 +116,27 @@ export const WORK_PACKAGES_ROUTES:Ng2StateDeclaration[] = [ url: '', data: { // This has to be empty to avoid inheriting the parent bodyClasses - bodyClasses: '' - } + bodyClasses: '', + }, }, { name: 'work-packages.partitioned.list', url: '', reloadOnSearch: false, views: { - 'content-left': { component: WorkPackageListViewComponent } + 'content-left': { component: WorkPackageListViewComponent }, }, data: { bodyClasses: 'router--work-packages-partitioned-split-view', menuItem: menuItemClass, - partition: '-left-only' - } + partition: '-left-only', + }, }, ...makeSplitViewRoutes( 'work-packages.partitioned.list', menuItemClass, - WorkPackageSplitViewComponent - ) + WorkPackageSplitViewComponent, + ), // Avoid lazy-loading the routes for now // { // name: 'work-packages.calendar.**', diff --git a/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts b/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts index 129f14532e..ed1746990a 100644 --- a/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { EditFormRoutingService } from "core-app/shared/components/fields/edit/edit-form/edit-form-routing.service"; -import { WorkPackageEditFormRoutingService } from "core-app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service"; +import { Component } from '@angular/core'; +import { EditFormRoutingService } from 'core-app/shared/components/fields/edit/edit-form/edit-form-routing.service'; +import { WorkPackageEditFormRoutingService } from 'core-app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service'; export const wpBaseSelector = 'work-packages-base'; @@ -40,8 +40,8 @@ export const wpBaseSelector = 'work-packages-base';
  • `, providers: [ - { provide: EditFormRoutingService, useClass: WorkPackageEditFormRoutingService } - ] + { provide: EditFormRoutingService, useClass: WorkPackageEditFormRoutingService }, + ], }) export class WorkPackagesBaseComponent { } diff --git a/frontend/src/app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service.ts b/frontend/src/app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service.ts index 6474ef6442..c04007fe82 100644 --- a/frontend/src/app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Transition } from "@uirouter/core"; -import { Injectable } from "@angular/core"; -import { EditFormRoutingService } from "core-app/shared/components/fields/edit/edit-form/edit-form-routing.service"; +import { Transition } from '@uirouter/core'; +import { Injectable } from '@angular/core'; +import { EditFormRoutingService } from 'core-app/shared/components/fields/edit/edit-form/edit-form-routing.service'; @Injectable() export class WorkPackageEditFormRoutingService extends EditFormRoutingService { @@ -45,7 +45,7 @@ export class WorkPackageEditFormRoutingService extends EditFormRoutingService { const toParams = transition.params('to'); // In new/copy mode, transitions to the same controller are allowed - if (fromState.name && fromState.name.match(/\.(new|copy)$/)) { + if (fromState.name && (/\.(new|copy)$/.exec(fromState.name))) { return !(toState.data && toState.data.allowMovingInEditMode); } @@ -53,4 +53,3 @@ export class WorkPackageEditFormRoutingService extends EditFormRoutingService { return toParams.workPackageId === undefined || toParams.workPackageId !== fromParams.workPackageId; } } - diff --git a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts index 3cc650d4ee..5e3c836753 100644 --- a/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-full-view/wp-full-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,36 +26,38 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { StateService } from '@uirouter/core'; import { Component, Injector, OnInit } from '@angular/core'; import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; -import { WorkPackageSingleViewBase } from "core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base"; -import { of } from "rxjs"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; +import { WorkPackageSingleViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base'; +import { of } from 'rxjs'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; @Component({ templateUrl: './wp-full-view.html', selector: 'wp-full-view-entry', // Required class to support inner scrolling on page - host: { 'class': 'work-packages-page--ui-view' }, + host: { class: 'work-packages-page--ui-view' }, providers: [ - { provide: HalResourceNotificationService, useExisting: WorkPackageNotificationService } - ] + { provide: HalResourceNotificationService, useExisting: WorkPackageNotificationService }, + ], }) export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase implements OnInit { // Watcher properties public isWatched:boolean; + public displayWatchButton:boolean; + public watchers:any; stateName$ = of('work-packages.new'); constructor(public injector:Injector, - public wpTableSelection:WorkPackageViewSelectionService, - readonly $state:StateService) { - super(injector, $state.params['workPackageId']); + public wpTableSelection:WorkPackageViewSelectionService, + readonly $state:StateService) { + super(injector, $state.params.workPackageId); } ngOnInit():void { @@ -66,7 +68,7 @@ export class WorkPackagesFullViewComponent extends WorkPackageSingleViewBase imp super.initializeTexts(); this.text.full_view = { - button_more: this.I18n.t('js.button_more') + button_more: this.I18n.t('js.button_more'), }; } diff --git a/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts index 0c3ba07dab..47485bcd67 100644 --- a/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -33,46 +33,45 @@ import { Injector, OnInit, ElementRef, - NgZone -} from "@angular/core"; -import { take } from "rxjs/operators"; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { DragAndDropService } from "core-app/shared/helpers/drag-and-drop/drag-and-drop.service"; + NgZone, +} from '@angular/core'; +import { take } from 'rxjs/operators'; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { DragAndDropService } from 'core-app/shared/helpers/drag-and-drop/drag-and-drop.service'; import { WorkPackageViewDisplayRepresentationService, - wpDisplayCardRepresentation -} from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { WorkPackageTableConfigurationObject } from "core-app/features/work-packages/components/wp-table/wp-table-configuration"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { StateService } from "@uirouter/core"; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; + wpDisplayCardRepresentation, +} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { WorkPackageTableConfigurationObject } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { StateService } from '@uirouter/core'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; @Component({ selector: 'wp-list-view', templateUrl: './wp-list-view.component.html', styleUrls: ['./wp-list-view.component.sass'], - host: { 'class': 'work-packages-split-view--tabletimeline-side' }, + host: { class: 'work-packages-split-view--tabletimeline-side' }, changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService }, DragAndDropService, - CausedUpdatesService - ] + CausedUpdatesService, + ], }) export class WorkPackageListViewComponent extends UntilDestroyedMixin implements OnInit { - text = { - 'jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.pagination'), - 'text_jump_to_pagination': this.I18n.t('js.work_packages.jump_marks.label_pagination'), - 'button_settings': this.I18n.t('js.button_settings') + jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.pagination'), + text_jump_to_pagination: this.I18n.t('js.work_packages.jump_marks.label_pagination'), + button_settings: this.I18n.t('js.button_settings'), }; /** Switch between list and card view */ @@ -89,21 +88,21 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements /** */ readonly wpTableConfiguration:WorkPackageTableConfigurationObject = { - dragAndDropEnabled: true + dragAndDropEnabled: true, }; constructor(readonly I18n:I18nService, - readonly injector:Injector, - readonly $state:StateService, - readonly keepTab:KeepTabService, - readonly querySpace:IsolatedQuerySpace, - readonly wpViewFilters:WorkPackageViewFiltersService, - readonly deviceService:DeviceService, - readonly CurrentProject:CurrentProjectService, - readonly wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - private ngZone:NgZone) { + readonly injector:Injector, + readonly $state:StateService, + readonly keepTab:KeepTabService, + readonly querySpace:IsolatedQuerySpace, + readonly wpViewFilters:WorkPackageViewFiltersService, + readonly deviceService:DeviceService, + readonly CurrentProject:CurrentProjectService, + readonly wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, + readonly cdRef:ChangeDetectorRef, + readonly elementRef:ElementRef, + private ngZone:NgZone) { super(); } @@ -112,7 +111,7 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements this.setupInformationLoadedListener(); this.querySpace.query.values$().pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((query) => { // Update the visible representation this.updateViewRepresentation(query); @@ -135,11 +134,11 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements // The header of the table hides the scrolledIntoView element // so we scrollIntoView the previous element, if any if (selectedRow && selectedRow.previousSibling) { - selectedRow.previousSibling.scrollIntoView({ block: "start" }); + selectedRow.previousSibling.scrollIntoView({ block: 'start' }); } if (selectedCard) { - selectedCard.scrollIntoView({ block: "start" }); + selectedCard.scrollIntoView({ block: 'start' }); } }, 0); }); @@ -162,8 +161,8 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements } protected updateViewRepresentation(query:QueryResource) { - this.showTableView = !(this.deviceService.isMobile || - this.wpDisplayRepresentation.valueFromQuery(query) === wpDisplayCardRepresentation); + this.showTableView = !(this.deviceService.isMobile + || this.wpDisplayRepresentation.valueFromQuery(query) === wpDisplayCardRepresentation); } handleWorkPackageClicked(event:{ workPackageId:string; double:boolean }) { @@ -200,7 +199,7 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements private openInFullView(workPackageId:string) { this.$state.go( 'work-packages.show', - { workPackageId: workPackageId } + { workPackageId }, ); } } diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts index b91c425309..446bc76749 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,46 +26,47 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, Component, Injector, OnInit, +} from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { States } from "core-app/core/states/states.service"; -import { FirstRouteService } from "core-app/core/routing/first-route-service"; -import { KeepTabService } from "core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service"; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageSingleViewBase } from "core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { BackRoutingService } from "core-app/features/work-packages/components/back-routing/back-routing.service"; +import { States } from 'core-app/core/states/states.service'; +import { FirstRouteService } from 'core-app/core/routing/first-route-service'; +import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageSingleViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-package-single-view.base'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { BackRoutingService } from 'core-app/features/work-packages/components/back-routing/back-routing.service'; @Component({ templateUrl: './wp-split-view.html', changeDetection: ChangeDetectionStrategy.OnPush, selector: 'wp-split-view-entry', providers: [ - { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService } - ] + { provide: HalResourceNotificationService, useClass: WorkPackageNotificationService }, + ], }) export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase implements OnInit { - /** Reference to the base route e.g., work-packages.partitioned.list or bim.partitioned.split */ private baseRoute:string = this.$state.current.data.baseRoute; constructor(public injector:Injector, - public states:States, - public firstRoute:FirstRouteService, - public keepTab:KeepTabService, - public wpTableSelection:WorkPackageViewSelectionService, - public wpTableFocus:WorkPackageViewFocusService, - readonly $state:StateService, - readonly backRouting:BackRoutingService) { - super(injector, $state.params['workPackageId']); + public states:States, + public firstRoute:FirstRouteService, + public keepTab:KeepTabService, + public wpTableSelection:WorkPackageViewSelectionService, + public wpTableFocus:WorkPackageViewFocusService, + readonly $state:StateService, + readonly backRouting:BackRoutingService) { + super(injector, $state.params.workPackageId); } ngOnInit():void { this.observeWorkPackage(); - const wpId = this.$state.params['workPackageId']; + const wpId = this.$state.params.workPackageId; const focusedWP = this.wpTableFocus.focusedWorkPackage; if (!focusedWP) { @@ -83,14 +84,14 @@ export class WorkPackageSplitViewComponent extends WorkPackageSingleViewBase imp this.wpTableFocus.whenChanged() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(newId => { + .subscribe((newId) => { const idSame = wpId.toString() === newId.toString(); if (!idSame && this.$state.includes(`${this.baseRoute}.details`)) { this.$state.go( (this.$state.current.name as string), - { workPackageId: newId, focus: false } + { workPackageId: newId, focus: false }, ); } }); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry.ts index b66aca1feb..292a8b348b 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/event-handling/event-handler-registry.ts @@ -1,7 +1,7 @@ import { EventEmitter, InjectionToken, Injector } from '@angular/core'; export interface WorkPackageViewEventHandler { - /** Event name to register **/ + /** Event name to register * */ EVENT:string; /** Event context CSS selector */ @@ -31,14 +31,13 @@ export const WorkPackageViewHandlerToken = new InjectionToken { - constructor(public readonly injector:Injector) { } protected abstract eventHandlers:((view:T) => WorkPackageViewEventHandler)[]; attachTo(viewRef:T) { - this.eventHandlers.map(factory => { + this.eventHandlers.map((factory) => { const handler = factory(viewRef); const target = handler.eventScope(viewRef); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/typings.d.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/typings.d.ts index 9e1f1cdcdd..8dd9364763 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/typings.d.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/typings.d.ts @@ -1,8 +1,8 @@ interface IGroupsCollapseEvent { - state:{[identifier:string]:boolean}; + state:{ [identifier:string]:boolean }; allGroupsAreCollapsed:boolean; allGroupsAreExpanded:boolean; lastChangedGroup:string|null; allGroupsChanged:boolean; groupedBy:string|null; -} \ No newline at end of file +} diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies.ts index 8e7adbbee3..ddaded49fb 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,8 +28,10 @@ export class WorkPackageViewHierarchies { public isVisible = false; + public last:string|null = null; - public collapsed:{[workPackageId:string]:boolean} = {}; + + public collapsed:{ [workPackageId:string]:boolean } = {}; constructor(visible:boolean) { this.isVisible = visible; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight.ts index 82c2259ee7..ab4b8c5c94 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HighlightingMode } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HighlightingMode } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting-mode.const'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export interface WorkPackageViewHighlight { mode:HighlightingMode; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination.ts index 6447bb2a13..e8aba7be89 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { PaginationInstance } from "core-app/shared/components/table-pagination/pagination-instance"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { PaginationInstance } from 'core-app/shared/components/table-pagination/pagination-instance'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; export class WorkPackageViewPagination { public current:PaginationInstance; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns.ts index b019cad6e1..f56e7453bc 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline.ts index b1fa59ab99..8df671fb09 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { TimelineLabels, TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; +import { TimelineLabels, TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; export interface WorkPackageTimelineState { visible:boolean; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts index 9fb965b973..6048dd97fb 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-additional-elements.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,31 +26,30 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageViewColumnsService } from './wp-view-columns.service'; -import { WorkPackageViewHierarchiesService } from './wp-view-hierarchy.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; -import { RelationsStateValue, WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { RelationsStateValue, WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { WorkPackageViewHierarchiesService } from './wp-view-hierarchy.service'; +import { WorkPackageViewColumnsService } from './wp-view-columns.service'; @Injectable() export class WorkPackageViewAdditionalElementsService { - constructor(readonly querySpace:IsolatedQuerySpace, - readonly wpTableHierarchies:WorkPackageViewHierarchiesService, - readonly wpTableColumns:WorkPackageViewColumnsService, - readonly notificationService:WorkPackageNotificationService, - readonly halResourceService:HalResourceService, - readonly apiV3Service:APIV3Service, - readonly schemaCache:SchemaCacheService, - readonly wpRelations:WorkPackageRelationsService) { + readonly wpTableHierarchies:WorkPackageViewHierarchiesService, + readonly wpTableColumns:WorkPackageViewColumnsService, + readonly notificationService:WorkPackageNotificationService, + readonly halResourceService:HalResourceService, + readonly apiV3Service:APIV3Service, + readonly schemaCache:SchemaCacheService, + readonly wpRelations:WorkPackageRelationsService) { } public initialize(query:QueryResource, results:WorkPackageCollectionResource) { @@ -58,9 +57,9 @@ export class WorkPackageViewAdditionalElementsService { // Add relations to the stack Promise.all([ - this.requireInvolvedRelations(rows.map(el => el.id!)), + this.requireInvolvedRelations(rows.map((el) => el.id!)), this.requireHierarchyElements(rows), - this.requireSumsSchema(results) + this.requireSumsSchema(results), ]).then((results:string[][]) => { this.loadAdditional(_.flatten(results)); }); @@ -85,16 +84,13 @@ export class WorkPackageViewAdditionalElementsService { * as the `to` work packages returned from the relations */ private requireInvolvedRelations(rows:string[]):Promise { - if (!this.wpTableColumns.hasRelationColumns()) { return Promise.resolve([]); } return this.wpRelations .requireAll(rows) .then(() => { - const ids = this.getInvolvedWorkPackages(rows.map(id => { - return this.wpRelations.state(id).value!; - })); + const ids = this.getInvolvedWorkPackages(rows.map((id) => this.wpRelations.state(id).value!)); return _.flatten(ids); }); } @@ -109,7 +105,7 @@ export class WorkPackageViewAdditionalElementsService { return Promise.resolve([]); } - const ids = _.flatten(rows.map(el => el.ancestorIds)); + const ids = _.flatten(rows.map((el) => el.ancestorIds)); return Promise.resolve(ids); } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts index a3eede8417..54c54c7dff 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { combine, deriveRaw, input, State } from 'reactivestates'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { + combine, deriveRaw, input, State, +} from 'reactivestates'; import { map, mapTo, take } from 'rxjs/operators'; import { merge, Observable } from 'rxjs'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { Injectable } from "@angular/core"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { Injectable } from '@angular/core'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; @Injectable() export abstract class WorkPackageViewBaseService { @@ -125,7 +127,7 @@ export abstract class WorkPackageViewBaseService { .values$() .pipe( take(1), - mapTo(null) + mapTo(null), ) .toPromise(); } @@ -142,9 +144,8 @@ export abstract class WorkPackageViewBaseService { return pristine; } return current; - }) - ) - ); + }), + )); } /** diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts index 74dcfda59e..676db33f29 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-collapsed-groups.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,22 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageViewBaseService } from './wp-view-base.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Injectable } from '@angular/core'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { take } from 'rxjs/operators'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { GroupObject, WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { QueryGroupByResource } from "core-app/features/hal/resources/query-group-by-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { GroupObject, WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; +import { WorkPackageViewBaseService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseService { readonly wpTypesToShowInCollapsedGroupHeaders:((wp:WorkPackageResource) => boolean)[]; + readonly groupTypesWithHeaderCellsWhenCollapsed = ['project']; get config():IGroupsCollapseEvent { @@ -83,15 +84,13 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe }; } - isMilestone = (workPackage:WorkPackageResource):boolean => { - return this.schemaCacheService.of(workPackage)?.isMilestone; - }; + isMilestone = (workPackage:WorkPackageResource):boolean => this.schemaCacheService.of(workPackage)?.isMilestone; toggleGroupCollapseState(groupIdentifier:string):void { const newCollapsedState = !this.config.state[groupIdentifier]; const state = { ...this.config.state, - [groupIdentifier]: newCollapsedState + [groupIdentifier]: newCollapsedState, }; const newState = { ...this.config, @@ -104,12 +103,10 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe } setAllGroupsCollapseStateTo(collapsedState:boolean):void { - const groupUpdatedState = this.currentGroups.reduce((updatedState:{[key:string]:boolean}, group) => { - return { - ...updatedState, - [group.identifier]:collapsedState, - }; - }, {}); + const groupUpdatedState = this.currentGroups.reduce((updatedState:{ [key:string]:boolean }, group) => ({ + ...updatedState, + [group.identifier]: collapsedState, + }), {}); const newState = { ...this.config, state: { @@ -132,10 +129,8 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe if (currentCollapsedGroupsState && groups?.length) { const firstGroupIdentifier = groups[0].identifier; const firstGroupCollapsedState = currentCollapsedGroupsState[firstGroupIdentifier]; - const allGroupsHaveTheSameCollapseState = groups.every((group) => { - return currentCollapsedGroupsState[group.identifier] != null && - currentCollapsedGroupsState[group.identifier] === currentCollapsedGroupsState[firstGroupIdentifier]; - }); + const allGroupsHaveTheSameCollapseState = groups.every((group) => currentCollapsedGroupsState[group.identifier] != null + && currentCollapsedGroupsState[group.identifier] === currentCollapsedGroupsState[firstGroupIdentifier]); allGroupsAreCollapsed = allGroupsHaveTheSameCollapseState && firstGroupCollapsedState; allGroupsAreExpanded = allGroupsHaveTheSameCollapseState && !firstGroupCollapsedState; @@ -156,6 +151,6 @@ export class WorkPackageViewCollapsedGroupsService extends WorkPackageViewBaseSe } applyToQuery(query:QueryResource) { - return; + } } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts index 1eb78d53bb..776e7cf6cf 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageQueryStateService } from './wp-view-base.service'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { States } from 'core-app/core/states/states.service'; import { Injectable } from '@angular/core'; -import { QueryColumn, queryColumnTypes } from "core-app/features/work-packages/components/wp-query/query-column"; -import { combine } from "reactivestates"; -import { mapTo, take } from "rxjs/operators"; -import { cloneHalResourceCollection } from "core-app/features/hal/helpers/hal-resource-builder"; +import { QueryColumn, queryColumnTypes } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { combine } from 'reactivestates'; +import { mapTo, take } from 'rxjs/operators'; +import { cloneHalResourceCollection } from 'core-app/features/hal/helpers/hal-resource-builder'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewColumnsService extends WorkPackageQueryStateService { - public constructor(readonly states:States, readonly querySpace:IsolatedQuerySpace) { super(querySpace); } @@ -56,19 +55,19 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< } public isCurrentlyEqualTo(a:QueryColumn[]) { - const comparer = (columns:QueryColumn[]) => columns.map(c => c.href); + const comparer = (columns:QueryColumn[]) => columns.map((c) => c.href); return _.isEqual( comparer(a), - comparer(this.getColumns()) + comparer(this.getColumns()), ); } public applyToQuery(query:QueryResource) { const toApply = this.getColumns(); - const oldColumns = query.columns.map(el => el.id); - const newColumns = toApply.map(el => el.id); + const oldColumns = query.columns.map((el) => el.id); + const newColumns = toApply.map((el) => el.id); query.columns = cloneHalResourceCollection(toApply); // We can avoid reloading even with relation columns if we only removed columns @@ -91,14 +90,14 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< * Returns a shallow copy with the original column objects. */ public getColumns():QueryColumn[] { - return [ ...this.current ]; + return [...this.current]; } /** * Return the index of the given column or -1 if it is not contained. */ public index(id:string):number { - return _.findIndex(this.getColumns(), column => column.id === id); + return _.findIndex(this.getColumns(), (column) => column.id === id); } /** @@ -106,7 +105,7 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< * @param id */ public findById(id:string):QueryColumn|undefined { - return _.find(this.getColumns(), column => column.id === id); + return _.find(this.getColumns(), (column) => column.id === id); } /** @@ -164,7 +163,7 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< } public setColumnsById(columnIds:string[]) { - const mapped = columnIds.map(id => _.find(this.all, c => c.id === id)); + const mapped = columnIds.map((id) => _.find(this.all, (c) => c.id === id)); this.setColumns(_.compact(mapped)); } @@ -215,10 +214,10 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< } if (this.index(id) === -1) { - const newColumn = _.find(this.all, (column) => column.id === id); + const newColumn = _.find(this.all, (column) => column.id === id); if (!newColumn) { - throw "Column with provided name is not found"; + throw new Error('Column with provided name is not found'); } columns.splice(position, 0, newColumn); @@ -284,7 +283,7 @@ export class WorkPackageViewColumnsService extends WorkPackageQueryStateService< .values$() .pipe( take(1), - mapTo(null) + mapTo(null), ) .toPromise(); } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts index 9cfe8c538d..92edbbde43 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageQueryStateService } from './wp-view-base.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { States } from 'core-app/core/states/states.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; - +import { WorkPackageQueryStateService } from './wp-view-base.service'; export const wpDisplayListRepresentation = 'list'; export const wpDisplayCardRepresentation = 'card'; @@ -40,7 +39,7 @@ export type WorkPackageDisplayRepresentationValue = 'list'|'card'; @Injectable() export class WorkPackageViewDisplayRepresentationService extends WorkPackageQueryStateService { public constructor(readonly states:States, - readonly querySpace:IsolatedQuerySpace) { + readonly querySpace:IsolatedQuerySpace) { super(querySpace); } @@ -53,7 +52,7 @@ export class WorkPackageViewDisplayRepresentationService extends WorkPackageQuer } public applyToQuery(query:QueryResource) { - const current = this.current; + const { current } = this; query.displayRepresentation = current === null ? undefined : current; return false; @@ -64,7 +63,7 @@ export class WorkPackageViewDisplayRepresentationService extends WorkPackageQuer } public get isList():boolean { - const current = this.current; + const { current } = this; return !current || current === wpDisplayListRepresentation; } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts index 4b9342d0ec..2a0a830299 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageQueryStateService } from './wp-view-base.service'; import { Injectable } from '@angular/core'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { combine, input, InputState } from 'reactivestates'; -import { States } from "core-app/core/states/states.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { mapTo, take } from "rxjs/operators"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { QueryFilterInstanceResource } from "core-app/features/hal/resources/query-filter-instance-resource"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { cloneHalResourceCollection } from "core-app/features/hal/helpers/hal-resource-builder"; -import { QueryFilterInstanceSchemaResource } from "core-app/features/hal/resources/query-filter-instance-schema-resource"; -import { QueryFilterResource } from "core-app/features/hal/resources/query-filter-resource"; +import { States } from 'core-app/core/states/states.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { mapTo, take } from 'rxjs/operators'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { cloneHalResourceCollection } from 'core-app/features/hal/helpers/hal-resource-builder'; +import { QueryFilterInstanceSchemaResource } from 'core-app/features/hal/resources/query-filter-instance-schema-resource'; +import { QueryFilterResource } from 'core-app/features/hal/resources/query-filter-resource'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewFiltersService extends WorkPackageQueryStateService { @@ -60,14 +60,14 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< // it is only named subjectOr 'subjectOrId', 'subjectOr', - 'manualSort' + 'manualSort', ]; /** Flag state to determine whether the filters are incomplete */ private incomplete = input(false); constructor(protected readonly states:States, - readonly querySpace:IsolatedQuerySpace) { + readonly querySpace:IsolatedQuerySpace) { super(querySpace); } @@ -87,7 +87,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * Return whether the filters are empty */ public get isEmpty() { - const value = this.lastUpdatedState.value; + const { value } = this.lastUpdatedState; return !value || value.length === 0; } @@ -100,7 +100,6 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< return this.incomplete.values$(); } - /** * Add a filter instantiation from the set of available filter schemas * @@ -158,7 +157,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< const schema = _.find( this.availableSchemas, - schema => (schema.filter.allowedValues as HalResource)[0].id === id + (schema) => (schema.filter.allowedValues as HalResource)[0].id === id, )!; return schema.getFilter(); @@ -169,11 +168,11 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * @param filters Filters to be removed */ public remove(...filters:(QueryFilterInstanceResource|string)[]) { - const mapper = (f:QueryFilterInstanceResource|string) => (f instanceof QueryFilterInstanceResource) ? f.id : f; + const mapper = (f:QueryFilterInstanceResource|string) => ((f instanceof QueryFilterInstanceResource) ? f.id : f); const set = new Set(filters.map(mapper)); this.update( - this.rawFilters.filter(f => !set.has(mapper(f))) + this.rawFilters.filter((f) => !set.has(mapper(f))), ); } @@ -192,7 +191,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * They need to be instantiated before using them in this service. */ public get availableFilters():QueryFilterResource[] { - return this.availableSchemas.map(schema => schema.allowedFilterValue); + return this.availableSchemas.map((schema) => schema.allowedFilterValue); } private get availableSchemas():QueryFilterInstanceSchemaResource[] { @@ -204,7 +203,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * @param filters */ public isComplete(filters:QueryFilterInstanceResource[]):boolean { - return _.every(filters, filter => filter.isCompletelyDefined()); + return _.every(filters, (filter) => filter.isCompletelyDefined()); } /** @@ -212,11 +211,11 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * @param query */ public hasChanged(query:QueryResource) { - const comparer = (filter:HalResource[]) => filter.map(el => el.$source); + const comparer = (filter:HalResource[]) => filter.map((el) => el.$source); return !_.isEqual( comparer(query.filters), - comparer(this.rawFilters) + comparer(this.rawFilters), ); } @@ -250,7 +249,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * @param id Identifier of the filter */ public findIndex(id:string):number { - return _.findIndex(this.current, f => f.id === id); + return _.findIndex(this.current, (f) => f.id === id); } public applyToQuery(query:QueryResource) { @@ -309,7 +308,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< .values$() .pipe( take(1), - mapTo(null) + mapTo(null), ) .toPromise(); } @@ -318,7 +317,7 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< * Get all filters that are not in the current active set */ private remainingFilters(filters = this.rawFilters) { - return _.differenceBy(this.availableFilters, filters, filter => filter.id); + return _.differenceBy(this.availableFilters, filters, (filter) => filter.id); } /** @@ -329,6 +328,6 @@ export class WorkPackageViewFiltersService extends WorkPackageQueryStateService< } isAvailable(el:QueryFilterInstanceResource):boolean { - return !!this.availableFilters.find(available => available.id === el.id); + return !!this.availableFilters.find((available) => available.id === el.id); } } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts index 1c1986688a..70e2b24d77 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,10 +30,10 @@ import { Injectable } from '@angular/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Observable } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; -import { WorkPackageViewSelectionService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service"; -import { WorkPackageViewBaseService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { WorkPackageViewSelectionService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service'; +import { WorkPackageViewBaseService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; export interface WPFocusState { workPackageId:string; @@ -42,9 +42,8 @@ export interface WPFocusState { @Injectable() export class WorkPackageViewFocusService extends WorkPackageViewBaseService { - constructor(public querySpace:IsolatedQuerySpace, - public wpTableSelection:WorkPackageViewSelectionService) { + public wpTableSelection:WorkPackageViewSelectionService) { super(querySpace); } @@ -82,7 +81,7 @@ export class WorkPackageViewFocusService extends WorkPackageViewBaseService val.workPackageId), - distinctUntilChanged() + distinctUntilChanged(), ); } @@ -91,7 +90,7 @@ export class WorkPackageViewFocusService extends WorkPackageViewBaseService { public constructor(readonly states:States, - readonly querySpace:IsolatedQuerySpace) { + readonly querySpace:IsolatedQuerySpace) { super(querySpace); } @@ -47,22 +47,22 @@ export class WorkPackageViewGroupByService extends WorkPackageQueryStateService< } public hasChanged(query:QueryResource) { - const comparer = (groupBy:QueryColumn|HalResource|null|undefined) => groupBy ? groupBy.href : null; + const comparer = (groupBy:QueryColumn|HalResource|null|undefined) => (groupBy ? groupBy.href : null); return !_.isEqual( comparer(query.groupBy), - comparer(this.current) + comparer(this.current), ); } public applyToQuery(query:QueryResource) { - const current = this.current; + const { current } = this; query.groupBy = current === null ? undefined : current; return true; } public isGroupable(column:QueryColumn):boolean { - return !!_.find(this.available, candidate => candidate.id === column.id); + return !!_.find(this.available, (candidate) => candidate.id === column.id); } public disable() { @@ -70,7 +70,7 @@ export class WorkPackageViewGroupByService extends WorkPackageQueryStateService< } public setBy(column:QueryColumn) { - const groupBy = _.find(this.available, candidate => candidate.id === column.id); + const groupBy = _.find(this.available, (candidate) => candidate.id === column.id); if (groupBy) { this.update(groupBy); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts index 2555c79bc9..a22eb4d1aa 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,20 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -/*jshint expr: true*/ - -import { TestBed, waitForAsync } from "@angular/core/testing"; -import { States } from "core-app/core/states/states.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { WorkPackageViewHierarchyIdentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; +/* jshint expr: true */ + +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { States } from 'core-app/core/states/states.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { WorkPackageViewHierarchyIdentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { of } from 'rxjs'; import SpyObj = jasmine.SpyObj; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { of } from "rxjs"; -describe('WorkPackageViewIndentation service', function () { +describe('WorkPackageViewIndentation service', () => { let service:WorkPackageViewHierarchyIdentationService; let states:States; let querySpace:IsolatedQuerySpace; @@ -54,18 +54,16 @@ describe('WorkPackageViewIndentation service', function () { class Apiv3serviceStub { work_packages = { - id: (wpId:string) => { - return { - get: () => of(states.workPackages.get(wpId).value) - }; - } + id: (wpId:string) => ({ + get: () => of(states.workPackages.get(wpId).value), + }), }; } beforeEach(waitForAsync(() => { parentServiceSpy = jasmine.createSpyObj( 'WorkPackageRelationHierarchyService', - ['changeParent'] + ['changeParent'], ); parentServiceSpy.changeParent.and.returnValue(Promise.resolve()); @@ -79,8 +77,8 @@ describe('WorkPackageViewIndentation service', function () { { provide: APIV3Service, useClass: Apiv3serviceStub }, { provide: WorkPackageViewHierarchiesService, useClass: HierarchyServiceStub }, { provide: WorkPackageRelationsHierarchyService, useValue: parentServiceSpy }, - WorkPackageViewHierarchyIdentationService - ] + WorkPackageViewHierarchyIdentationService, + ], }) .compileComponents() .then(() => { @@ -100,7 +98,7 @@ describe('WorkPackageViewIndentation service', function () { it('Cannot indent when is first index', () => { querySpace.tableRendered.putValue([ { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '2345', hidden: false, classIdentifier: 'foo' } + { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo' }; @@ -110,7 +108,7 @@ describe('WorkPackageViewIndentation service', function () { it('Can indent as second when it has no ancestors', () => { querySpace.tableRendered.putValue([ { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: [] }; @@ -120,7 +118,7 @@ describe('WorkPackageViewIndentation service', function () { it('Cannot indent when possible but hierarchy disabled', () => { querySpace.tableRendered.putValue([ { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); spyOnProperty(hierarchyServiceStub, 'isEnabled', 'get') @@ -133,7 +131,7 @@ describe('WorkPackageViewIndentation service', function () { it('Can not indent with a predecessor that is an ancestor already', () => { querySpace.tableRendered.putValue([ { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: ['2345'] }; @@ -144,7 +142,7 @@ describe('WorkPackageViewIndentation service', function () { querySpace.tableRendered.putValue([ { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, { workPackageId: '5555', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: ['2345'] }; @@ -179,7 +177,7 @@ describe('WorkPackageViewIndentation service', function () { querySpace.tableRendered.putValue([ { workPackageId: '5555', hidden: false, classIdentifier: 'foo' }, { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: [] }; @@ -197,7 +195,7 @@ describe('WorkPackageViewIndentation service', function () { querySpace.tableRendered.putValue([ { workPackageId: '5555', hidden: false, classIdentifier: 'foo' }, { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: [] }; @@ -215,7 +213,7 @@ describe('WorkPackageViewIndentation service', function () { querySpace.tableRendered.putValue([ { workPackageId: '5555', hidden: false, classIdentifier: 'foo' }, { workPackageId: '2345', hidden: false, classIdentifier: 'foo' }, - { workPackageId: '1234', hidden: false, classIdentifier: 'foo' } + { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); const workPackage:any = { id: '1234', changeParent: () => 'foo', ancestorIds: ['5555'] }; @@ -236,7 +234,9 @@ describe('WorkPackageViewIndentation service', function () { { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); - const workPackage:any = { id: '1234', changeParent: () => 'foo', parent: '5555', ancestorIds: ['2345', '5555'] }; + const workPackage:any = { + id: '1234', changeParent: () => 'foo', parent: '5555', ancestorIds: ['2345', '5555'], + }; service.outdent(workPackage).then(() => { expect(parentServiceSpy.changeParent).toHaveBeenCalledWith(workPackage, '2345'); @@ -249,7 +249,9 @@ describe('WorkPackageViewIndentation service', function () { { workPackageId: '1234', hidden: false, classIdentifier: 'foo' }, ]); - const workPackage:any = { id: '1234', changeParent: () => 'foo', parent: '2345', ancestorIds: ['2345'] }; + const workPackage:any = { + id: '1234', changeParent: () => 'foo', parent: '2345', ancestorIds: ['2345'], + }; service.outdent(workPackage).then(() => { expect(parentServiceSpy.changeParent).toHaveBeenCalledWith(workPackage, null); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts index 18d4512172..b0ebc58791 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.ts @@ -1,21 +1,20 @@ import { Injectable } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { WorkPackageRelationsHierarchyService } from "core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageRelationsHierarchyService } from 'core-app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.service'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable() export class WorkPackageViewHierarchyIdentationService { - constructor(private wpViewHierarchies:WorkPackageViewHierarchiesService, - private wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, - private states:States, - private wpRelationHierarchy:WorkPackageRelationsHierarchyService, - private apiV3Service:APIV3Service, - private querySpace:IsolatedQuerySpace) { + private wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService, + private states:States, + private wpRelationHierarchy:WorkPackageRelationsHierarchyService, + private apiV3Service:APIV3Service, + private querySpace:IsolatedQuerySpace) { } /** @@ -114,7 +113,7 @@ export class WorkPackageViewHierarchyIdentationService { // If we have more than one ancestor, // just drop the last one - const ancestorIds = workPackage.ancestorIds; + const { ancestorIds } = workPackage; const ancestorCount = ancestorIds.length; if (ancestorCount > 1) { newParentId = ancestorIds[ancestorCount - 2]; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service.ts index 0c4c018095..8671346c43 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service.ts @@ -1,14 +1,13 @@ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageQueryStateService } from './wp-view-base.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Injectable } from '@angular/core'; -import { WorkPackageViewHierarchies } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies"; +import { WorkPackageViewHierarchies } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-hierarchies'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewHierarchiesService extends WorkPackageQueryStateService { - public valueFromQuery(query:QueryResource):WorkPackageViewHierarchies { - const value = new WorkPackageViewHierarchies(query.showHierarchies); - const current = this.current; + const value = new WorkPackageViewHierarchies(query.showHierarchies); + const { current } = this; // Take over current collapsed values // which are not yet saved diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts index 8018c2e48c..7398609841 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts @@ -1,19 +1,19 @@ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageQueryStateService } from './wp-view-base.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; -import { BannersService } from "core-app/core/enterprise/banners.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { WorkPackageViewHighlight } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight"; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { WorkPackageViewHighlight } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-highlight'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewHighlightingService extends WorkPackageQueryStateService { public constructor(readonly states:States, - readonly Banners:BannersService, - readonly querySpace:IsolatedQuerySpace) { + readonly Banners:BannersService, + readonly querySpace:IsolatedQuerySpace) { super(querySpace); } @@ -64,12 +64,12 @@ export class WorkPackageViewHighlightingService extends WorkPackageQueryStateSer } public hasChanged(query:QueryResource) { - return query.highlightingMode !== this.current.mode || - !_.isEqual(query.highlightedAttributes, this.current.selectedAttributes); + return query.highlightingMode !== this.current.mode + || !_.isEqual(query.highlightedAttributes, this.current.selectedAttributes); } public applyToQuery(query:QueryResource):boolean { - const current = this.current; + const { current } = this; query.highlightingMode = current.mode; query.highlightedAttributes = current.selectedAttributes; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts index b876d696f7..a7b9adb54a 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,33 +26,31 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { Injectable } from '@angular/core'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { States } from 'core-app/core/states/states.service'; +import { QuerySchemaResource } from 'core-app/features/hal/resources/query-schema-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { MAX_ORDER, buildDelta } from 'core-app/shared/helpers/drag-and-drop/reorder-delta-builder'; +import { take } from 'rxjs/operators'; +import { InputState } from 'reactivestates'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { CausedUpdatesService } from 'core-app/features/boards/board/caused-updates/caused-updates.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { QueryOrder } from 'core-app/core/apiv3/endpoints/queries/apiv3-query-order'; import { WorkPackageQueryStateService } from './wp-view-base.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { States } from "core-app/core/states/states.service"; -import { QuerySchemaResource } from "core-app/features/hal/resources/query-schema-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { MAX_ORDER, ReorderDeltaBuilder } from "core-app/shared/helpers/drag-and-drop/reorder-delta-builder"; -import { take } from "rxjs/operators"; -import { InputState } from "reactivestates"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { CausedUpdatesService } from "core-app/features/boards/board/caused-updates/caused-updates.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { QueryOrder } from "core-app/core/apiv3/endpoints/queries/apiv3-query-order"; - @Injectable() export class WorkPackageViewOrderService extends WorkPackageQueryStateService { - constructor(protected readonly querySpace:IsolatedQuerySpace, - protected readonly apiV3Service:APIV3Service, - protected readonly states:States, - protected readonly causedUpdates:CausedUpdatesService, - protected readonly wpTableSortBy:WorkPackageViewSortByService, - protected readonly pathHelper:PathHelperService) { + protected readonly apiV3Service:APIV3Service, + protected readonly states:States, + protected readonly causedUpdates:CausedUpdatesService, + protected readonly wpTableSortBy:WorkPackageViewSortByService, + protected readonly pathHelper:PathHelperService) { super(querySpace); } @@ -62,7 +60,6 @@ export class WorkPackageViewOrderService extends WorkPackageQueryStateService id === wpId); + _.remove(order, (id) => id === wpId); this.update({ [wpId]: -1 }); return order; } @@ -126,7 +123,7 @@ export class WorkPackageViewOrderService extends WorkPackageQueryStateService { if (this.currentQuery.persisted) { - const value = this.positions.value; + const { value } = this.positions; // Remove empty or stale values given we can reload them if ((value === {} || this.positions.isValueOlderThan(60000))) { - this.positions.clear("Clearing old positions value"); + this.positions.clear('Clearing old positions value'); } // Load the current order from backend @@ -181,7 +178,7 @@ export class WorkPackageViewOrderService extends WorkPackageQueryStateService this.states.workPackages.get(wp.id!).getValueOr(wp)); + .map((wp) => this.states.workPackages.get(wp.id!).getValueOr(wp)); if (this.currentQuery.persisted || this.positions.isPristine()) { return upstreamOrder; - } else { - const positions = this.positions.value!; - return _.sortBy(upstreamOrder, (wp) => { - const pos = positions[wp.id!]; - return pos !== undefined ? pos : MAX_ORDER; - }); } + const positions = this.positions.value!; + return _.sortBy(upstreamOrder, (wp) => { + const pos = positions[wp.id!]; + return pos !== undefined ? pos : MAX_ORDER; + }); } applyToQuery(query:QueryResource):boolean { diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts index 4a3796becc..ec204f9e3c 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,12 +27,12 @@ //++ import { Injectable } from '@angular/core'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageViewPagination } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination"; -import { WorkPackageViewBaseService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service"; -import { PaginationObject, PaginationService } from "core-app/shared/components/table-pagination/pagination-service"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageViewPagination } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-pagination'; +import { WorkPackageViewBaseService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; +import { PaginationObject, PaginationService } from 'core-app/shared/components/table-pagination/pagination-service'; export interface PaginationUpdateObject { page?:number; @@ -44,7 +44,7 @@ export interface PaginationUpdateObject { @Injectable() export class WorkPackageViewPaginationService extends WorkPackageViewBaseService { public constructor(querySpace:IsolatedQuerySpace, - readonly paginationService:PaginationService) { + readonly paginationService:PaginationService) { super(querySpace); } @@ -52,15 +52,13 @@ export class WorkPackageViewPaginationService extends WorkPackageViewBaseService if (this.current) { return { pageSize: this.current.perPage, - offset: this.current.page - }; - } else { - return { - pageSize: this.paginationService.getCachedPerPage([]), - offset: 1 + offset: this.current.page, }; } - + return { + pageSize: this.paginationService.getCachedPerPage([]), + offset: 1, + }; } public valueFromQuery(query:QueryResource, results:WorkPackageCollectionResource) { @@ -88,7 +86,7 @@ export class WorkPackageViewPaginationService extends WorkPackageViewBaseService page: results.offset, perPage: results.pageSize, total: results.total, - count: results.count + count: results.count, }; this.updateFromObject(update); diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts index bef6b8c655..2bcfa72fd2 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-relation-columns.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,33 +26,33 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageViewColumnsService } from './wp-view-columns.service'; -import { WorkPackageViewBaseService } from './wp-view-base.service'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageViewRelationColumns } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { RelationsStateValue, WorkPackageRelationsService } from "core-app/features/work-packages/components/wp-relations/wp-relations.service"; -import { Injectable } from "@angular/core"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageViewRelationColumns } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-relation-columns'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { RelationsStateValue, WorkPackageRelationsService } from 'core-app/features/work-packages/components/wp-relations/wp-relations.service'; +import { Injectable } from '@angular/core'; import { QueryColumn, queryColumnTypes, RelationQueryColumn, - TypeRelationQueryColumn -} from "core-app/features/work-packages/components/wp-query/query-column"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { RelationResource } from "core-app/features/hal/resources/relation-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; + TypeRelationQueryColumn, +} from 'core-app/features/work-packages/components/wp-query/query-column'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { RelationResource } from 'core-app/features/hal/resources/relation-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { WorkPackageViewBaseService } from './wp-view-base.service'; +import { WorkPackageViewColumnsService } from './wp-view-columns.service'; export type RelationColumnType = 'toType'|'ofType'; @Injectable() export class WorkPackageViewRelationColumnsService extends WorkPackageViewBaseService { constructor(public querySpace:IsolatedQuerySpace, - public wpTableColumns:WorkPackageViewColumnsService, - public halResourceService:HalResourceService, - public apiV3Service:APIV3Service, - public wpRelations:WorkPackageRelationsService) { + public wpTableColumns:WorkPackageViewColumnsService, + public halResourceService:HalResourceService, + public apiV3Service:APIV3Service, + public wpRelations:WorkPackageRelationsService) { super(querySpace); } @@ -124,11 +124,9 @@ export class WorkPackageViewRelationColumnsService extends WorkPackageViewBaseSe // Get the relation types for OF relation columns if (type === 'ofType') { - const relationType = (column as RelationQueryColumn).relationType; + const { relationType } = column as RelationQueryColumn; - return _.filter(relations, (relation:RelationResource) => { - return relation.denormalized(workPackage).relationType === relationType; - }); + return _.filter(relations, (relation:RelationResource) => relation.denormalized(workPackage).relationType === relationType); } return []; @@ -136,12 +134,12 @@ export class WorkPackageViewRelationColumnsService extends WorkPackageViewBaseSe public relationColumnType(column:QueryColumn):RelationColumnType|null { switch (column._type) { - case queryColumnTypes.RELATION_TO_TYPE: - return 'toType'; - case queryColumnTypes.RELATION_OF_TYPE: - return 'ofType'; - default: - return null; + case queryColumnTypes.RELATION_TO_TYPE: + return 'toType'; + case queryColumnTypes.RELATION_OF_TYPE: + return 'ofType'; + default: + return null; } } @@ -167,4 +165,3 @@ export class WorkPackageViewRelationColumnsService extends WorkPackageViewBaseSe return this.lastUpdatedState.getValueOr({}); } } - diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts index d1739d512d..c2d4bf639b 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-selection.service.ts @@ -1,11 +1,11 @@ import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable, OnDestroy } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { States } from 'core-app/core/states/states.service'; -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; -import { WorkPackageViewBaseService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service"; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; +import { WorkPackageViewBaseService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; export interface WorkPackageViewSelectionState { // Map of selected rows @@ -17,10 +17,9 @@ export interface WorkPackageViewSelectionState { @Injectable() export class WorkPackageViewSelectionService extends WorkPackageViewBaseService implements OnDestroy { - public constructor(readonly querySpace:IsolatedQuerySpace, - readonly states:States, - readonly opContextMenu:OPContextMenuService) { + readonly states:States, + readonly opContextMenu:OPContextMenuService) { super(querySpace); this.reset(); } @@ -33,10 +32,10 @@ export class WorkPackageViewSelectionService extends WorkPackageViewBaseService< public initializeSelection(selectedWorkPackageIds:string[]) { const state:WorkPackageViewSelectionState = { selected: {}, - activeRowIndex: null + activeRowIndex: null, }; - selectedWorkPackageIds.forEach(id => state.selected[id] = true); + selectedWorkPackageIds.forEach((id) => state.selected[id] = true); this.updatesState.clear(); this.pristineState.putValue(state); @@ -66,7 +65,7 @@ export class WorkPackageViewSelectionService extends WorkPackageViewBaseService< */ public getSelectedWorkPackages():WorkPackageResource[] { const wpState = this.states.workPackages; - return this.getSelectedWorkPackageIds().map(id => wpState.get(id).value!); + return this.getSelectedWorkPackageIds().map((id) => wpState.get(id).value!); } public getSelectedWorkPackageIds():string[] { @@ -181,7 +180,7 @@ export class WorkPackageViewSelectionService extends WorkPackageViewBaseService< private get _emptyState():WorkPackageViewSelectionState { return { selected: {}, - activeRowIndex: null + activeRowIndex: null, }; } @@ -189,4 +188,3 @@ export class WorkPackageViewSelectionService extends WorkPackageViewBaseService< return undefined; } } - diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts index c0c9785eda..fb2f72554e 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,21 +29,20 @@ import { States } from 'core-app/core/states/states.service'; import { combine } from 'reactivestates'; import { mapTo } from 'rxjs/operators'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; -import { WorkPackageQueryStateService } from './wp-view-base.service'; import { Observable } from 'rxjs'; -import { QuerySortByResource } from "core-app/features/hal/resources/query-sort-by-resource"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { QueryColumn } from "core-app/features/work-packages/components/wp-query/query-column"; +import { QuerySortByResource } from 'core-app/features/hal/resources/query-sort-by-resource'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewSortByService extends WorkPackageQueryStateService { - constructor(protected readonly states:States, - protected readonly querySpace:IsolatedQuerySpace, - protected readonly pathHelper:PathHelperService) { + protected readonly querySpace:IsolatedQuerySpace, + protected readonly pathHelper:PathHelperService) { super(querySpace); } @@ -55,16 +54,16 @@ export class WorkPackageViewSortByService extends WorkPackageQueryStateService sortBy.map(el => el.href); + const comparer = (sortBy:QuerySortByResource[]) => sortBy.map((el) => el.href); return !_.isEqual( comparer(query.sortBy), - comparer(this.current) + comparer(this.current), ); } @@ -80,7 +79,7 @@ export class WorkPackageViewSortByService extends WorkPackageQueryStateService candidate.column.href === column.href + (candidate) => candidate.column.href === column.href, ); } @@ -103,14 +102,14 @@ export class WorkPackageViewSortByService extends WorkPackageQueryStateService (candidate.column.href === column.href && - candidate.direction.href === direction) + (candidate) => (candidate.column.href === column.href + && candidate.direction.href === direction), ); } public add(sortBy:QuerySortByResource) { const newValue = _ - .uniqBy([sortBy, ...this.current], sortBy => sortBy.column.href) + .uniqBy([sortBy, ...this.current], (sortBy) => sortBy.column.href) .slice(0, 3); this.update(newValue); @@ -121,17 +120,15 @@ export class WorkPackageViewSortByService extends WorkPackageQueryStateService { - return sort.column.href!.endsWith('/manualSorting'); - }); + return _.find(this.available, (sort) => sort.column.href!.endsWith('/manualSorting')); } } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts index e8e06860c3..aa1fceb7f9 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { WorkPackageQueryStateService } from './wp-view-base.service'; -import { QueryResource } from "core-app/features/hal/resources/query-resource"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { QueryResource } from 'core-app/features/hal/resources/query-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { Injectable } from '@angular/core'; +import { WorkPackageQueryStateService } from './wp-view-base.service'; @Injectable() export class WorkPackageViewSumService extends WorkPackageQueryStateService { - public constructor(querySpace:IsolatedQuerySpace) { super(querySpace); } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts index 72d723ff92..29ba13837c 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,17 +27,16 @@ //++ import { Injectable } from '@angular/core'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { input } from 'reactivestates'; +import { WorkPackageTimelineState } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline'; +import { zoomLevelOrder } from 'core-app/features/work-packages/components/wp-table/timeline/wp-timeline'; +import { QueryResource, TimelineLabels, TimelineZoomLevel } from 'core-app/features/hal/resources/query-resource'; import { WorkPackageQueryStateService } from './wp-view-base.service'; -import { WorkPackageTimelineState } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-table-timeline"; -import { zoomLevelOrder } from "core-app/features/work-packages/components/wp-table/timeline/wp-timeline"; -import { QueryResource, TimelineLabels, TimelineZoomLevel } from "core-app/features/hal/resources/query-resource"; @Injectable() export class WorkPackageViewTimelineService extends WorkPackageQueryStateService { - /** Remember the computed zoom level to correct zooming after leaving autozoom */ public appliedZoomLevel$ = input('auto'); @@ -50,7 +49,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService ...this.defaultState, visible: query.timelineVisible, zoomLevel: query.timelineZoomLevel, - labels: query.timelineLabels + labels: query.timelineLabels, }; } @@ -104,7 +103,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService } public updateLabels(labels:TimelineLabels) { - this.modify({ labels: labels }); + this.modify({ labels }); } public getNormalizedLabels(workPackage:WorkPackageResource) { @@ -152,7 +151,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService } public enableAutozoom() { - this.modify({ zoomLevel: "auto" }); + this.modify({ zoomLevel: 'auto' }); } public get current():WorkPackageTimelineState { @@ -186,7 +185,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService return { left: '', right: '', - farRight: 'subject' + farRight: 'subject', }; } @@ -194,7 +193,7 @@ export class WorkPackageViewTimelineService extends WorkPackageQueryStateService return { zoomLevel: 'auto', visible: false, - labels: this.defaultLabels + labels: this.defaultLabels, }; } } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts index ba08a5dab3..322bf12046 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/work-package-single-view.base.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -30,8 +30,8 @@ import { ChangeDetectorRef, Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { OpTitleService } from "core-app/core/html/op-title.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { OpTitleService } from 'core-app/core/html/op-title.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { States } from 'core-app/core/states/states.service'; import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; @@ -44,16 +44,27 @@ import { HookService } from 'core-app/features/plugins/hook-service'; export class WorkPackageSingleViewBase extends UntilDestroyedMixin { @InjectField() states:States; + @InjectField() I18n!:I18nService; + @InjectField() keepTab:KeepTabService; + @InjectField() PathHelper:PathHelperService; + @InjectField() halEditing:HalResourceEditingService; + @InjectField() wpTableFocus:WorkPackageViewFocusService; + @InjectField() notificationService:WorkPackageNotificationService; + @InjectField() authorisationService:AuthorisationService; + @InjectField() cdRef:ChangeDetectorRef; + @InjectField() readonly titleService:OpTitleService; + @InjectField() readonly apiV3Service:APIV3Service; + @InjectField() readonly hooks:HookService; // Static texts @@ -61,13 +72,15 @@ export class WorkPackageSingleViewBase extends UntilDestroyedMixin { // Work package resource to be loaded from the cache public workPackage:WorkPackageResource; + public projectIdentifier:string; public focusAnchorLabel:string; + public showStaticPagePath:string; constructor(public injector:Injector, - protected workPackageId:string) { + protected workPackageId:string) { super(); this.initializeTexts(); } @@ -84,15 +97,14 @@ export class WorkPackageSingleViewBase extends UntilDestroyedMixin { .id(this.workPackageId) .requireAndStream() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((wp:WorkPackageResource) => { - this.workPackage = wp; - this.init(); - this.cdRef.detectChanges(); - }, - (error) => this.notificationService.handleRawError(error) - ); + this.workPackage = wp; + this.init(); + this.cdRef.detectChanges(); + }, + (error) => this.notificationService.handleRawError(error)); } /** @@ -100,8 +112,8 @@ export class WorkPackageSingleViewBase extends UntilDestroyedMixin { */ protected initializeTexts() { this.text.tabs = {}; - ['overview', 'activity', 'relations', 'watchers'].forEach(tab => { - this.text.tabs[tab] = this.I18n.t('js.work_packages.tabs.' + tab); + ['overview', 'activity', 'relations', 'watchers'].forEach((tab) => { + this.text.tabs[tab] = this.I18n.t(`js.work_packages.tabs.${tab}`); }); } @@ -132,7 +144,7 @@ export class WorkPackageSingleViewBase extends UntilDestroyedMixin { // Listen to tab changes to update the tab label this.keepTab.observable .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((tabs:any) => { this.updateFocusAnchorLabel(tabs.active); @@ -144,9 +156,9 @@ export class WorkPackageSingleViewBase extends UntilDestroyedMixin { */ public updateFocusAnchorLabel(tabName:string):string { const tabLabel = this.I18n.t('js.label_work_package_details_you_are_here', { - tab: this.I18n.t('js.work_packages.tabs.' + tabName), + tab: this.I18n.t(`js.work_packages.tabs.${tabName}`), type: this.workPackage.type.name, - subject: this.workPackage.subject + subject: this.workPackage.subject, }); return this.focusAnchorLabel = tabLabel; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts index 9c1507c85c..04f26ec7fa 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/work-packages-view.base.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,64 +26,90 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Directive, Injector, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectorRef, Directive, Injector, OnDestroy, OnInit, +} from '@angular/core'; import { StateService, TransitionService } from '@uirouter/core'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { filter, take, withLatestFrom } from 'rxjs/operators'; -import { LoadingIndicatorService } from "core-app/core/loading-indicator/loading-indicator.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading-indicator.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageStaticQueriesService } from 'core-app/features/work-packages/components/wp-query-select/wp-static-queries.service'; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { States } from "core-app/core/states/states.service"; -import { WorkPackageViewColumnsService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service"; -import { WorkPackageViewSortByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service"; -import { WorkPackageViewGroupByService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import { WorkPackageViewSumService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service"; -import { WorkPackageViewTimelineService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service"; -import { WorkPackageViewHierarchiesService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service"; -import { WorkPackageViewPaginationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { WorkPackagesListChecksumService } from "core-app/features/work-packages/components/wp-list/wp-list-checksum.service"; -import { WorkPackageQueryStateService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service"; -import { WorkPackageStatesInitializationService } from "core-app/features/work-packages/components/wp-list/wp-states-initialization.service"; -import { WorkPackageViewOrderService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service"; -import { WorkPackageViewDisplayRepresentationService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service"; -import { HalEvent, HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { DeviceService } from "core-app/core/browser/device.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { States } from 'core-app/core/states/states.service'; +import { WorkPackageViewColumnsService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-columns.service'; +import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; +import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; +import { WorkPackageViewTimelineService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; +import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; +import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; +import { WorkPackageQueryStateService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-base.service'; +import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; +import { WorkPackageViewOrderService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service'; +import { WorkPackageViewDisplayRepresentationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-display-representation.service'; +import { HalEvent, HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { DeviceService } from 'core-app/core/browser/device.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Directive() export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implements OnInit, OnDestroy { - @InjectField() $state:StateService; + @InjectField() states:States; + @InjectField() querySpace:IsolatedQuerySpace; + @InjectField() authorisationService:AuthorisationService; + @InjectField() wpTableColumns:WorkPackageViewColumnsService; + @InjectField() wpTableHighlighting:WorkPackageViewHighlightingService; + @InjectField() wpTableSortBy:WorkPackageViewSortByService; + @InjectField() wpTableGroupBy:WorkPackageViewGroupByService; + @InjectField() wpTableFilters:WorkPackageViewFiltersService; + @InjectField() wpTableSum:WorkPackageViewSumService; + @InjectField() wpTableTimeline:WorkPackageViewTimelineService; + @InjectField() wpTableHierarchies:WorkPackageViewHierarchiesService; + @InjectField() wpTablePagination:WorkPackageViewPaginationService; + @InjectField() wpTableOrder:WorkPackageViewOrderService; + @InjectField() wpListService:WorkPackagesListService; + @InjectField() wpListChecksumService:WorkPackagesListChecksumService; + @InjectField() loadingIndicatorService:LoadingIndicatorService; + @InjectField() $transitions:TransitionService; + @InjectField() I18n!:I18nService; + @InjectField() wpStaticQueries:WorkPackageStaticQueriesService; + @InjectField() wpStatesInitialization:WorkPackageStatesInitializationService; + @InjectField() cdRef:ChangeDetectorRef; + @InjectField() wpDisplayRepresentation:WorkPackageViewDisplayRepresentationService; + @InjectField() halEvents:HalEventsService; + @InjectField() deviceService:DeviceService; + @InjectField() currentProject:CurrentProjectService; /** Determine when query is initially loaded */ @@ -112,7 +138,7 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement .updates$() .pipe( this.untilDestroyed(), - withLatestFrom(this.querySpace.query.values$()) + withLatestFrom(this.querySpace.query.values$()), ).subscribe(([pagination, query]) => { if (this.wpListChecksumService.isQueryOutdated(query, pagination)) { this.wpListChecksumService.update(query, pagination); @@ -146,7 +172,7 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement .updates$() .pipe( this.untilDestroyed(), - filter(() => queryState.hasValue() && service.hasChanged(queryState.value!)) + filter(() => queryState.hasValue() && service.hasChanged(queryState.value!)), ) .subscribe(() => { const newQuery = queryState.value!; @@ -178,14 +204,13 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement .aggregated$('WorkPackage') .pipe( this.untilDestroyed(), - filter((events:HalEvent[]) => this.filterRefreshEvents(events)) + filter((events:HalEvent[]) => this.filterRefreshEvents(events)), ) .subscribe((events:HalEvent[]) => { this.refresh(false, false); }); } - /** * Refresh the set of results, * showing the loading indicator if visibly is set. @@ -194,7 +219,6 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement */ public abstract refresh(visibly:boolean, firstPage:boolean):Promise; - /** * Set the loading indicator for this set instance * @param promise @@ -227,7 +251,7 @@ export abstract class WorkPackagesViewBase extends UntilDestroyedMixin implement .values$() .pipe( take(1), - filter(() => !this.componentDestroyed) + filter(() => !this.componentDestroyed), ) .subscribe(() => { this.queryLoaded = true; diff --git a/frontend/src/app/features/work-packages/routing/wp-view-page/wp-view-page.component.ts b/frontend/src/app/features/work-packages/routing/wp-view-page/wp-view-page.component.ts index 7a5d30bb71..6be71d22e3 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-page/wp-view-page.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-page/wp-view-page.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,24 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; -import { take } from "rxjs/operators"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageNotificationService } from "core-app/features/work-packages/services/notifications/work-package-notification.service"; -import { QueryParamListenerService } from "core-app/features/work-packages/components/wp-query/query-param-listener.service"; +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { take } from 'rxjs/operators'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; +import { QueryParamListenerService } from 'core-app/features/work-packages/components/wp-query/query-param-listener.service'; import { PartitionedQuerySpacePageComponent, ToolbarButtonComponentDefinition, -} from "core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component"; -import { WorkPackageCreateButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component"; -import { WorkPackageFilterButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component"; -import { WorkPackageViewToggleButton } from "core-app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component"; -import { WorkPackageDetailsViewButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component"; -import { WorkPackageTimelineButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component"; -import { ZenModeButtonComponent } from "core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component"; -import { WorkPackageSettingsButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component"; -import { of } from "rxjs"; -import { WorkPackageFoldToggleButtonComponent } from "core-app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component"; +} from 'core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component'; +import { WorkPackageCreateButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-create-button/wp-create-button.component'; +import { WorkPackageFilterButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-filter-button/wp-filter-button.component'; +import { WorkPackageViewToggleButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-view-toggle-button/work-package-view-toggle-button.component'; +import { WorkPackageDetailsViewButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-details-view-button/wp-details-view-button.component'; +import { WorkPackageTimelineButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-timeline-toggle-button/wp-timeline-toggle-button.component'; +import { ZenModeButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/zen-mode-toggle-button/zen-mode-toggle-button.component'; +import { WorkPackageSettingsButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-settings-button/wp-settings-button.component'; +import { of } from 'rxjs'; +import { WorkPackageFoldToggleButtonComponent } from 'core-app/features/work-packages/components/wp-buttons/wp-fold-toggle-button/wp-fold-toggle-button.component'; @Component({ selector: 'wp-view-page', @@ -64,7 +64,7 @@ export class WorkPackageViewPageComponent extends PartitionedQuerySpacePageCompo { component: WorkPackageCreateButtonComponent, inputs: { - stateName$: of("work-packages.partitioned.list.new"), + stateName$: of('work-packages.partitioned.list.new'), allowed: ['work_packages.createWorkPackage'], }, }, @@ -72,14 +72,12 @@ export class WorkPackageViewPageComponent extends PartitionedQuerySpacePageCompo component: WorkPackageFilterButtonComponent, }, { - component: WorkPackageViewToggleButton, + component: WorkPackageViewToggleButtonComponent, containerClasses: 'hidden-for-mobile', }, { component: WorkPackageFoldToggleButtonComponent, - show: () => { - return !!(this.currentQuery && this.currentQuery.groupBy); - }, + show: () => !!(this.currentQuery && this.currentQuery.groupBy), }, { component: WorkPackageDetailsViewButtonComponent, @@ -106,9 +104,8 @@ export class WorkPackageViewPageComponent extends PartitionedQuerySpacePageCompo protected additionalLoadingTime():Promise { if (this.wpTableTimeline.isVisible) { return this.querySpace.timelineRendered.pipe(take(1)).toPromise(); - } else { - return this.querySpace.tableRendered.valuesPromise() as Promise; } + return this.querySpace.tableRendered.valuesPromise() as Promise; } protected shouldUpdateHtmlTitle():boolean { diff --git a/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts b/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts index 6faf1a7536..a330d6c02f 100644 --- a/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts +++ b/frontend/src/app/features/work-packages/services/notifications/work-package-notification.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,21 +28,20 @@ import { Injectable, Injector } from '@angular/core'; import { INotification } from 'core-app/shared/components/notifications/notifications.service'; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; @Injectable() export class WorkPackageNotificationService extends HalResourceNotificationService { - constructor(readonly injector:Injector, - readonly apiV3Service:APIV3Service) { + readonly apiV3Service:APIV3Service) { super(injector); } public showSave(resource:WorkPackageResource, isCreate = false) { const message:any = { - message: this.I18n.t('js.notice_successful_' + (isCreate ? 'create' : 'update')), + message: this.I18n.t(`js.notice_successful_${isCreate ? 'create' : 'update'}`), }; this.addWorkPackageFullscreenLink(message, resource as any); @@ -57,8 +56,8 @@ export class WorkPackageNotificationService extends HalResourceNotificationServi type: 'error', link: { text: this.I18n.t('js.hal.error.update_conflict_refresh'), - target: () => this.apiV3Service.work_packages.id(resource).refresh() - } + target: () => this.apiV3Service.work_packages.id(resource).refresh(), + }, }); return true; @@ -71,9 +70,8 @@ export class WorkPackageNotificationService extends HalResourceNotificationServi // Don't show the 'Show in full screen' link if we're there already if (!this.$state.includes('work-packages.show')) { message.link = { - target: () => - this.$state.go('work-packages.show.tabs', { tabIdentifier: 'activity', workPackageId: resource.id }), - text: this.I18n.t('js.work_packages.message_successful_show_in_fullscreen') + target: () => this.$state.go('work-packages.show.tabs', { tabIdentifier: 'activity', workPackageId: resource.id }), + text: this.I18n.t('js.work_packages.message_successful_show_in_fullscreen'), }; } } diff --git a/frontend/src/app/features/work-packages/services/work-package-authorization.service.ts b/frontend/src/app/features/work-packages/services/work-package-authorization.service.ts index 2218329387..456f35f054 100644 --- a/frontend/src/app/features/work-packages/services/work-package-authorization.service.ts +++ b/frontend/src/app/features/work-packages/services/work-package-authorization.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { StateService } from '@uirouter/core'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; export class WorkPackageAuthorization { - public project:any; constructor(public workPackage:WorkPackageResource, - readonly PathHelper:PathHelperService, - readonly $state:StateService) { + readonly PathHelper:PathHelperService, + readonly $state:StateService) { this.project = workPackage.project; } public get allActions():any { return { workPackage: this.workPackage, - project: this.project + project: this.project, }; } @@ -52,9 +50,8 @@ export class WorkPackageAuthorization { const stateName = this.$state.current.name as string; if (stateName.indexOf('work-packages.partitioned.list.details') === 0) { return this.PathHelper.workPackageDetailsCopyPath(this.project.identifier, this.workPackage.id!); - } else { - return this.PathHelper.workPackageCopyPath(this.workPackage.id!); } + return this.PathHelper.workPackageCopyPath(this.workPackage.id!); } public linkForAction(action:any) { @@ -68,22 +65,20 @@ export class WorkPackageAuthorization { } public isPermitted(action:any) { - return this.allActions[action.resource] !== undefined && - this.allActions[action.resource][action.link] !== undefined; + return this.allActions[action.resource] !== undefined + && this.allActions[action.resource][action.link] !== undefined; } public permittedActionKeys(allowedActions:any) { - var validActions = _.filter(allowedActions, (action:any) => this.isPermitted(action)); + const validActions = _.filter(allowedActions, (action:any) => this.isPermitted(action)); - return _.map(validActions, function (action:any) { - return action.key; - }); + return _.map(validActions, (action:any) => action.key); } public permittedActionsWithLinks(allowedActions:any) { - var validActions = _.filter(_.cloneDeep(allowedActions), (action:any) => this.isPermitted(action)); + const validActions = _.filter(_.cloneDeep(allowedActions), (action:any) => this.isPermitted(action)); - var allowed = _.map(validActions, (action:any) => this.linkForAction(action)); + const allowed = _.map(validActions, (action:any) => this.linkForAction(action)); return allowed; } diff --git a/frontend/src/app/features/work-packages/services/work-package.service.ts b/frontend/src/app/features/work-packages/services/work-package.service.ts index 87982e2564..ee0e0c7065 100644 --- a/frontend/src/app/features/work-packages/services/work-package.service.ts +++ b/frontend/src/app/features/work-packages/services/work-package.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,38 +27,37 @@ //++ import { StateService } from '@uirouter/core'; -import { Injectable } from "@angular/core"; -import { HttpClient } from "@angular/common/http"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { UrlParamsHelperService } from "core-app/features/work-packages/components/wp-query/url-params-helper"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HalDeletedEvent, HalEventsService } from "core-app/features/hal/services/hal-events.service"; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HalDeletedEvent, HalEventsService } from 'core-app/features/hal/services/hal-events.service'; @Injectable() export class WorkPackageService { - private text = { - successful_delete: this.I18n.t('js.work_packages.message_successful_bulk_delete') + successful_delete: this.I18n.t('js.work_packages.message_successful_bulk_delete'), }; constructor(private readonly http:HttpClient, - private readonly $state:StateService, - private readonly PathHelper:PathHelperService, - private readonly UrlParamsHelper:UrlParamsHelperService, - private readonly NotificationsService:NotificationsService, - private readonly I18n:I18nService, - private readonly halEvents:HalEventsService) { + private readonly $state:StateService, + private readonly PathHelper:PathHelperService, + private readonly UrlParamsHelper:UrlParamsHelperService, + private readonly NotificationsService:NotificationsService, + private readonly I18n:I18nService, + private readonly halEvents:HalEventsService) { } public performBulkDelete(ids:string[], defaultHandling:boolean) { const params = { - 'ids[]': ids + 'ids[]': ids, }; const promise = this.http .delete( this.PathHelper.workPackagesBulkDeletePath(), - { params: params, withCredentials: true } + { params, withCredentials: true }, ) .toPromise(); @@ -67,7 +66,7 @@ export class WorkPackageService { .then(() => { this.NotificationsService.addSuccess(this.text.successful_delete); - ids.forEach(id => this.halEvents.push({ _type:'WorkPackage', id: id }, { eventType: 'deleted' } as HalDeletedEvent)); + ids.forEach((id) => this.halEvents.push({ _type: 'WorkPackage', id }, { eventType: 'deleted' } as HalDeletedEvent)); if (this.$state.includes('**.list.details.**') && ids.indexOf(this.$state.params.workPackageId) > -1) { @@ -76,7 +75,7 @@ export class WorkPackageService { }) .catch(() => { const urlParams = this.UrlParamsHelper.buildQueryString(params); - window.location.href = this.PathHelper.workPackagesBulkDeletePath() + '?' + urlParams; + window.location.href = `${this.PathHelper.workPackagesBulkDeletePath()}?${urlParams}`; }); } diff --git a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts index b830c5762f..3a67d58af9 100644 --- a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts +++ b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list-item.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, EventEmitter, Input, Output, +} from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { States } from 'core-app/core/states/states.service'; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; @Component({ selector: 'attachment-list-item', - templateUrl: './attachment-list-item.html' + templateUrl: './attachment-list-item.html', }) export class AttachmentListItemComponent { @Input() public resource:HalResource; + @Input() public attachment:any; + @Input() public index:any; + @Input() destroyImmediately = true; @Output() public removeAttachment = new EventEmitter(); @@ -50,13 +55,13 @@ export class AttachmentListItemComponent { public text = { dragHint: this.I18n.t('js.attachments.draggable_hint'), destroyConfirmation: this.I18n.t('js.text_attachment_destroy_confirmation'), - removeFile: (arg:any) => this.I18n.t('js.label_remove_file', arg) + removeFile: (arg:any) => this.I18n.t('js.label_remove_file', arg), }; constructor(protected halNotification:HalResourceNotificationService, - readonly I18n:I18nService, - readonly states:States, - readonly pathHelper:PathHelperService) { + readonly I18n:I18nService, + readonly states:States, + readonly pathHelper:PathHelperService) { } /** @@ -67,9 +72,9 @@ export class AttachmentListItemComponent { const url = this.downloadPath; const previewElement = this.draggableHTML(url); - evt.dataTransfer!.setData("text/plain", url); - evt.dataTransfer!.setData("text/html", previewElement.outerHTML); - evt.dataTransfer!.setData("text/uri-list", url); + evt.dataTransfer!.setData('text/plain', url); + evt.dataTransfer!.setData('text/html', previewElement.outerHTML); + evt.dataTransfer!.setData('text/uri-list', url); evt.dataTransfer!.setDragImage(previewElement, 0, 0); } @@ -77,11 +82,11 @@ export class AttachmentListItemComponent { let el:HTMLImageElement|HTMLAnchorElement; if (this.isImage) { - el = document.createElement('img') as HTMLImageElement; + el = document.createElement('img'); el.src = url; el.textContent = this.fileName; } else { - el = document.createElement('a') as HTMLAnchorElement; + el = document.createElement('a'); el.href = url; el.textContent = this.fileName; } diff --git a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts index 2648e6f680..ad444cfa62 100644 --- a/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts +++ b/frontend/src/app/shared/components/attachments/attachment-list/attachment-list.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,34 +26,39 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { filter } from "rxjs/operators"; -import { States } from "core-app/core/states/states.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { + ChangeDetectorRef, Component, ElementRef, Input, OnInit, +} from '@angular/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { filter } from 'rxjs/operators'; +import { States } from 'core-app/core/states/states.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Component({ selector: 'attachment-list', - templateUrl: './attachment-list.html' + templateUrl: './attachment-list.html', }) export class AttachmentListComponent extends UntilDestroyedMixin implements OnInit { @Input() public resource:HalResource; + @Input() public destroyImmediately = true; trackByHref = AngularTrackingHelpers.trackByHref; attachments:HalResource[] = []; + deletedAttachments:HalResource[] = []; public $element:JQuery; + public $formElement:JQuery; constructor(protected elementRef:ElementRef, - protected states:States, - protected cdRef:ChangeDetectorRef, - protected halResourceService:HalResourceService) { + protected states:States, + protected cdRef:ChangeDetectorRef, + protected halResourceService:HalResourceService) { super(); } @@ -73,7 +78,7 @@ export class AttachmentListComponent extends UntilDestroyedMixin implements OnIn .values$() .pipe( this.untilDestroyed(), - filter(newResource => !!newResource) + filter((newResource) => !!newResource), ) .subscribe((newResource:HalResource) => { this.resource = newResource || this.resource; diff --git a/frontend/src/app/shared/components/attachments/attachments-upload/attachments-upload.component.ts b/frontend/src/app/shared/components/attachments/attachments-upload/attachments-upload.component.ts index 1af2cd1104..2d94cb1bbe 100644 --- a/frontend/src/app/shared/components/attachments/attachments-upload/attachments-upload.component.ts +++ b/frontend/src/app/shared/components/attachments/attachments-upload/attachments-upload.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,15 +28,17 @@ import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Component, ElementRef, Input, ViewChild , OnInit } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { UploadFile } from "core-app/core/file-upload/op-file-upload.service"; +import { + Component, ElementRef, Input, OnInit, ViewChild, +} from '@angular/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { UploadFile } from 'core-app/core/file-upload/op-file-upload.service'; @Component({ selector: 'attachments-upload', - templateUrl: './attachments-upload.html' + templateUrl: './attachments-upload.html', }) export class AttachmentsUploadComponent implements OnInit { @Input() public resource:HalResource; @@ -44,29 +46,30 @@ export class AttachmentsUploadComponent implements OnInit { @ViewChild('hiddenFileInput') public filePicker:ElementRef; public draggingOver = false; + public text:any; + public maxFileSize:number; + public $element:JQuery; constructor(readonly I18n:I18nService, - readonly ConfigurationService:ConfigurationService, - readonly notificationsService:NotificationsService, - protected elementRef:ElementRef, - protected halResourceService:HalResourceService) { + readonly ConfigurationService:ConfigurationService, + readonly notificationsService:NotificationsService, + protected elementRef:ElementRef, + protected halResourceService:HalResourceService) { this.text = { uploadLabel: I18n.t('js.label_add_attachments'), dropFiles: I18n.t('js.label_drop_files'), dropFilesHint: I18n.t('js.label_drop_files_hint'), - foldersWarning: I18n.t('js.label_drop_folders_hint') + foldersWarning: I18n.t('js.label_drop_folders_hint'), }; } ngOnInit() { this.$element = jQuery(this.elementRef.nativeElement); - this.ConfigurationService.initialized.then(() => - this.maxFileSize = this.ConfigurationService.maximumAttachmentFileSize - ); + this.ConfigurationService.initialized.then(() => this.maxFileSize = this.ConfigurationService.maximumAttachmentFileSize); } public triggerFileInput(event:MouseEvent) { @@ -94,7 +97,7 @@ export class AttachmentsUploadComponent implements OnInit { this.draggingOver = false; } - public onDragOver(event:DragEvent) { + public onDragOver(event:DragEvent) { if (this.containsFiles(event.dataTransfer)) { event.dataTransfer!.dropEffect = 'copy'; this.draggingOver = true; @@ -104,7 +107,7 @@ export class AttachmentsUploadComponent implements OnInit { event.stopPropagation(); } - public onDragLeave(event:DragEvent) { + public onDragLeave(event:DragEvent) { this.draggingOver = false; event.preventDefault(); event.stopPropagation(); @@ -118,9 +121,8 @@ export class AttachmentsUploadComponent implements OnInit { private containsFiles(dataTransfer:any) { if (dataTransfer.types.contains) { return dataTransfer.types.contains('Files'); - } else { - return (dataTransfer as DataTransfer).types.indexOf('Files') >= 0; } + return (dataTransfer as DataTransfer).types.indexOf('Files') >= 0; } protected uploadFiles(files:UploadFile[]):void { @@ -129,7 +131,6 @@ export class AttachmentsUploadComponent implements OnInit { files = this.filterFolders(files); if (files.length === 0) { - // If we filtered all files as directories, show a notice if (countBefore > 0) { this.notificationsService.addNotice(this.text.foldersWarning); @@ -148,7 +149,6 @@ export class AttachmentsUploadComponent implements OnInit { */ protected filterFolders(files:UploadFile[]) { return files.filter((file) => { - // Folders never have a mime type if (file.type !== '') { return true; diff --git a/frontend/src/app/shared/components/attachments/attachments.component.ts b/frontend/src/app/shared/components/attachments/attachments.component.ts index cb67f7cf47..281667a0a8 100644 --- a/frontend/src/app/shared/components/attachments/attachments.component.ts +++ b/frontend/src/app/shared/components/attachments/attachments.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,32 +26,37 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, Input, OnInit } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { + Component, ElementRef, Input, OnInit, +} from '@angular/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { States } from 'core-app/core/states/states.service'; import { filter } from 'rxjs/operators'; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; export const attachmentsSelector = 'attachments'; @Component({ selector: attachmentsSelector, - templateUrl: './attachments.html' + templateUrl: './attachments.html', }) export class AttachmentsComponent extends UntilDestroyedMixin implements OnInit { @Input('resource') public resource:HalResource; public $element:JQuery; + public allowUploading:boolean; + public destroyImmediately:boolean; + public text:any; constructor(protected elementRef:ElementRef, - protected I18n:I18nService, - protected states:States, - protected halResourceService:HalResourceService) { + protected I18n:I18nService, + protected states:States, + protected halResourceService:HalResourceService) { super(); this.text = { @@ -83,7 +88,7 @@ export class AttachmentsComponent extends UntilDestroyedMixin implements OnInit this.states.forResource(this.resource)!.changes$() .pipe( this.untilDestroyed(), - filter(newResource => !!newResource) + filter((newResource) => !!newResource), ) .subscribe((newResource:HalResource) => { this.resource = newResource || this.resource; diff --git a/frontend/src/app/shared/components/attachments/authoring/authoring.component.ts b/frontend/src/app/shared/components/attachments/authoring/authoring.component.ts index 3ab08ba68f..f37f2ed492 100644 --- a/frontend/src/app/shared/components/attachments/authoring/authoring.component.ts +++ b/frontend/src/app/shared/components/attachments/authoring/authoring.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,8 +29,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ templateUrl: './authoring.component.html', @@ -40,19 +40,26 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service"; export class AuthoringComponent implements OnInit { // scope: { createdOn: '=', author: '=', showAuthorAsLink: '=', project: '=', activity: '=' }, @Input('createdOn') createdOn:string; + @Input('author') author:HalResource; + @Input('showAuthorAsLink') showAuthorAsLink:boolean; + @Input('project') project:any; + @Input('activity') activity:any; public createdOnTime:any; + public timeago:any; + public time:any; + public userLink:string; public constructor(readonly PathHelper:PathHelperService, - readonly I18n:I18nService, - readonly timezoneService:TimezoneService) { + readonly I18n:I18nService, + readonly timezoneService:TimezoneService) { } @@ -64,10 +71,10 @@ export class AuthoringComponent implements OnInit { } public activityFromPath(from:any) { - var path = this.PathHelper.projectActivityPath(this.project); + let path = this.PathHelper.projectActivityPath(this.project); if (from) { - path += '?from=' + from; + path += `?from=${from}`; } return path; diff --git a/frontend/src/app/shared/components/attachments/openproject-attachments.module.ts b/frontend/src/app/shared/components/attachments/openproject-attachments.module.ts index c51edfc6fb..2c819e03b1 100644 --- a/frontend/src/app/shared/components/attachments/openproject-attachments.module.ts +++ b/frontend/src/app/shared/components/attachments/openproject-attachments.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,15 +27,15 @@ //++ import { NgModule } from '@angular/core'; -import { CommonModule } from "@angular/common"; +import { CommonModule } from '@angular/common'; import { IconModule } from 'core-app/shared/components/icon/icon.module'; import { OpenprojectAccessibilityModule } from 'core-app/shared/directives/a11y/openproject-a11y.module'; -import { AttachmentsComponent } from "./attachments.component"; -import { AttachmentListComponent } from "./attachment-list/attachment-list.component"; -import { AttachmentListItemComponent } from "./attachment-list/attachment-list-item.component"; -import { AttachmentsUploadComponent } from "./attachments-upload/attachments-upload.component"; +import { AttachmentsComponent } from './attachments.component'; +import { AttachmentListComponent } from './attachment-list/attachment-list.component'; +import { AttachmentListItemComponent } from './attachment-list/attachment-list-item.component'; +import { AttachmentsUploadComponent } from './attachments-upload/attachments-upload.component'; import { AuthoringComponent } from './authoring/authoring.component'; @NgModule({ @@ -58,8 +58,7 @@ import { AuthoringComponent } from './authoring/authoring.component'; AttachmentsComponent, AuthoringComponent, - ] + ], }) export class OpenprojectAttachmentsModule { } - diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts index 08708ed716..3b25863db2 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -33,27 +33,29 @@ import { ElementRef, Injector, Input, - OnInit + OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { AttributeHelpTextsService } from './attribute-help-text.service'; -import { AttributeHelpTextModal } from "./attribute-help-text.modal"; +import { AttributeHelpTextModalComponent } from './attribute-help-text.modal'; export const attributeHelpTextSelector = 'attribute-help-text'; @Component({ selector: attributeHelpTextSelector, changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './attribute-help-text.component.html' + templateUrl: './attribute-help-text.component.html', }) export class AttributeHelpTextComponent implements OnInit { // Attribute to show help text for @Input() public attribute:string; + @Input() public additionalLabel?:string; // Scope to search for @Input() public attributeScope:string; + // Load single id entry if given @Input() public helpTextId?:string; @@ -61,16 +63,16 @@ export class AttributeHelpTextComponent implements OnInit { readonly text = { open_dialog: this.I18n.t('js.help_texts.show_modal'), - 'edit': this.I18n.t('js.button_edit'), - 'close': this.I18n.t('js.button_close') + edit: this.I18n.t('js.button_edit'), + close: this.I18n.t('js.button_close'), }; constructor(protected elementRef:ElementRef, - protected attributeHelpTexts:AttributeHelpTextsService, - protected opModalService:OpModalService, - protected cdRef:ChangeDetectorRef, - protected injector:Injector, - protected I18n:I18nService) { + protected attributeHelpTexts:AttributeHelpTextsService, + protected opModalService:OpModalService, + protected cdRef:ChangeDetectorRef, + protected injector:Injector, + protected I18n:I18nService) { } ngOnInit() { @@ -95,7 +97,7 @@ export class AttributeHelpTextComponent implements OnInit { public handleClick(event:Event):void { this.load().then((resource) => { - this.opModalService.show(AttributeHelpTextModal, this.injector, { helpText: resource }); + this.opModalService.show(AttributeHelpTextModalComponent, this.injector, { helpText: resource }); }); event.preventDefault(); @@ -104,9 +106,7 @@ export class AttributeHelpTextComponent implements OnInit { private load() { if (this.helpTextId) { return this.attributeHelpTexts.requireById(this.helpTextId); - } else { - return this.attributeHelpTexts.require(this.attribute, this.attributeScope); } + return this.attributeHelpTexts.require(this.attribute, this.attributeScope); } } - diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.modal.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.modal.ts index 0c06796d97..944446b956 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.modal.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.modal.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, +} from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HelpTextResource } from "core-app/features/hal/resources/help-text-resource"; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HelpTextResource } from 'core-app/features/hal/resources/help-text-resource'; @Component({ templateUrl: './help-text.modal.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AttributeHelpTextModal extends OpModalComponent implements OnInit { - +export class AttributeHelpTextModalComponent extends OpModalComponent implements OnInit { /* Close on escape? */ public closeOnEscape = true; @@ -46,16 +47,16 @@ export class AttributeHelpTextModal extends OpModalComponent implements OnInit { public closeOnOutsideClick = false; readonly text = { - 'edit': this.I18n.t('js.button_edit'), - 'close': this.I18n.t('js.button_close') + edit: this.I18n.t('js.button_edit'), + close: this.I18n.t('js.button_close'), }; public helpText:HelpTextResource = this.locals.helpText!; constructor(@Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef) { + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef, + readonly elementRef:ElementRef) { super(locals, cdRef, elementRef); } @@ -76,7 +77,5 @@ export class AttributeHelpTextModal extends OpModalComponent implements OnInit { } return ''; - } } - diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.module.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.module.ts index c1bcfebcfc..f77f6a8b25 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.module.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.module.ts @@ -1,12 +1,12 @@ -import { NgModule } from "@angular/core"; -import { CommonModule } from "@angular/common"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { OpenprojectAttachmentsModule } from "core-app/shared/components/attachments/openproject-attachments.module"; -import { OpenprojectAccessibilityModule } from "core-app/shared/directives/a11y/openproject-a11y.module"; -import { IconModule } from "core-app/shared/components/icon/icon.module"; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; +import { OpenprojectAccessibilityModule } from 'core-app/shared/directives/a11y/openproject-a11y.module'; +import { IconModule } from 'core-app/shared/components/icon/icon.module'; -import { AttributeHelpTextComponent } from "./attribute-help-text.component"; -import { AttributeHelpTextModal } from "./attribute-help-text.modal"; +import { AttributeHelpTextComponent } from './attribute-help-text.component'; +import { AttributeHelpTextModalComponent } from './attribute-help-text.modal'; @NgModule({ imports: [ @@ -18,12 +18,12 @@ import { AttributeHelpTextModal } from "./attribute-help-text.modal"; ], declarations: [ AttributeHelpTextComponent, - AttributeHelpTextModal, + AttributeHelpTextModalComponent, ], providers: [ ], exports: [ AttributeHelpTextComponent, - ] + ], }) export class AttributeHelpTextModule {} diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts index 03c11ec137..a47af06caa 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,10 +28,10 @@ import { input } from 'reactivestates'; import { Injectable } from '@angular/core'; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { take } from "rxjs/operators"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { HelpTextResource } from "core-app/features/hal/resources/help-text-resource"; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { take } from 'rxjs/operators'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { HelpTextResource } from 'core-app/features/hal/resources/help-text-resource'; @Injectable({ providedIn: 'root' }) export class AttributeHelpTextsService { @@ -67,24 +67,21 @@ export class AttributeHelpTextsService { .helpTexts .values$() .pipe( - take(1) + take(1), ) .toPromise() .then(() => { const value = this.helpTexts.getValueOr([]); - return _.find(value, element => element.id?.toString() === id); + return _.find(value, (element) => element.id?.toString() === id); }); } private load():void { - this.helpTexts.putFromPromiseIfPristine(() => - this.apiV3Service - .help_texts - .get() - .toPromise() - .then((resources:CollectionResource) => resources.elements) - ); - + this.helpTexts.putFromPromiseIfPristine(() => this.apiV3Service + .help_texts + .get() + .toPromise() + .then((resources:CollectionResource) => resources.elements)); } private find(attribute:string, scope:string):HelpTextResource|undefined { diff --git a/frontend/src/app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component.ts b/frontend/src/app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component.ts index 95ca059f7c..08cd124286 100644 --- a/frontend/src/app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component.ts +++ b/frontend/src/app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; -import { NgSelectComponent } from "@ng-select/ng-select"; +import { + Component, ElementRef, OnInit, ViewChild, +} from '@angular/core'; +import { NgSelectComponent } from '@ng-select/ng-select'; type SelectItem = { label:string, value:string, selected?:boolean }; @@ -49,7 +51,7 @@ export const autocompleteSelectDecorationSelector = 'autocomplete-select-decorat `, - selector: autocompleteSelectDecorationSelector + selector: autocompleteSelectDecorationSelector, }) export class AutocompleteSelectDecorationComponent implements OnInit { @ViewChild(NgSelectComponent) public ngSelectComponent:NgSelectComponent; @@ -103,12 +105,12 @@ export class AutocompleteSelectDecorationComponent implements OnInit { } setInitialSelection(data:SelectItem[]) { - this.updateSelection(data.filter(element => element.selected)); + this.updateSelection(data.filter((element) => element.selected)); } updateSelection(items:SelectItem|SelectItem[]) { this.selected = items; - items = _.castArray(items) as SelectItem[]; + items = _.castArray(items); this.removeCurrentSyncedFields(); items.forEach((el:SelectItem) => { diff --git a/frontend/src/app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive.ts b/frontend/src/app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive.ts index 89a0a81855..073d642112 100644 --- a/frontend/src/app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive.ts +++ b/frontend/src/app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive.ts @@ -1,11 +1,11 @@ -import { Directive } from "@angular/core"; +import { Directive } from '@angular/core'; @Directive({ - selector: "[op-autocompleter-footer-tmp]" + selector: '[op-autocompleter-footer-tmp]', }) - // A Directive to be used on the option template of - // ng-select (what will be shown in each row of drop down) - // it is used when you want to inject a different template +// A Directive to be used on the option template of +// ng-select (what will be shown in each row of drop down) +// it is used when you want to inject a different template export class OpAutocompleterFooterTemplateDirective { - -} \ No newline at end of file + +} diff --git a/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts index 298ab5b640..4b7564f921 100644 --- a/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -31,24 +31,24 @@ import { ChangeDetectorRef, Component, EventEmitter, + Injector, Input, Output, ViewChild, - Injector, } from '@angular/core'; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { OpInviteUserModalService } from "core-app/features/invite-user-modal/invite-user-modal.service"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { AddTagFn } from "@ng-select/ng-select/lib/ng-select.component"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { OpInviteUserModalService } from 'core-app/features/invite-user-modal/invite-user-modal.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { AddTagFn } from '@ng-select/ng-select/lib/ng-select.component'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { Subject } from 'rxjs'; -import { PrincipalHelper } from "core-app/shared/components/principal/principal-helper"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { filter } from "rxjs/operators"; +import { PrincipalHelper } from 'core-app/shared/components/principal/principal-helper'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { filter } from 'rxjs/operators'; export interface CreateAutocompleterValueOption { name:string; @@ -62,37 +62,59 @@ export interface CreateAutocompleterValueOption { }) export class CreateAutocompleterComponent extends UntilDestroyedMixin implements AfterViewInit { @Input() public availableValues:CreateAutocompleterValueOption[]; + @Input() public appendTo:string; + @Input() public resource:HalResource; + @Input() public model:any; + @Input() public required = false; + @Input() public disabled = false; + @Input() public finishedLoading = false; + @Input() public id = ''; + @Input() public classes = ''; + @Input() public typeahead?:Subject; + @Input() public hideSelected = false; + @Input() public showAddNewButton:boolean; @Output() public onChange = new EventEmitter(); + @Output() public onKeydown = new EventEmitter(); + @Output() public onOpen = new EventEmitter(); + @Output() public onClose = new EventEmitter(); + @Output() public onAfterViewInit = new EventEmitter(); - @Output() public onAddNew = new EventEmitter(); + @Output() public onAddNew = new EventEmitter(); @ViewChild(NgSelectComponent) public ngSelectComponent:NgSelectComponent; @InjectField() readonly opInviteUserModalService:OpInviteUserModalService; + @InjectField() readonly I18n:I18nService; + @InjectField() readonly cdRef:ChangeDetectorRef; + @InjectField() readonly currentProject:CurrentProjectService; + @InjectField() readonly pathHelper:PathHelperService; public compareByHref = AngularTrackingHelpers.compareByHref; + public text:{ [key:string]:string } = {}; + public createAllowed:boolean|AddTagFn = false; + private _openDirectly = false; constructor(readonly injector:Injector) { @@ -107,7 +129,7 @@ export class CreateAutocompleterComponent extends UntilDestroyedMixin implements this.opInviteUserModalService.close .pipe( this.untilDestroyed(), - filter(user => !!user) + filter((user) => !!user), ) .subscribe((user:HalResource) => { this.onChange.emit(user); diff --git a/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts b/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts index 1382229d76..a325179a85 100644 --- a/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts +++ b/frontend/src/app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component.ts @@ -6,15 +6,15 @@ import { Input, OnInit, Output, - ViewChild -} from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { DragulaService, Group } from "ng2-dragula"; -import { DomAutoscrollService } from "core-app/shared/helpers/drag-and-drop/dom-autoscroll.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { merge } from "rxjs"; -import { DomHelpers } from "core-app/shared/helpers/dom/set-window-cursor.helper"; + ViewChild, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { DragulaService, Group } from 'ng2-dragula'; +import { DomAutoscrollService } from 'core-app/shared/helpers/drag-and-drop/dom-autoscroll.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { merge } from 'rxjs'; +import { DomHelpers } from 'core-app/shared/helpers/dom/set-window-cursor.helper'; export interface DraggableOption { name:string; @@ -25,7 +25,7 @@ export interface DraggableOption { selector: 'draggable-autocompleter', templateUrl: './draggable-autocomplete.component.html', styleUrls: ['./draggable-autocomplete.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DraggableAutocompleteComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { /** Options to show in the autocompleter */ @@ -44,16 +44,17 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen availableOptions:DraggableOption[] = []; private autoscroll:any; + private columnsGroup:Group; @ViewChild('ngSelectComponent') public ngSelectComponent:NgSelectComponent; text = { - placeholder: this.I18n.t('js.label_add_columns') + placeholder: this.I18n.t('js.label_add_columns'), }; constructor(readonly I18n:I18nService, - readonly dragula:DragulaService) { + readonly dragula:DragulaService) { super(); } @@ -70,8 +71,8 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen // Reset cursor when cancel or dropped merge( - this.dragula.drop("columns"), - this.dragula.cancel("columns") + this.dragula.drop('columns'), + this.dragula.cancel('columns'), ) .pipe(this.untilDestroyed()) .subscribe(() => DomHelpers.setBodyCursor('auto')); @@ -80,16 +81,17 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen const that = this; this.autoscroll = new DomAutoscrollService( [ - document.getElementById('content-wrapper')! + document.getElementById('content-wrapper')!, ], { margin: 25, maxSpeed: 10, scrollWhenOutside: true, - autoScroll: function (this:any) { + autoScroll(this:any) { return this.down && that.columnsGroup.drake.dragging; - } - }); + }, + }, + ); } ngAfterViewInit():void { @@ -116,7 +118,7 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen } remove(item:DraggableOption) { - this.selected = this.selected.filter(selected => selected.id !== item.id); + this.selected = this.selected.filter((selected) => selected.id !== item.id); } get selected() { @@ -143,6 +145,6 @@ export class DraggableAutocompleteComponent extends UntilDestroyedMixin implemen private updateAvailableOptions() { this.availableOptions = this.options - .filter(item => !this.selected.find(selected => selected.id === item.id)); + .filter((item) => !this.selected.find((selected) => selected.id === item.id)); } } diff --git a/frontend/src/app/shared/components/autocompleter/lazyloaded/lazyloaded-autocompleter.ts b/frontend/src/app/shared/components/autocompleter/lazyloaded/lazyloaded-autocompleter.ts index 9d72e6c9b7..f9ed0417bb 100644 --- a/frontend/src/app/shared/components/autocompleter/lazyloaded/lazyloaded-autocompleter.ts +++ b/frontend/src/app/shared/components/autocompleter/lazyloaded/lazyloaded-autocompleter.ts @@ -56,7 +56,7 @@ export abstract class ILazyAutocompleterBridge { private fuzzySearch(items:IAutocompleteItem[], term:string) { if (term === '') { return items; - } else if (term.length >= 3) { + } if (term.length >= 3) { const literalMatches = this.literalSearch(items, term); if (literalMatches.length > 0) { @@ -79,7 +79,7 @@ export abstract class ILazyAutocompleterBridge { const results:IAutocompleteItem[] = []; const str:string = term.toLowerCase(); - items.forEach(e => { + items.forEach((e) => { if (e.label.toLowerCase().indexOf(str) !== -1) { results.push(e); } @@ -98,11 +98,11 @@ export abstract class ILazyAutocompleterBridge { // By default, set all to match const results:IAutocompleteItem[] = []; - matched.forEach(el => { + matched.forEach((el) => { results.push({ label: el.label, object: el.object, - render: 'match' + render: 'match', } as IAutocompleteItem); }); @@ -121,7 +121,7 @@ export abstract class ILazyAutocompleterBridge { distance: 10000, // allow the term to appear anywhere maxPatternLength: 16, minMatchCharLength: 2, - keys: ['label'] as any + keys: ['label'] as any, }; this.fuseInstance = new Fuse(items, options); @@ -132,7 +132,7 @@ export abstract class ILazyAutocompleterBridge { return { delay: 50, - source: function (request:any, response:any) { + source(request:any, response:any) { const fuzzyResults = ctrl.fuzzySearch(autocompleteValues, request.term); response(ctrl.augmentedResultSet(autocompleteValues, fuzzyResults)); }, @@ -146,7 +146,7 @@ export abstract class ILazyAutocompleterBridge { ctrl.onNoResultsFound(event, ui); }, autoFocus: true, - minLength: 0 + minLength: 0, }; } } @@ -158,23 +158,23 @@ export namespace LazyLoadedAutocompleter { * @param ul */ function isScrollbarBottom(container:JQuery) { - var height = container.outerHeight()!; - var scrollHeight = container[0].scrollHeight; - var scrollTop = container.scrollTop()!; + const height = container.outerHeight()!; + const { scrollHeight } = container[0]; + const scrollTop = container.scrollTop()!; return scrollTop >= (scrollHeight - height); } export function register(name:string, ctrl:ILazyAutocompleterBridge) { jQuery.widget(`custom.${name}`, jQuery.ui.autocomplete, { - _create: function (this:any) { + _create(this:any) { ctrl.currentPage = 0; this._super(); this.widget().menu('option', 'items', '> .ui-matched-item'); this._search(''); }, - _renderMenu: function (this:any, ul:HTMLElement, items:IAutocompleteItem[]) { - //remove scroll event to prevent attaching multiple scroll events to one container element + _renderMenu(this:any, ul:HTMLElement, items:IAutocompleteItem[]) { + // remove scroll event to prevent attaching multiple scroll events to one container element jQuery(ul).unbind('scroll'); this._renderLazyMenu(ul, items); @@ -192,7 +192,7 @@ export namespace LazyLoadedAutocompleter { } // Insert elements of this page - jQuery.each(pageElements, function (index, item) { + jQuery.each(pageElements, (index, item) => { widget._renderItemData(ul, item); }); @@ -217,9 +217,9 @@ export namespace LazyLoadedAutocompleter { return Math.ceil(items.length / ctrl.maxItemsPerPage); }, - _repositionMenu: function (this:any, container:JQuery) { + _repositionMenu(this:any, container:JQuery) { const widget = this; - const menu = widget.menu; + const { menu } = widget; menu.refresh(); @@ -232,12 +232,12 @@ export namespace LazyLoadedAutocompleter { } }, - _resizeMenu: function (this:any) { - var ul = this.menu.element; + _resizeMenu(this:any) { + const ul = this.menu.element; ul.outerWidth(this.element.outerWidth()); }, - _renderItem: function (this:any, ul:JQuery, item:IAutocompleteItem) { + _renderItem(this:any, ul:JQuery, item:IAutocompleteItem) { const term = this.element.val(); const disabled = item.render === 'disabled'; const div = jQuery('
    ').addClass('ui-menu-item-wrapper'); @@ -257,7 +257,7 @@ export namespace LazyLoadedAutocompleter { return element; }, - _renderLazyMenu: function (this:any, ul:Element, items:IAutocompleteItem[]) { + _renderLazyMenu(this:any, ul:Element, items:IAutocompleteItem[]) { const widget = this; const container = jQuery(ul) as JQuery; const pages = this._pages(items); @@ -268,7 +268,7 @@ export namespace LazyLoadedAutocompleter { widget._renderMenuPage(ul, items, 0); - container.scroll(function () { + container.scroll(() => { if (isScrollbarBottom(container)) { if (++ctrl.currentPage >= pages) { return; @@ -281,7 +281,7 @@ export namespace LazyLoadedAutocompleter { widget._repositionMenu(ul); } }); - } + }, }); } } diff --git a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts index 931360252c..da43b7d005 100644 --- a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts @@ -1,33 +1,32 @@ -import { Observable } from "rxjs"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HttpClient, HttpParams } from "@angular/common/http"; -import { Component } from "@angular/core"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { Observable } from 'rxjs'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Component } from '@angular/core'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { UserAutocompleteItem, UserAutocompleterComponent, -} from "core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component"; -import { URLParamsEncoder } from "core-app/features/hal/services/url-params-encoder"; - +} from 'core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component'; +import { URLParamsEncoder } from 'core-app/features/hal/services/url-params-encoder'; export const membersAutocompleterSelector = 'members-autocompleter'; @Component({ templateUrl: '../user-autocompleter/user-autocompleter.component.html', - selector: membersAutocompleterSelector + selector: membersAutocompleterSelector, }) export class MembersAutocompleterComponent extends UserAutocompleterComponent { @InjectField() http:HttpClient; + @InjectField() pathHelper:PathHelperService; protected getAvailableUsers(url:string, searchTerm:any):Observable { return this.http .get(url, - { - params: new HttpParams({ encoder: new URLParamsEncoder(), fromObject: { q: searchTerm } }), - responseType: 'json', - headers: { 'Content-Type': 'application/json; charset=utf-8' } - }, - ); + { + params: new HttpParams({ encoder: new URLParamsEncoder(), fromObject: { q: searchTerm } }), + responseType: 'json', + headers: { 'Content-Type': 'application/json; charset=utf-8' }, + }); } } diff --git a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members.module.ts b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members.module.ts index cd76059548..6e4c95ba8d 100644 --- a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members.module.ts +++ b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { NgModule } from "@angular/core"; -import { MembersAutocompleterComponent } from "core-app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component"; -import { NgSelectModule } from "@ng-select/ng-select"; -import { OPSharedModule } from "core-app/shared/shared.module"; +import { NgModule } from '@angular/core'; +import { MembersAutocompleterComponent } from 'core-app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { OPSharedModule } from 'core-app/shared/shared.module'; @NgModule({ imports: [ OPSharedModule, - NgSelectModule + NgSelectModule, ], - exports: [ ], + exports: [], declarations: [ - MembersAutocompleterComponent - ] + MembersAutocompleterComponent, + ], }) export class OpenprojectMembersModule { } diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive.ts index 1a8a74b9b9..52ec231648 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive.ts @@ -1,11 +1,11 @@ -import { Directive } from "@angular/core"; +import { Directive } from '@angular/core'; @Directive({ - selector: "[op-autocompleter-header-tmp]" + selector: '[op-autocompleter-header-tmp]', }) - // A Directive to be used on the option template of - // ng-select (what will be shown in each row of drop down) - // it is used when you want to inject a different template +// A Directive to be used on the option template of +// ng-select (what will be shown in each row of drop down) +// it is used when you want to inject a different template export class OpAutocompleterHeaderTemplateDirective { - -} \ No newline at end of file + +} diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive.ts index 5bde5268ed..868eb4e766 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive.ts @@ -1,11 +1,11 @@ -import { Directive } from "@angular/core"; +import { Directive } from '@angular/core'; @Directive({ - selector: "[op-autocompleter-label-tmp]" + selector: '[op-autocompleter-label-tmp]', }) - // A Directive to be used on the label template of - // ng-select (what will be shown in the input box after selecting an item) - // it is used when you want to inject a different template +// A Directive to be used on the label template of +// ng-select (what will be shown in the input box after selecting an item) +// it is used when you want to inject a different template export class OpAutocompleterLabelTemplateDirective { -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive.ts index 7ebd63db66..b4c4d4cee4 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive.ts @@ -1,11 +1,11 @@ -import { Directive } from "@angular/core"; +import { Directive } from '@angular/core'; @Directive({ - selector: "[op-autocompleter-option-tmp]" + selector: '[op-autocompleter-option-tmp]', }) - // A Directive to be used on the option template of - // ng-select (what will be shown in each row of drop down) - // it is used when you want to inject a different template +// A Directive to be used on the option template of +// ng-select (what will be shown in each row of drop down) +// it is used when you want to inject a different template export class OpAutocompleterOptionTemplateDirective { - -} \ No newline at end of file + +} diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts index a0bd06867e..862812e04b 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts @@ -1,113 +1,192 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild, TemplateRef, ContentChild, AfterViewInit, NgZone} from '@angular/core'; -import {DropdownPosition, NgSelectComponent} from '@ng-select/ng-select'; +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChild, + EventEmitter, + Input, + NgZone, + Output, + TemplateRef, + ViewChild, +} from '@angular/core'; +import { DropdownPosition, NgSelectComponent } from '@ng-select/ng-select'; import { Observable, of, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; import { GroupValueFn } from '@ng-select/ng-select/lib/ng-select.component'; -import { OpAutocompleterOptionTemplateDirective } from "./directives/op-autocompleter-option-template.directive"; -import { OpAutocompleterLabelTemplateDirective } from "./directives/op-autocompleter-label-template.directive"; -import { OpAutocompleterHeaderTemplateDirective } from "./directives/op-autocompleter-header-template.directive"; -import { OpAutocompleterService } from "./services/op-autocompleter.service"; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { AngularTrackingHelpers } from "core-app/shared/helpers/angular/tracking-functions"; -import { OpAutocompleterFooterTemplateDirective } from "core-app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive"; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { AngularTrackingHelpers } from 'core-app/shared/helpers/angular/tracking-functions'; +import { OpAutocompleterFooterTemplateDirective } from 'core-app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive'; +import { OpAutocompleterService } from './services/op-autocompleter.service'; +import { OpAutocompleterHeaderTemplateDirective } from './directives/op-autocompleter-header-template.directive'; +import { OpAutocompleterLabelTemplateDirective } from './directives/op-autocompleter-label-template.directive'; +import { OpAutocompleterOptionTemplateDirective } from './directives/op-autocompleter-option-template.directive'; @Component({ selector: 'op-autocompleter', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl:'./op-autocompleter.component.html', + templateUrl: './op-autocompleter.component.html', styleUrls: ['./op-autocompleter.component.sass'], - providers: [OpAutocompleterService] + providers: [OpAutocompleterService], }) // It is component that you can use whenever you need an autocompleter // it has all inputs and outputs of ng-select // in order to use it, you only need to pass the data type and its filters // you also can change the value of ng-select default options by changing @inputs and @outputs -export class OpAutocompleterComponent extends UntilDestroyedMixin implements AfterViewInit{ - +export class OpAutocompleterComponent extends UntilDestroyedMixin implements AfterViewInit { @Input() public filters?:IAPIFilter[] = []; + @Input() public resource:resource; + @Input() public model?:any; + @Input() public searchKey?:string = ''; + @Input() public defaulData?:boolean = false; + @Input() public focusDirectly?:boolean = true; + @Input() public fetchDataDirectly?:boolean = false; + @Input() public labelRequired?:boolean = true; + @Input() public name?:string; + @Input() public required?:boolean = false; + @Input() public disabled?:string; + @Input() public searchable?:boolean = true; + @Input() public clearable?:boolean = true; + @Input() public addTag?:boolean = false; + @Input() public id?:string; + @Input() public configOptions?:IOPAutocompleterOptions[]; + @Input() public clearSearchOnAdd?:boolean = true; + @Input() public classes?:string; + @Input() public multiple?:boolean = false; + @Input() public openDirectly?:boolean = false; + @Input() public bindLabel?:string; + @Input() public bindValue?:string; + @Input() public markFirst ? = true; + @Input() public placeholder?:string = this.I18n.t('js.autocompleter.placeholder'); + @Input() public notFoundText?:string = this.I18n.t('js.autocompleter.notFoundText'); + @Input() public typeToSearchText?:string = this.I18n.t('js.autocompleter.typeToSearchText'); + @Input() public addTagText?:string; + @Input() public loadingText?:string; + @Input() public clearAllText?:string; + @Input() public appearance?:string; + @Input() public dropdownPosition?:DropdownPosition = 'auto'; + @Input() public appendTo?:string; + @Input() public loading?:boolean = false; + @Input() public closeOnSelect?:boolean = true; + @Input() public hideSelected?:boolean = false; + @Input() public selectOnTab?:boolean = false; + @Input() public openOnEnter?:boolean; + @Input() public maxSelectedItems?:number; + @Input() public groupBy?:string | Function; + @Input() public groupValue?:GroupValueFn; + @Input() public bufferAmount ? = 4; + @Input() public virtualScroll?:boolean; + @Input() public selectableGroup?:boolean = false; + @Input() public selectableGroupAsModel?:boolean = true; + @Input() public searchFn ? = null; + @Input() public trackByFn ? = null; + @Input() public clearOnBackspace?:boolean = true; + @Input() public labelForId ? = null; + @Input() public inputAttrs?:{ [key:string]:string } = {}; + @Input() public tabIndex?:number; + @Input() public readonly?:boolean = false; + @Input() public searchWhileComposing?:boolean = true; + @Input() public minTermLength ? = 0; + @Input() public editableSearchTerm?:boolean = false; + @Input() public keyDownFn ? = (_:KeyboardEvent) => true; + @Input() public typeahead?:Subject; + // a function for setting the options of ng-select - @Input() public getOptionsFn: (searchTerm:string) => any; + @Input() public getOptionsFn:(searchTerm:string) => any; @Output() public open = new EventEmitter(); + @Output() public close = new EventEmitter(); + @Output() public change = new EventEmitter(); + @Output() public focus = new EventEmitter(); + @Output() public blur = new EventEmitter(); + @Output() public search = new EventEmitter<{ term:string, items:any[] }>(); + @Output() public keydown = new EventEmitter(); + @Output() public clear = new EventEmitter(); + @Output() public add = new EventEmitter(); + @Output() public remove = new EventEmitter(); + @Output() public scroll = new EventEmitter<{ start:number; end:number }>(); + @Output() public scrollToEnd = new EventEmitter(); public compareByHrefOrString = AngularTrackingHelpers.compareByHrefOrString; + public active:Set; public searchInput$ = new Subject(); - public results$ :any; + public results$:any; public isLoading = false; - @ViewChild('ngSelectInstance') ngSelectInstance: NgSelectComponent; + @ViewChild('ngSelectInstance') ngSelectInstance:NgSelectComponent; @ContentChild(OpAutocompleterOptionTemplateDirective, { read: TemplateRef }) optionTemplate:TemplateRef; @@ -125,7 +204,7 @@ export class OpAutocompleterComponent extends UntilDestroyedMixin implements Aft readonly opAutocompleterService:OpAutocompleterService, readonly cdRef:ChangeDetectorRef, readonly ngZone:NgZone, - private readonly I18n:I18nService + private readonly I18n:I18nService, ) { super(); } @@ -137,40 +216,35 @@ export class OpAutocompleterComponent extends UntilDestroyedMixin implements Aft this.ngZone.runOutsideAngular(() => { setTimeout(() => { - this.results$ = this.configOptions ? (this.searchInput$.pipe( debounceTime(250), distinctUntilChanged(), - switchMap(queryString => this.getOptionsItems(queryString)) + switchMap((queryString) => this.getOptionsItems(queryString)), )) : this.defaulData ? (this.searchInput$.pipe( debounceTime(250), distinctUntilChanged(), - switchMap(queryString => this.opAutocompleterService.loadData(queryString, this.resource, this.filters, this.searchKey)) + switchMap((queryString) => this.opAutocompleterService.loadData(queryString, this.resource, this.filters, this.searchKey)), )) : (this.searchInput$.pipe( debounceTime(250), distinctUntilChanged(), - switchMap(queryString => this.getOptionsFn(queryString)) + switchMap((queryString) => this.getOptionsFn(queryString)), )); if (this.fetchDataDirectly) { this.results$ = this.defaulData ? (this.opAutocompleterService.loadData('', this.resource, this.filters, this.searchKey)) : (this.getOptionsFn('')); } - if(this.openDirectly) { + if (this.openDirectly) { this.ngSelectInstance.open(); this.ngSelectInstance.focus(); - } - else if (this.focusDirectly) { + } else if (this.focusDirectly) { this.ngSelectInstance.focus(); } - }, 25); }); - } public repositionDropdown() { - if (this.ngSelectInstance) { setTimeout(() => { this.cdRef.detectChanges(); @@ -192,12 +266,14 @@ export class OpAutocompleterComponent extends UntilDestroyedMixin implements Aft this.open.emit(); } - public getOptionsItems(searchKey :string):Observable{ - return of(this.configOptions?.filter(element => element.name.includes(searchKey))) ; + public getOptionsItems(searchKey:string):Observable { + return of(this.configOptions?.filter((element) => element.name.includes(searchKey))); } + public closeSelect() { this.ngSelectInstance && this.ngSelectInstance.close(); } + public openSelect() { this.ngSelectInstance && this.ngSelectInstance.open(); } @@ -209,13 +285,15 @@ export class OpAutocompleterComponent extends UntilDestroyedMixin implements Aft }, 25); }); } - public closed(val:any) { + public closed(val:any) { this.close.emit(); } + public changed(val:any) { this.change.emit(val); } + public searched(val:any) { this.search.emit(val); } @@ -235,19 +313,24 @@ export class OpAutocompleterComponent extends UntilDestroyedMixin implements Aft public keydowned(val:any) { this.keydown.emit(val); } + public added(val:any) { this.add.emit(val); } + public removed(val:any) { this.remove.emit(val); } + public scrolled(val:any) { this.scroll.emit(val); } + public scrolledToEnd(val:any) { this.scrollToEnd.emit(val); } + public highlighting(property:string, id:string) { return Highlighting.inlineClass(property, id); } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts index 5af0767d1f..ac98c0ead5 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { OpAutocompleterComponent } from "./op-autocompleter.component"; -import { OpAutocompleterService } from "./services/op-autocompleter.service"; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { of } from "rxjs"; -import { NgSelectComponent} from '@ng-select/ng-select'; -import { NgSelectModule } from "@ng-select/ng-select"; +import { of } from 'rxjs'; +import { NgSelectModule } from '@ng-select/ng-select'; + import { By } from '@angular/platform-browser'; +import { OpAutocompleterService } from './services/op-autocompleter.service'; +import { OpAutocompleterComponent } from './op-autocompleter.component'; function triggerKeyDownEvent(element:DebugElement, which:number, key = ''):void { element.triggerEventHandler('keydown', { - which: which, - key: key, + which, + key, preventDefault: () => { }, }); @@ -77,7 +77,8 @@ describe('autocompleter', () => { }) .overrideComponent( OpAutocompleterComponent, - { set: { providers: [{ provide: OpAutocompleterService, useValue: opAutocompleterServiceSpy }] } }) + { set: { providers: [{ provide: OpAutocompleterService, useValue: opAutocompleterServiceSpy }] } }, + ) .compileComponents(); fixture = TestBed.createComponent(OpAutocompleterComponent); @@ -110,7 +111,7 @@ describe('autocompleter', () => { fixture.componentInstance.ngAfterViewInit(); tick(1000); fixture.detectChanges(); - var select = fixture.componentInstance.ngSelectInstance as NgSelectComponent; + const select = fixture.componentInstance.ngSelectInstance; expect(fixture.componentInstance.ngSelectInstance.isOpen).toBeFalse(); fixture.componentInstance.ngSelectInstance.open(); fixture.componentInstance.ngSelectInstance.focus(); @@ -126,6 +127,5 @@ describe('autocompleter', () => { fixture.componentInstance.resource, fixture.componentInstance.filters, fixture.componentInstance.searchKey); expect(fixture.componentInstance.ngSelectInstance.itemsList.items.length).toEqual(2); - })); -}); \ No newline at end of file +}); diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts index 2993bab4fd..c5eb9253aa 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/services/op-autocompleter.service.ts @@ -2,34 +2,33 @@ import { Injectable } from '@angular/core'; import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; import { map } from 'rxjs/operators'; import { APIv3ResourceCollection } from 'core-app/core/apiv3/paths/apiv3-resource'; -import { UserResource } from "core-app/features/hal/resources/user-resource"; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; import { APIv3UserPaths } from 'core-app/core/apiv3/endpoints/users/apiv3-user-paths'; import { APIV3WorkPackagePaths } from 'core-app/core/apiv3/endpoints/work_packages/api-v3-work-package-paths'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import {HalResource} from "core-app/features/hal/resources/hal-resource"; -import {APIV3Service} from "core-app/core/apiv3/api-v3.service"; -import {Observable} from "rxjs"; -import {UntilDestroyedMixin} from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @Injectable() export class OpAutocompleterService extends UntilDestroyedMixin { - constructor( private apiV3Service:APIV3Service, ) { super(); } + // A method for fetching data with different resource type and different filter - public loadAvailable(matching:string, resource:resource, filters?: IAPIFilter[], searchKey?:string):Observable { + public loadAvailable(matching:string, resource:resource, filters?:IAPIFilter[], searchKey?:string):Observable { const finalFilters:ApiV3FilterBuilder = this.createFilters(filters ?? [], matching, searchKey); const filteredData = (this.apiV3Service[resource] as APIv3ResourceCollection) .filtered(finalFilters).get() - .pipe(map(collection => collection.elements)); - return filteredData; - + .pipe(map((collection) => collection.elements)); + return filteredData; } // A method for building filters @@ -49,13 +48,13 @@ export class OpAutocompleterService extends UntilDestroyedMixin { // If you need to fetch our default date sources like work_packages or users, // you should use the default method (loadAvailable), otherwise you should implement a function for // your desired resourse - public loadData(matching:string, resource:resource, filters?:IAPIFilter[], searchKey?:string) { + public loadData(matching:string, resource:resource, filters?:IAPIFilter[], searchKey?:string) { switch (resource) { - // in this case we can add more functions for fetching usual resources + // in this case we can add more functions for fetching usual resources default: { - return this.loadAvailable(matching, resource, filters, searchKey); - break; + return this.loadAvailable(matching, resource, filters, searchKey); + break; } - } + } } } diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/typings.d.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/typings.d.ts index 568113f453..7e7e6283f0 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/typings.d.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/typings.d.ts @@ -1,12 +1,12 @@ interface IAPIFilter { name:string; operator:FilterOperator; - values:unknown[]|boolean; -}; + values:ApiV3FilterValueType[]; +} interface IOPAutocompleterOptions { - id: number; + id:number; name:string; -}; +} -type resource = 'work_packages' | 'users'; \ No newline at end of file +type resource = 'work_packages' | 'users'; diff --git a/frontend/src/app/shared/components/autocompleter/openproject-autocompleter.module.ts b/frontend/src/app/shared/components/autocompleter/openproject-autocompleter.module.ts index 5411307481..a8b332e568 100644 --- a/frontend/src/app/shared/components/autocompleter/openproject-autocompleter.module.ts +++ b/frontend/src/app/shared/components/autocompleter/openproject-autocompleter.module.ts @@ -1,24 +1,24 @@ -import { NgModule } from "@angular/core"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; -import { NgSelectModule } from "@ng-select/ng-select"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { DraggableAutocompleteComponent } from "core-app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component"; -import { DynamicModule } from "ng-dynamic-component"; -import { ColorsAutocompleter } from "core-app/shared/components/colors/colors-autocompleter.component"; -import { WorkPackageAutocompleterComponent } from "core-app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component"; -import { TimeEntryWorkPackageAutocompleterComponent } from "core-app/shared/components/autocompleter/te-work-package-autocompleter/te-work-package-autocompleter.component"; -import { AutocompleteSelectDecorationComponent } from "core-app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component"; -import { VersionAutocompleterComponent } from "core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component"; -import { UserAutocompleterComponent } from "core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component"; -import { CommonModule } from "@angular/common"; -import { OpenprojectInviteUserModalModule } from "core-app/features/invite-user-modal/invite-user-modal.module"; -import { DragulaModule } from "ng2-dragula"; -import {OpAutocompleterComponent} from "core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component"; -import {OpAutocompleterOptionTemplateDirective} from "core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive"; -import {OpAutocompleterLabelTemplateDirective} from "core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive"; -import {OpAutocompleterHeaderTemplateDirective} from "core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive"; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; -import { OpAutocompleterFooterTemplateDirective } from "core-app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive"; +import { NgModule } from '@angular/core'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { DraggableAutocompleteComponent } from 'core-app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component'; +import { DynamicModule } from 'ng-dynamic-component'; +import { ColorsAutocompleterComponent } from 'core-app/shared/components/colors/colors-autocompleter.component'; +import { WorkPackageAutocompleterComponent } from 'core-app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component'; +import { TimeEntryWorkPackageAutocompleterComponent } from 'core-app/shared/components/autocompleter/te-work-package-autocompleter/te-work-package-autocompleter.component'; +import { AutocompleteSelectDecorationComponent } from 'core-app/shared/components/autocompleter/autocomplete-select-decoration/autocomplete-select-decoration.component'; +import { VersionAutocompleterComponent } from 'core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component'; +import { UserAutocompleterComponent } from 'core-app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component'; +import { CommonModule } from '@angular/common'; +import { OpenprojectInviteUserModalModule } from 'core-app/features/invite-user-modal/invite-user-modal.module'; +import { DragulaModule } from 'ng2-dragula'; +import { OpAutocompleterComponent } from 'core-app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component'; +import { OpAutocompleterOptionTemplateDirective } from 'core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-option-template.directive'; +import { OpAutocompleterLabelTemplateDirective } from 'core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-label-template.directive'; +import { OpAutocompleterHeaderTemplateDirective } from 'core-app/shared/components/autocompleter/op-autocompleter/directives/op-autocompleter-header-template.directive'; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; +import { OpAutocompleterFooterTemplateDirective } from 'core-app/shared/components/autocompleter/autocompleter-footer-template/op-autocompleter-footer-template.directive'; export const OPENPROJECT_AUTOCOMPLETE_COMPONENTS = [ CreateAutocompleterComponent, @@ -27,7 +27,7 @@ export const OPENPROJECT_AUTOCOMPLETE_COMPONENTS = [ TimeEntryWorkPackageAutocompleterComponent, DraggableAutocompleteComponent, UserAutocompleterComponent, - ColorsAutocompleter, + ColorsAutocompleterComponent, AutocompleteSelectDecorationComponent, OpAutocompleterComponent, OpAutocompleterOptionTemplateDirective, @@ -45,9 +45,9 @@ export const OPENPROJECT_AUTOCOMPLETE_COMPONENTS = [ NgSelectModule, DragulaModule, - DynamicModule.withComponents(OPENPROJECT_AUTOCOMPLETE_COMPONENTS) + DynamicModule.withComponents(OPENPROJECT_AUTOCOMPLETE_COMPONENTS), ], exports: OPENPROJECT_AUTOCOMPLETE_COMPONENTS, - declarations: OPENPROJECT_AUTOCOMPLETE_COMPONENTS + declarations: OPENPROJECT_AUTOCOMPLETE_COMPONENTS, }) export class OpenprojectAutocompleterModule { } diff --git a/frontend/src/app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component.ts b/frontend/src/app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component.ts index 81e97f3638..681847f8fa 100644 --- a/frontend/src/app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component.ts +++ b/frontend/src/app/shared/components/autocompleter/project-menu-autocomplete/project-menu-autocomplete.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,14 +29,16 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { IAutocompleteItem, - ILazyAutocompleterBridge + ILazyAutocompleterBridge, } from 'core-app/shared/components/autocompleter/lazyloaded/lazyloaded-autocompleter'; -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; -import { LinkHandling } from 'core-app/shared/helpers/link-handling/link-handling'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { HttpClient } from "@angular/common/http"; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit } from "@angular/core"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { HttpClient } from '@angular/common/http'; +import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, +} from '@angular/core'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; export interface IProjectMenuEntry { id:number; @@ -53,15 +55,17 @@ export const projectMenuAutocompleteSelector = 'project-menu-autocomplete'; @Component({ templateUrl: './project-menu-autocomplete.template.html', changeDetection: ChangeDetectionStrategy.OnPush, - selector: projectMenuAutocompleteSelector + selector: projectMenuAutocompleteSelector, }) export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge implements OnInit { public text:any; // The project dropdown menu public dropdownMenu:JQuery; + // The project filter input public input:JQuery; + // No results element public noResults:JQuery; @@ -69,21 +73,21 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge { - const autocompleteValues = _.map(results, project => { - return { label: project.name, render: 'match', object: project } as ProjectAutocompleteItem; - }); + const autocompleteValues = _.map(results, (project) => ({ label: project.name, render: 'match', object: project } as ProjectAutocompleteItem)); this.setup(this.input, autocompleteValues); this.addInputHandlers(); @@ -148,7 +150,7 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge 0) { link .text(`» ${item.label}`) - .css('padding-left', (4 + item.object.level * 16) + 'px'); + .css('padding-left', `${4 + item.object.level * 16}px`); } // Highlight selected project @@ -162,7 +164,7 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge { - return this.results = this.augmentWithParents(result.projects); - }); + .then((result:{ projects:any }) => this.results = this.augmentWithParents(result.projects)); } /** @@ -216,18 +215,18 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge el.object.identifier); - const matchedParents = _.flatten(matched.map(el => el.object.parents)); + const matches = matched.map((el) => el.object.identifier); + const matchedParents = _.flatten(matched.map((el) => el.object.parents)); const results:ProjectAutocompleteItem[] = []; - items.forEach(el => { - const identifier = el.object.identifier; + items.forEach((el) => { + const { identifier } = el.object; let renderType:'disabled'|'match'; if (matches.indexOf(identifier) >= 0) { renderType = 'match'; - } else if (_.find(matchedParents, e => e.identifier === identifier)) { + } else if (_.find(matchedParents, (e) => e.identifier === identifier)) { renderType = 'disabled'; } else { return; @@ -236,7 +235,7 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge { - if (evt.which === keyCodes.ESCAPE) { + if (evt.which === KeyCodes.ESCAPE) { this.input.val(''); (this.input as any)[this.widgetName].call(this.input, 'search', ''); return false; @@ -266,11 +265,11 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge { - if (LinkHandling.isClickedWithModifier(evt)) { + if (isClickedWithModifier(evt)) { evt.stopImmediatePropagation(); } @@ -279,13 +278,14 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge { if (!touchMoved) { window.location.href = (evt.target as HTMLAnchorElement).href; } - }).on('touchmove', '.ui-menu-item a', function () { + }).on('touchmove', '.ui-menu-item a', () => { touchMoved = true; - }).on('touchstart', '.ui-menu-item a', function () { + }) + .on('touchstart', '.ui-menu-item a', () => { touchMoved = false; }); } @@ -296,10 +296,10 @@ export class ProjectMenuAutocompleteComponent extends ILazyAutocompleterBridge(); @@ -56,11 +47,12 @@ export class TimeEntryWorkPackageAutocompleterComponent extends WorkPackageAutoc ) { super(injector); - this.text['all'] = this.I18n.t('js.label_all'); - this.text['recent'] = this.I18n.t('js.label_recent'); + this.text.all = this.I18n.t('js.label_all'); + this.text.recent = this.I18n.t('js.label_recent'); } public loading = false; + public mode:TimeEntryWorkPackageAutocompleterMode = 'all'; public setMode(value:TimeEntryWorkPackageAutocompleterMode) { diff --git a/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component.ts index 8265b8f489..e52b321d4b 100644 --- a/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/user-autocompleter/user-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,23 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, EventEmitter, Injector, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Observable } from "rxjs"; -import { map } from "rxjs/operators"; -import { DebouncedRequestSwitchmap, errorNotificationHandler } from "core-app/shared/helpers/rxjs/debounced-input-switchmap"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { UserResource } from "core-app/features/hal/resources/user-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { ApiV3FilterBuilder, FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { + Component, ElementRef, EventEmitter, Injector, Input, OnInit, Output, ViewChild, +} from '@angular/core'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { + DebouncedRequestSwitchmap, + errorNotificationHandler, +} from 'core-app/shared/helpers/rxjs/debounced-input-switchmap'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { ApiV3FilterBuilder, FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; export const usersAutocompleterSelector = 'user-autocompleter'; @@ -47,22 +52,26 @@ export interface UserAutocompleteItem { href:string|null; } - @Component({ templateUrl: './user-autocompleter.component.html', - selector: usersAutocompleterSelector + selector: usersAutocompleterSelector, }) export class UserAutocompleterComponent implements OnInit { userTracker = (item:any) => item.href || item.id; @ViewChild(NgSelectComponent, { static: true }) public ngSelectComponent:NgSelectComponent; + @Output() public onChange = new EventEmitter(); + @Input() public clearAfterSelection = false; // Load all users as default @Input() public url:string = this.apiV3Service.users.path; + @Input() public allowEmpty = false; + @Input() public appendTo = ''; + @Input() public multiple = false; @Input() public initialSelection:number|null = null; @@ -73,36 +82,36 @@ export class UserAutocompleterComponent implements OnInit { /** Keep a switchmap for search term and loading state */ public requests = new DebouncedRequestSwitchmap( (searchTerm:string) => this.getAvailableUsers(this.url, searchTerm), - errorNotificationHandler(this.halNotification) + errorNotificationHandler(this.halNotification), ); public inputFilters:ApiV3FilterBuilder = new ApiV3FilterBuilder(); constructor(protected elementRef:ElementRef, - protected halResourceService:HalResourceService, - protected I18n:I18nService, - protected halNotification:HalResourceNotificationService, - readonly pathHelper:PathHelperService, - readonly apiV3Service:APIV3Service, - readonly injector:Injector) { + protected halResourceService:HalResourceService, + protected I18n:I18nService, + protected halNotification:HalResourceNotificationService, + readonly pathHelper:PathHelperService, + readonly apiV3Service:APIV3Service, + readonly injector:Injector) { } ngOnInit() { - const input = this.elementRef.nativeElement.dataset['updateInput']; - const allowEmpty = this.elementRef.nativeElement.dataset['allowEmpty']; - const appendTo = this.elementRef.nativeElement.dataset['appendTo']; - const multiple = this.elementRef.nativeElement.dataset['multiple']; - const url = this.elementRef.nativeElement.dataset['url']; + const input = this.elementRef.nativeElement.dataset.updateInput; + const { allowEmpty } = this.elementRef.nativeElement.dataset; + const { appendTo } = this.elementRef.nativeElement.dataset; + const { multiple } = this.elementRef.nativeElement.dataset; + const { url } = this.elementRef.nativeElement.dataset; if (input) { this.updateInputField = document.getElementsByName(input)[0] as HTMLInputElement|undefined; this.setInitialSelection(); } - const filterInput = this.elementRef.nativeElement.dataset['additionalFilter']; + const filterInput = this.elementRef.nativeElement.dataset.additionalFilter; if (filterInput) { - JSON.parse(filterInput).forEach((filter:{selector:string; operator:FilterOperator, values:string[]}) => { - this.inputFilters.add(filter['selector'], filter['operator'], filter['values']); + JSON.parse(filterInput).forEach((filter:{ selector:string; operator:FilterOperator, values:string[] }) => { + this.inputFilters.add(filter.selector, filter.operator, filter.values); }); } @@ -160,17 +169,17 @@ export class UserAutocompleterComponent implements OnInit { return this.halResourceService .get(url, { filters: searchFilters.toJson() }) .pipe( - map(res => { - const options = res.elements.map((el:any) => { - return { name: el.name, id: el.id, href: el.href, avatar: el.avatar }; - }); + map((res) => { + const options = res.elements.map((el:any) => ({ + name: el.name, id: el.id, href: el.href, avatar: el.avatar, + })); if (this.allowEmpty) { options.unshift({ name: this.I18n.t('js.timelines.filter.noneSelection'), href: null, id: null }); } return options; - }) + }), ); } @@ -181,4 +190,3 @@ export class UserAutocompleterComponent implements OnInit { } } } - diff --git a/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts index 6f3b1f9506..72b2b9935a 100644 --- a/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,24 +26,26 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Output, Injector } from '@angular/core'; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { VersionResource } from "core-app/features/hal/resources/version-resource"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { + AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Injector, Output, +} from '@angular/core'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { VersionResource } from 'core-app/features/hal/resources/version-resource'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @Component({ templateUrl: '../create-autocompleter/create-autocompleter.component.html', - selector: 'version-autocompleter' + selector: 'version-autocompleter', }) export class VersionAutocompleterComponent extends CreateAutocompleterComponent implements AfterViewInit { @Output() public onCreate = new EventEmitter(); constructor( - readonly injector: Injector, + readonly injector:Injector, readonly I18n:I18nService, readonly currentProject:CurrentProjectService, readonly cdRef:ChangeDetectorRef, @@ -79,7 +81,7 @@ export class VersionAutocompleterComponent extends CreateAutocompleterComponent .apiV3Service .versions .available_projects - .exists(this.currentProject.id!) + .exists(this.currentProject.id) .toPromise() .catch(() => false); } @@ -90,20 +92,21 @@ export class VersionAutocompleterComponent extends CreateAutocompleterComponent .versions .post(this.getVersionPayload(name)) .subscribe( - version => this.onCreate.emit(version), - error => { + (version) => this.onCreate.emit(version), + (error) => { this.closeSelect(); this.halNotification.handleRawError(error); - }); + }, + ); } private getVersionPayload(name:string) { const payload:any = {}; - payload['name'] = name; - payload['_links'] = { + payload.name = name; + payload._links = { definingProject: { - href: this.apiV3Service.projects.id(this.currentProject.id!).path - } + href: this.apiV3Service.projects.id(this.currentProject.id!).path, + }, }; return payload; diff --git a/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts index 0dde95511e..a016c0de85 100644 --- a/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { - AfterViewInit, - Component, -} from '@angular/core'; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; +import { AfterViewInit, Component } from '@angular/core'; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; @Component({ templateUrl: '../create-autocompleter/create-autocompleter.component.html', - selector: 'wp-autocompleter' + selector: 'wp-autocompleter', }) export class WorkPackageAutocompleterComponent extends CreateAutocompleterComponent implements AfterViewInit { } diff --git a/frontend/src/app/shared/components/calendar/openproject-calendar.module.ts b/frontend/src/app/shared/components/calendar/openproject-calendar.module.ts index 6e9cf7192a..b1e5d097ab 100644 --- a/frontend/src/app/shared/components/calendar/openproject-calendar.module.ts +++ b/frontend/src/app/shared/components/calendar/openproject-calendar.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -29,13 +29,13 @@ import { OPSharedModule } from 'core-app/shared/shared.module'; import { NgModule } from '@angular/core'; import { FullCalendarModule } from '@fullcalendar/angular'; -import { WorkPackagesCalendarEntryComponent } from "core-app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component"; -import { WorkPackagesCalendarController } from "core-app/shared/components/calendar/wp-calendar/wp-calendar.component"; -import { OpenprojectWorkPackagesModule } from "core-app/features/work-packages/openproject-work-packages.module"; -import { Ng2StateDeclaration, UIRouterModule } from "@uirouter/angular"; -import { TimeEntryCalendarComponent } from "core-app/shared/components/calendar/te-calendar/te-calendar.component"; -import { OpenprojectFieldsModule } from "core-app/shared/components/fields/openproject-fields.module"; -import { OpenprojectTimeEntriesModule } from "core-app/shared/components/time_entries/openproject-time-entries.module"; +import { WorkPackagesCalendarEntryComponent } from 'core-app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component'; +import { WorkPackagesCalendarController } from 'core-app/shared/components/calendar/wp-calendar/wp-calendar.component'; +import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; +import { Ng2StateDeclaration, UIRouterModule } from '@uirouter/angular'; +import { TimeEntryCalendarComponent } from 'core-app/shared/components/calendar/te-calendar/te-calendar.component'; +import { OpenprojectFieldsModule } from 'core-app/shared/components/fields/openproject-fields.module'; +import { OpenprojectTimeEntriesModule } from 'core-app/shared/components/time_entries/openproject-time-entries.module'; const menuItemClass = 'calendar-menu-item'; @@ -48,9 +48,9 @@ export const CALENDAR_ROUTES:Ng2StateDeclaration[] = [ data: { bodyClasses: 'router--work-packages-calendar', menuItem: menuItemClass, - parent: 'work-packages' - } - } + parent: 'work-packages', + }, + }, ]; @NgModule({ @@ -82,7 +82,7 @@ export const CALENDAR_ROUTES:Ng2StateDeclaration[] = [ exports: [ WorkPackagesCalendarController, TimeEntryCalendarComponent, - ] + ], }) export class OpenprojectCalendarModule { } diff --git a/frontend/src/app/shared/components/calendar/te-calendar/te-calendar.component.ts b/frontend/src/app/shared/components/calendar/te-calendar/te-calendar.component.ts index fad67f3cee..57d0f2621b 100644 --- a/frontend/src/app/shared/components/calendar/te-calendar/te-calendar.component.ts +++ b/frontend/src/app/shared/components/calendar/te-calendar/te-calendar.component.ts @@ -9,31 +9,33 @@ import { Output, SecurityContext, ViewChild, - ViewEncapsulation -} from "@angular/core"; + ViewEncapsulation, +} from '@angular/core'; import { FullCalendarComponent } from '@fullcalendar/angular'; -import { States } from "core-app/core/states/states.service"; -import * as moment from "moment"; -import { Moment } from "moment"; -import { StateService } from "@uirouter/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { DomSanitizer } from "@angular/platform-browser"; +import { States } from 'core-app/core/states/states.service'; +import * as moment from 'moment'; +import { Moment } from 'moment'; +import { StateService } from '@uirouter/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { DomSanitizer } from '@angular/platform-browser'; import timeGrid from '@fullcalendar/timegrid'; -import { CalendarOptions, Duration, EventApi, EventInput } from '@fullcalendar/core'; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { TimeEntryResource } from "core-app/features/hal/resources/time-entry-resource"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; +import { + CalendarOptions, Duration, EventApi, EventInput, +} from '@fullcalendar/core'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; import interactionPlugin from '@fullcalendar/interaction'; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { TimeEntryEditService } from "core-app/shared/components/time_entries/edit/edit.service"; -import { TimeEntryCreateService } from "core-app/shared/components/time_entries/create/create.service"; -import { ColorsService } from "core-app/shared/components/colors/colors.service"; -import { BrowserDetector } from "core-app/core/browser/browser-detector.service"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { FilterOperator } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { TimeEntryEditService } from 'core-app/shared/components/time_entries/edit/edit.service'; +import { TimeEntryCreateService } from 'core-app/shared/components/time_entries/create/create.service'; +import { ColorsService } from 'core-app/shared/components/colors/colors.service'; +import { BrowserDetector } from 'core-app/core/browser/browser-detector.service'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; interface CalendarViewEvent { el:HTMLElement; @@ -66,12 +68,14 @@ const ADD_ENTRY_PROHIBITED_CLASS_NAME = '-prohibited'; providers: [ TimeEntryEditService, TimeEntryCreateService, - HalResourceEditingService - ] + HalResourceEditingService, + ], }) export class TimeEntryCalendarComponent implements AfterViewInit { @ViewChild(FullCalendarComponent) ucCalendar:FullCalendarComponent; + @Input() projectIdentifier:string; + @Input() static = false; @Input() set displayedDays(days:DisplayedDays) { @@ -82,17 +86,23 @@ export class TimeEntryCalendarComponent implements AfterViewInit { // Not used by the calendar but rather is the maximum/minimum of the graph. public minHour = 1; + public maxHour = 12; + public labelIntervalHours = 2; + public scaleRatio = 1; public calendarEvents:Function; + protected memoizedTimeEntries:{ start:Date, end:Date, entries:Promise> }; + public memoizedCreateAllowed = false; + public hiddenDays:number[] = []; public text = { - logTime: this.i18n.t('js.button_log_time') + logTime: this.i18n.t('js.button_log_time'), }; calendarOptions:CalendarOptions = { @@ -102,7 +112,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { headerToolbar: { right: '', center: 'title', - left: 'prev,next today' + left: 'prev,next today', }, initialView: 'timeGridWeek', firstDay: this.configuration.startOfWeek(), @@ -117,24 +127,24 @@ export class TimeEntryCalendarComponent implements AfterViewInit { slotMaxTime: `${this.maxHour}:00:00`, events: this.calendarEventsFunction.bind(this), eventOverlap: (stillEvent:any) => !stillEvent.classNames.includes(TIME_ENTRY_CLASS_NAME), - plugins: [timeGrid, interactionPlugin] + plugins: [timeGrid, interactionPlugin], }; constructor(readonly states:States, - readonly apiV3Service:APIV3Service, - readonly $state:StateService, - private element:ElementRef, - readonly i18n:I18nService, - readonly injector:Injector, - readonly notifications:HalResourceNotificationService, - private sanitizer:DomSanitizer, - private configuration:ConfigurationService, - private timezone:TimezoneService, - private timeEntryEdit:TimeEntryEditService, - private timeEntryCreate:TimeEntryCreateService, - private schemaCache:SchemaCacheService, - private colors:ColorsService, - private browserDetector:BrowserDetector) { + readonly apiV3Service:APIV3Service, + readonly $state:StateService, + private element:ElementRef, + readonly i18n:I18nService, + readonly injector:Injector, + readonly notifications:HalResourceNotificationService, + private sanitizer:DomSanitizer, + private configuration:ConfigurationService, + private timezone:TimezoneService, + private timeEntryEdit:TimeEntryEditService, + private timeEntryCreate:TimeEntryCreateService, + private schemaCache:SchemaCacheService, + private colors:ColorsService, + private browserDetector:BrowserDetector) { } ngAfterViewInit() { @@ -158,7 +168,6 @@ export class TimeEntryCalendarComponent implements AfterViewInit { public calendarEventsFunction(fetchInfo:{ start:Date, end:Date }, successCallback:(events:EventInput[]) => void, failureCallback:(error:unknown) => void):void|PromiseLike { - this.fetchTimeEntries(fetchInfo.start, fetchInfo.end) .then((collection) => { this.entries.emit(collection); @@ -168,21 +177,21 @@ export class TimeEntryCalendarComponent implements AfterViewInit { } protected fetchTimeEntries(start:Date, end:Date) { - if (!this.memoizedTimeEntries || - this.memoizedTimeEntries.start.getTime() !== start.getTime() || - this.memoizedTimeEntries.end.getTime() !== end.getTime()) { + if (!this.memoizedTimeEntries + || this.memoizedTimeEntries.start.getTime() !== start.getTime() + || this.memoizedTimeEntries.end.getTime() !== end.getTime()) { const promise = this .apiV3Service .time_entries .list({ filters: this.dmFilters(start, end), pageSize: 500 }) .toPromise() - .then(collection => { + .then((collection) => { this.memoizedCreateAllowed = !!collection.createTimeEntry; return collection; }); - this.memoizedTimeEntries = { start: start, end: end, entries: promise }; + this.memoizedTimeEntries = { start, end, entries: promise }; } return this.memoizedTimeEntries.entries; @@ -296,8 +305,8 @@ export class TimeEntryCalendarComponent implements AfterViewInit { end: end.format(), backgroundColor: color, borderColor: color, - classNames: classNames, - entry: entry + classNames, + entry, }; } @@ -308,7 +317,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { classNames: DAY_SUM_CLASS_NAME, rendering: 'background' as const, startEditable: false, - sum: this.i18n.t('js.units.hour', { count: this.formatNumber(duration) }) + sum: this.i18n.t('js.units.hour', { count: this.formatNumber(duration) }), }; } @@ -322,8 +331,8 @@ export class TimeEntryCalendarComponent implements AfterViewInit { return { start: date.clone().format(), end: date.clone().add(this.maxHour - Math.min(duration * this.scaleRatio, this.maxHour - 1) - 0.5, 'h').format(), - rendering: "background" as 'background', - classNames: classNames + rendering: 'background' as const, + classNames, }; } @@ -346,7 +355,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { this .timeEntryEdit .edit(entry) - .then(modificationAction => { + .then((modificationAction) => { this.updateEventSet(modificationAction.entry, modificationAction.action); }) .catch(() => { @@ -355,7 +364,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { } private moveEvent(event:CalendarMoveEvent) { - const entry = event.event.extendedProps.entry; + const { entry } = event.event.extendedProps; // Use end instead of start as when dragging, the event might be too long and would thus be start // on the day before by fullcalendar. @@ -364,18 +373,18 @@ export class TimeEntryCalendarComponent implements AfterViewInit { this .schemaCache .ensureLoaded(entry) - .then(schema => { + .then((schema) => { this .apiV3Service .time_entries .id(entry) .patch(entry, schema) .subscribe( - event => this.updateEventSet(event, 'update'), - e => { + (event) => this.updateEventSet(event, 'update'), + (e) => { this.notifications.handleRawError(e); event.revert(); - } + }, ); }); } @@ -392,7 +401,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { this .timeEntryCreate .create(date) - .then(modificationAction => { + .then((modificationAction) => { this.updateEventSet(modificationAction.entry, modificationAction.action); }) .catch(() => { @@ -401,25 +410,25 @@ export class TimeEntryCalendarComponent implements AfterViewInit { } private updateEventSet(event:TimeEntryResource, action:'update'|'destroy'|'create') { - this.memoizedTimeEntries.entries.then(collection => { - const foundIndex = collection.elements.findIndex(x => x.id === event.id); + this.memoizedTimeEntries.entries.then((collection) => { + const foundIndex = collection.elements.findIndex((x) => x.id === event.id); switch (action) { - case 'update': - collection.elements[foundIndex] = event; - break; - case 'destroy': - collection.elements.splice(foundIndex, 1); - break; - case 'create': - this - .apiV3Service - .time_entries - .cache - .updateFor(event); - - collection.elements.push(event); - break; + case 'update': + collection.elements[foundIndex] = event; + break; + case 'destroy': + collection.elements.splice(foundIndex, 1); + break; + case 'create': + this + .apiV3Service + .time_entries + .cache + .updateFor(event); + + collection.elements.push(event); + break; } this.ucCalendar.getApi().refetchEvents(); @@ -464,10 +473,10 @@ export class TimeEntryCalendarComponent implements AfterViewInit { jQuery(event.el).tooltip({ content: this.tooltipContentString(event.event.extendedProps.entry), items: '.fc-event', - close: function () { - jQuery(".ui-helper-hidden-accessible").remove(); + close() { + jQuery('.ui-helper-hidden-accessible').remove(); }, - track: true + track: true, }); } @@ -506,14 +515,14 @@ export class TimeEntryCalendarComponent implements AfterViewInit { } const $element = jQuery(event.el); - const fadeout = jQuery(`
    `); + const fadeout = jQuery('
    '); const hslaStart = this.colors.toHsla(this.entryName(timeEntry), 0); const hslaEnd = this.colors.toHsla(this.entryName(timeEntry), 100); fadeout.css('background', `-webkit-linear-gradient(${hslaStart} 0%, ${hslaEnd} 100%`); - ['-moz-linear-gradient', '-o-linear-gradient', 'linear-gradient', '-ms-linear-gradient'].forEach((style => { + ['-moz-linear-gradient', '-o-linear-gradient', 'linear-gradient', '-ms-linear-gradient'].forEach(((style) => { fadeout.css('background-image', `${style}(${hslaStart} 0%, ${hslaEnd} 100%`); })); @@ -530,7 +539,7 @@ export class TimeEntryCalendarComponent implements AfterViewInit { } private entryName(entry:TimeEntryResource) { - let name = entry.project.name; + let { name } = entry.project; if (entry.workPackage) { name += ` - ${this.workPackageName(entry)}`; } @@ -593,9 +602,8 @@ export class TimeEntryCalendarComponent implements AfterViewInit { .from(displayedDays, (value, index) => { if (!value) { return (index + 1) % 7; - } else { - return null; } + return null; }) .filter((value) => value !== null) as number[]; diff --git a/frontend/src/app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component.ts b/frontend/src/app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component.ts index 1c333a2869..11f81a4cf7 100644 --- a/frontend/src/app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component.ts +++ b/frontend/src/app/shared/components/calendar/wp-calendar-entry/wp-calendar-entry.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,11 +27,11 @@ //++ import { Component, ViewChild } from '@angular/core'; -import { WorkPackagesViewBase } from "core-app/features/work-packages/routing/wp-view-base/work-packages-view.base"; -import { WorkPackagesCalendarController } from "core-app/shared/components/calendar/wp-calendar/wp-calendar.component"; +import { WorkPackagesViewBase } from 'core-app/features/work-packages/routing/wp-view-base/work-packages-view.base'; +import { WorkPackagesCalendarController } from 'core-app/shared/components/calendar/wp-calendar/wp-calendar.component'; @Component({ - templateUrl: './wp-calendar-entry.component.html' + templateUrl: './wp-calendar-entry.component.html', }) export class WorkPackagesCalendarEntryComponent extends WorkPackagesViewBase { @@ -42,7 +42,6 @@ export class WorkPackagesCalendarEntryComponent extends WorkPackagesViewBase { } public refresh(visibly:boolean, firstPage:boolean):Promise { - return this.loadingIndicator = - this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier!); + return this.loadingIndicator = this.wpListService.loadCurrentQueryFromParams(this.projectIdentifier); } } diff --git a/frontend/src/app/shared/components/calendar/wp-calendar/wp-calendar.component.ts b/frontend/src/app/shared/components/calendar/wp-calendar/wp-calendar.component.ts index 47c928eb60..470359f5b0 100644 --- a/frontend/src/app/shared/components/calendar/wp-calendar/wp-calendar.component.ts +++ b/frontend/src/app/shared/components/calendar/wp-calendar/wp-calendar.component.ts @@ -1,32 +1,27 @@ import { - Component, - ElementRef, - Input, - OnInit, - SecurityContext, - ViewChild -} from "@angular/core"; + Component, ElementRef, Input, OnInit, SecurityContext, ViewChild, +} from '@angular/core'; import { FullCalendarComponent } from '@fullcalendar/angular'; -import { States } from "core-app/core/states/states.service"; -import { IsolatedQuerySpace } from "core-app/features/work-packages/directives/query-space/isolated-query-space"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { WorkPackageCollectionResource } from "core-app/features/hal/resources/wp-collection-resource"; -import { WorkPackageViewFiltersService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service"; -import * as moment from "moment"; -import { WorkPackagesListService } from "core-app/features/work-packages/components/wp-list/wp-list.service"; -import { StateService } from "@uirouter/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { DomSanitizer } from "@angular/platform-browser"; -import { WorkPackagesListChecksumService } from "core-app/features/work-packages/components/wp-list/wp-list-checksum.service"; -import { OpTitleService } from "core-app/core/html/op-title.service"; +import { States } from 'core-app/core/states/states.service'; +import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource'; +import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; +import * as moment from 'moment'; +import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; +import { StateService } from '@uirouter/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { DomSanitizer } from '@angular/platform-browser'; +import { WorkPackagesListChecksumService } from 'core-app/features/work-packages/components/wp-list/wp-list-checksum.service'; +import { OpTitleService } from 'core-app/core/html/op-title.service'; import dayGridPlugin from '@fullcalendar/daygrid'; import { CalendarOptions, EventApi, EventInput } from '@fullcalendar/core'; -import { Subject } from "rxjs"; -import { take, debounceTime } from 'rxjs/operators'; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; +import { Subject } from 'rxjs'; +import { debounceTime, take } from 'rxjs/operators'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; interface CalendarViewEvent { el:HTMLElement; @@ -40,8 +35,11 @@ interface CalendarViewEvent { }) export class WorkPackagesCalendarController extends UntilDestroyedMixin implements OnInit { private resizeObserver:ResizeObserver; + private resizeSubject = new Subject(); + private ucCalendar:FullCalendarComponent; + @ViewChild(FullCalendarComponent) set container(v:FullCalendarComponent|undefined) { // ViewChild reference may be undefined initially @@ -62,6 +60,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen this.toWPFullView(event); }); } + @ViewChild('ucCalendar', { read: ElementRef }) set ucCalendarElement(v:ElementRef|undefined) { if (!v) { @@ -76,7 +75,9 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen } @Input() projectIdentifier:string; + @Input() static = false; + static MAX_DISPLAYED = 100; public tooManyResultsText:string|null; @@ -86,18 +87,18 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen calendarOptions:CalendarOptions|undefined; constructor(readonly states:States, - readonly $state:StateService, - readonly wpTableFilters:WorkPackageViewFiltersService, - readonly wpListService:WorkPackagesListService, - readonly querySpace:IsolatedQuerySpace, - readonly wpListChecksumService:WorkPackagesListChecksumService, - readonly schemaCache:SchemaCacheService, - readonly titleService:OpTitleService, - private element:ElementRef, - readonly i18n:I18nService, - readonly notificationsService:NotificationsService, - private sanitizer:DomSanitizer, - private configuration:ConfigurationService) { + readonly $state:StateService, + readonly wpTableFilters:WorkPackageViewFiltersService, + readonly wpListService:WorkPackagesListService, + readonly querySpace:IsolatedQuerySpace, + readonly wpListChecksumService:WorkPackagesListChecksumService, + readonly schemaCache:SchemaCacheService, + readonly titleService:OpTitleService, + private element:ElementRef, + readonly i18n:I18nService, + readonly notificationsService:NotificationsService, + private sanitizer:DomSanitizer, + private configuration:ConfigurationService) { super(); } @@ -105,7 +106,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen this.resizeSubject .pipe( this.untilDestroyed(), - debounceTime(50) + debounceTime(50), ) .subscribe(() => { this.ucCalendar.getApi().updateSize(); @@ -119,22 +120,21 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen } public calendarEventsFunction(fetchInfo:{ start:Date, end:Date, timeZone:string }, - successCallback:(events:EventInput[]) => void, - failureCallback:(error:any) => void):void|PromiseLike { + successCallback:(events:EventInput[]) => void, + failureCallback:(error:any) => void):void|PromiseLike { if (this.alreadyLoaded) { this.alreadyLoaded = false; const events = this.updateResults(this.querySpace.results.value!); successCallback(events); } else { this.querySpace.results.values$().pipe( - take(1) + take(1), ).subscribe((collection:WorkPackageCollectionResource) => { const events = this.updateResults((collection)); successCallback(events); }); } - this.updateTimeframe(fetchInfo); } @@ -151,12 +151,11 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen initialView: (() => { if (this.static) { return 'dayGridWeek'; - } else { - return undefined; } + return undefined; })(), height: this.calendarHeight(), - headerToolbar: this.buildHeader() + headerToolbar: this.buildHeader(), }; }); } @@ -181,7 +180,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen this.wpListService.fromQueryParams({ query_props: queryProps }, this.projectIdentifier).toPromise(); } else { - const params = this.$state.params; + const { params } = this.$state; this.wpTableFilters.modify('datesInterval', (datesIntervalFilter) => { datesIntervalFilter.values[0] = startDate; @@ -194,15 +193,15 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen jQuery(event.el).tooltip({ content: this.tooltipContentString(event.event.extendedProps.workPackage), items: '.fc-event', - close: function () { - jQuery(".ui-helper-hidden-accessible").remove(); + close() { + jQuery('.ui-helper-hidden-accessible').remove(); }, - track: true + track: true, }); } public toWPFullView(event:CalendarViewEvent) { - const workPackage = event.event.extendedProps.workPackage; + const { workPackage } = event.event.extendedProps; if (event.el) { // do not display the tooltip on the wp show page @@ -218,8 +217,10 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen this.$state.go( 'work-packages.show', { workPackageId: workPackage.id }, - { inherit: false }); + { inherit: false }, + ); } + private get calendarElement() { return jQuery(this.element.nativeElement).find('.wp-calendar--container'); } @@ -236,22 +237,20 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen const topOfHeightElement = heightElement.position().top; return heightElement.height()! - (topOfCalendar - topOfHeightElement); - } else { - // -12 for the bottom padding - return jQuery(window).height()! - this.calendarElement.offset()!.top - 12; } + // -12 for the bottom padding + return jQuery(window).height()! - this.calendarElement.offset()!.top - 12; } public buildHeader() { if (this.static) { return false; - } else { - return { - right: 'dayGridMonth,dayGridWeek', - center: 'title', - left: 'prev,next today' - }; } + return { + right: 'dayGridMonth,dayGridWeek', + center: 'title', + left: 'prev,next today', + }; } private setCalendarsDate() { @@ -260,7 +259,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen return; } - const datesIntervalFilter = _.find(query.filters || [], { 'id': 'datesInterval' }) as any; + const datesIntervalFilter = _.find(query.filters || [], { id: 'datesInterval' }) as any; let calendarDate:any = null; let calendarUnit = 'dayGridMonth'; @@ -286,7 +285,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen private setupWorkPackagesListener() { this.querySpace.results.values$().pipe( - this.untilDestroyed() + this.untilDestroyed(), ).subscribe((collection:WorkPackageCollectionResource) => { this.alreadyLoaded = true; this.setCalendarsDate(); @@ -314,7 +313,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen end: exclusiveEnd, allDay: true, className: `__hl_background_type_${workPackage.type.id}`, - workPackage: workPackage + workPackage, }; }); @@ -326,7 +325,7 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen this.tooManyResultsText = this.i18n.t('js.calendar.too_many', { count: collection.total, - max: WorkPackagesCalendarController.MAX_DISPLAYED + max: WorkPackagesCalendarController.MAX_DISPLAYED, }); } else { this.tooManyResultsText = null; @@ -339,12 +338,12 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen private defaultQueryProps(startDate:string, endDate:string) { const props = { - "c": ["id"], - "t": - "id:asc", - "f": [{ "n": "status", "o": "o", "v": [] }, - { "n": "datesInterval", "o": "<>d", "v": [startDate, endDate] }], - "pp": WorkPackagesCalendarController.MAX_DISPLAYED + c: ['id'], + t: + 'id:asc', + f: [{ n: 'status', o: 'o', v: [] }, + { n: 'datesInterval', o: '<>d', v: [startDate, endDate] }], + pp: WorkPackagesCalendarController.MAX_DISPLAYED, }; return JSON.stringify(props); @@ -353,9 +352,8 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen private eventDate(workPackage:WorkPackageResource, type:'start'|'due') { if (this.schemaCache.of(workPackage).isMilestone) { return workPackage.date; - } else { - return workPackage[`${type}Date`]; } + return workPackage[`${type}Date`]; } private tooltipContentString(workPackage:WorkPackageResource) { @@ -401,10 +399,10 @@ export class WorkPackagesCalendarController extends UntilDestroyedMixin implemen private removeTooltip(element:HTMLElement) { // deactivate tooltip so that it is not displayed on the wp show page jQuery(element).tooltip({ - close: function () { - jQuery(".ui-helper-hidden-accessible").remove(); + close() { + jQuery('.ui-helper-hidden-accessible').remove(); }, - disabled: true + disabled: true, }); } } diff --git a/frontend/src/app/shared/components/collapsible-section/collapsible-section.component.ts b/frontend/src/app/shared/components/collapsible-section/collapsible-section.component.ts index 509a8a5858..f11e38d28c 100644 --- a/frontend/src/app/shared/components/collapsible-section/collapsible-section.component.ts +++ b/frontend/src/app/shared/components/collapsible-section/collapsible-section.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Component, ElementRef, OnInit, ViewChild } from "@angular/core"; +import { + Component, ElementRef, OnInit, ViewChild, +} from '@angular/core'; export const collapsibleSectionAugmentSelector = 'collapsible-section-augment'; @Component({ selector: collapsibleSectionAugmentSelector, - templateUrl: './collapsible-section.html' + templateUrl: './collapsible-section.html', }) export class CollapsibleSectionComponent implements OnInit { public expanded = false; + public sectionTitle:string; @ViewChild('sectionBody', { static: true }) public sectionBody:ElementRef; diff --git a/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts b/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts index b6f27e7632..dcdafc3b88 100644 --- a/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts +++ b/frontend/src/app/shared/components/colors/colors-autocompleter.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,8 @@ //++ import { Component, ElementRef, OnInit } from '@angular/core'; -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; export const colorsAutocompleterSelector = 'colors-autocompleter'; @@ -50,24 +50,28 @@ export const colorsAutocompleterSelector = 'colors-autocompleter'; `, - selector: colorsAutocompleterSelector + selector: colorsAutocompleterSelector, }) -export class ColorsAutocompleter implements OnInit { +export class ColorsAutocompleterComponent implements OnInit { public options:any[]; + public selectedOption:any; + private highlightTextInline = false; + private updateInputField:HTMLInputElement|undefined; + private selectedColorId:string; constructor(protected elementRef:ElementRef, - protected readonly I18n:I18nService) { + protected readonly I18n:I18nService) { } ngOnInit() { this.setColorOptions(); this.updateInputField = document.getElementsByName(this.elementRef.nativeElement.dataset.updateInput)[0] as HTMLInputElement|undefined; - this.highlightTextInline = JSON.parse(this.elementRef.nativeElement.dataset.highlightTextInline); + this.highlightTextInline = JSON.parse(this.elementRef.nativeElement.dataset.highlightTextInline); } public onModelChange(color:any) { @@ -104,7 +108,4 @@ export class ColorsAutocompleter implements OnInit { } return highlightingClass + Highlighting.colorClass(this.highlightTextInline, item.value); } - } - - diff --git a/frontend/src/app/shared/components/colors/colors.service.ts b/frontend/src/app/shared/components/colors/colors.service.ts index f739714ce3..5071bdc5ab 100644 --- a/frontend/src/app/shared/components/colors/colors.service.ts +++ b/frontend/src/app/shared/components/colors/colors.service.ts @@ -18,4 +18,4 @@ export class ColorsService { return hash % 360; } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.directive.ts b/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.directive.ts index d05c13935a..3a296bad4e 100644 --- a/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.directive.ts +++ b/frontend/src/app/shared/components/copy-to-clipboard/copy-to-clipboard.directive.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,27 +26,28 @@ // See docs/COPYRIGHT.rdoc for more details. //++ - -import { Component, ElementRef, OnInit } from "@angular/core"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { Component, ElementRef, OnInit } from '@angular/core'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; export const copyToClipboardSelector = 'copy-to-clipboard'; @Component({ template: '', - selector: copyToClipboardSelector + selector: copyToClipboardSelector, }) export class CopyToClipboardDirective implements OnInit { public clickTarget:string; + public clipboardTarget:string; + private target:JQuery; constructor(readonly NotificationsService:NotificationsService, - readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly ConfigurationService:ConfigurationService) { + readonly elementRef:ElementRef, + readonly I18n:I18nService, + readonly ConfigurationService:ConfigurationService) { } ngOnInit() { @@ -69,7 +70,7 @@ export class CopyToClipboardDirective implements OnInit { } onClick($event:JQuery.TriggeredEvent) { - var supported = (document.queryCommandSupported && document.queryCommandSupported('copy')); + const supported = (document.queryCommandSupported && document.queryCommandSupported('copy')); $event.preventDefault(); // At least select the input for the user @@ -85,7 +86,7 @@ export class CopyToClipboardDirective implements OnInit { } } catch (e) { console.log( - 'Your browser seems to support the clipboard API, but copying failed: ' + e + `Your browser seems to support the clipboard API, but copying failed: ${e}`, ); } } @@ -93,5 +94,3 @@ export class CopyToClipboardDirective implements OnInit { this.addNotification('addError', this.I18n.t('js.clipboard.browser_error')); } } - - diff --git a/frontend/src/app/shared/components/date/op-date-time.component.ts b/frontend/src/app/shared/components/date/op-date-time.component.ts index 32715a4b16..998c4e708d 100644 --- a/frontend/src/app/shared/components/date/op-date-time.component.ts +++ b/frontend/src/app/shared/components/date/op-date-time.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,7 +27,7 @@ //++ import { Component, Input } from '@angular/core'; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ selector: 'op-date-time', @@ -37,20 +37,20 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service";   - ` + `, }) export class OpDateTimeComponent { - @Input('dateTimeValue') dateTimeValue:any; public date:any; + public time:any; constructor(readonly timezoneService:TimezoneService) { } ngOnInit() { - var c = this.timezoneService.formattedDatetimeComponents(this.dateTimeValue); + const c = this.timezoneService.formattedDatetimeComponents(this.dateTimeValue); this.date = c[0]; this.time = c[1]; } diff --git a/frontend/src/app/shared/components/datepicker/datepicker.modal.helper.ts b/frontend/src/app/shared/components/datepicker/datepicker.modal.helper.ts index 81b33a2f6e..1bc26b0e46 100644 --- a/frontend/src/app/shared/components/datepicker/datepicker.modal.helper.ts +++ b/frontend/src/app/shared/components/datepicker/datepicker.modal.helper.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,9 +27,9 @@ //++ import { Injectable } from '@angular/core'; -import { DateKeys } from "core-app/shared/components/datepicker/datepicker.modal"; -import { DatePicker } from "core-app/shared/components/op-date-picker/datepicker"; -import { DateOption } from "flatpickr/dist/types/options"; +import { DateKeys } from 'core-app/shared/components/datepicker/datepicker.modal'; +import { DatePicker } from 'core-app/shared/components/op-date-picker/datepicker'; +import { DateOption } from 'flatpickr/dist/types/options'; @Injectable({ providedIn: 'root' }) export class DatePickerModalHelper { @@ -46,35 +46,31 @@ export class DatePickerModalHelper { public parseDate(date:Date|string):Date|'' { if (date instanceof Date) { - return new Date(date.setHours(0,0,0,0)); - } else if (date === '') { + return new Date(date.setHours(0, 0, 0, 0)); + } if (date === '') { return ''; - } else { - return new Date(new Date(date).setHours(0,0,0,0)); } + return new Date(new Date(date).setHours(0, 0, 0, 0)); } public validDate(date:Date|string) { - return (date instanceof Date) || - (date === '') || - !!new Date(date).valueOf(); + return (date instanceof Date) + || (date === '') + || !!new Date(date).valueOf(); } public sortDates(dates:Date[]):Date[] { - return dates.sort(function(a:Date, b:Date) { - return a.getTime() - b.getTime(); - }); + return dates.sort((a:Date, b:Date) => a.getTime() - b.getTime()); } public areDatesEqual(firstDate:Date|string, secondDate:Date|string) { const parsedDate1 = this.parseDate(firstDate); const parsedDate2 = this.parseDate(secondDate); - if ((typeof(parsedDate1) === 'string') || (typeof(parsedDate2) === 'string')) { + if ((typeof (parsedDate1) === 'string') || (typeof (parsedDate2) === 'string')) { return false; - } else { - return parsedDate1.getTime() === parsedDate2.getTime(); } + return parsedDate1.getTime() === parsedDate2.getTime(); } public setCurrentActivatedField(val:DateKeys) { @@ -91,8 +87,8 @@ export class DatePickerModalHelper { } public setDates(dates:DateOption|DateOption[], datePicker:DatePicker, enforceDate?:Date) { - const currentMonth = datePicker.datepickerInstance.currentMonth; - const currentYear = datePicker.datepickerInstance.currentYear; + const { currentMonth } = datePicker.datepickerInstance; + const { currentYear } = datePicker.datepickerInstance; datePicker.setDates(dates); if (enforceDate) { @@ -112,18 +108,12 @@ export class DatePickerModalHelper { return; } - let disableFunction:Function = (date:Date) => { - return false; - }; + let disableFunction:Function = (date:Date) => false; if (this.isStateOfCurrentActivatedField('start') && dates.end) { - disableFunction = (date:Date) => { - return date.getTime() > new Date(dates.end).setHours(0,0,0,0); - }; + disableFunction = (date:Date) => date.getTime() > new Date(dates.end).setHours(0, 0, 0, 0); } else if (this.isStateOfCurrentActivatedField('end') && dates.start) { - disableFunction = (date:Date) => { - return date.getTime() < new Date(dates.start).setHours(0,0,0,0); - }; + disableFunction = (date:Date) => date.getTime() < new Date(dates.start).setHours(0, 0, 0, 0); } datePicker.datepickerInstance.set('disable', [disableFunction]); @@ -134,7 +124,7 @@ export class DatePickerModalHelper { return; } - var monthContainer = document.getElementsByClassName('dayContainer'); + const monthContainer = document.getElementsByClassName('dayContainer'); // For each container of the two-month layout, set the highlighting classes for (let i = 0; i < monthContainer.length; i++) { this.highlightRangeInSingleMonth(monthContainer[i], dates); @@ -142,7 +132,7 @@ export class DatePickerModalHelper { } private highlightRangeInSingleMonth(container:Element, dates:{ [key in DateKeys]:string }) { - var selectedElements = jQuery(container).find('.flatpickr-day.selected'); + const selectedElements = jQuery(container).find('.flatpickr-day.selected'); if (selectedElements.length === 2) { // Both dates are in the same month selectedElements[0].classList.add('startRange'); @@ -173,11 +163,11 @@ export class DatePickerModalHelper { } private datepickerIsInDateRange(container:Element, dates:{ [key in DateKeys]:string }):boolean { - var firstDayOfMonthElement = jQuery(container).find('.flatpickr-day:not(.hidden)')[0]; - var firstDayOfMonth = new Date(firstDayOfMonthElement.getAttribute('aria-label')!); + const firstDayOfMonthElement = jQuery(container).find('.flatpickr-day:not(.hidden)')[0]; + const firstDayOfMonth = new Date(firstDayOfMonthElement.getAttribute('aria-label')!); - return firstDayOfMonth <= new Date(dates.end) && - firstDayOfMonth >= new Date(dates.start); + return firstDayOfMonth <= new Date(dates.end) + && firstDayOfMonth >= new Date(dates.start); } private selectRangeFromUntil(from:Element, until:string|Element) { diff --git a/frontend/src/app/shared/components/datepicker/datepicker.modal.ts b/frontend/src/app/shared/components/datepicker/datepicker.modal.ts index 22b9577c60..fb5d020505 100644 --- a/frontend/src/app/shared/components/datepicker/datepicker.modal.ts +++ b/frontend/src/app/shared/components/datepicker/datepicker.modal.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,21 +34,22 @@ import { ElementRef, EventEmitter, Inject, - Injector, ViewChild, - ViewEncapsulation -} from "@angular/core"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { OpModalLocalsMap } from "core-app/shared/components/modal/modal.types"; -import { OpModalLocalsToken } from "core-app/shared/components/modal/modal.service"; -import { DatePicker } from "core-app/shared/components/op-date-picker/datepicker"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { DatePickerModalHelper } from "core-app/shared/components/datepicker/datepicker.modal.helper"; -import { BrowserDetector } from "core-app/core/browser/browser-detector.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; + Injector, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; +import { DatePicker } from 'core-app/shared/components/op-date-picker/datepicker'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { DatePickerModalHelper } from 'core-app/shared/components/datepicker/datepicker.modal.helper'; +import { BrowserDetector } from 'core-app/core/browser/browser-detector.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; export type DateKeys = 'date'|'start'|'end'; @@ -56,13 +57,17 @@ export type DateKeys = 'date'|'start'|'end'; templateUrl: './datepicker.modal.html', styleUrls: ['./datepicker.modal.sass', './datepicker_mobile.modal.sass'], changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, }) -export class DatePickerModal extends OpModalComponent implements AfterViewInit { +export class DatePickerModalComponent extends OpModalComponent implements AfterViewInit { @InjectField() I18n!:I18nService; + @InjectField() timezoneService:TimezoneService; + @InjectField() halEditing:HalResourceEditingService; + @InjectField() datepickerHelper:DatePickerModalHelper; + @InjectField() browserDetector:BrowserDetector; @ViewChild('modalContainer') modalContainer:ElementRef; @@ -78,8 +83,9 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { placeholder: this.I18n.t('js.placeholders.default'), today: this.I18n.t('js.label_today'), isParent: this.I18n.t('js.work_packages.scheduling.is_parent'), - isSwitchedFromManualToAutomatic: this.I18n.t('js.work_packages.scheduling.is_switched_from_manual_to_automatic') + isSwitchedFromManualToAutomatic: this.I18n.t('js.work_packages.scheduling.is_switched_from_manual_to_automatic'), }; + public onDataUpdated = new EventEmitter(); public singleDate = false; @@ -91,7 +97,7 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { public dates:{ [key in DateKeys]:string } = { date: '', start: '', - end: '' + end: '', }; private changeset:ResourceChangeset; @@ -99,10 +105,10 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { private datePickerInstance:DatePicker; constructor(readonly injector:Injector, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly cdRef:ChangeDetectorRef, - readonly elementRef:ElementRef, - readonly configurationService:ConfigurationService) { + @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, + readonly cdRef:ChangeDetectorRef, + readonly elementRef:ElementRef, + readonly configurationService:ConfigurationService) { super(locals, cdRef, elementRef); this.changeset = locals.changeset; this.htmlId = `wp-datepicker-${locals.fieldName}`; @@ -192,7 +198,7 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { my: 'left top', at: 'left bottom', of: target, - collision: 'flipfit' + collision: 'flipfit', }); } @@ -209,9 +215,8 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { if (key === 'start') { return this.datepickerHelper.parseDate(new Date()) <= this.datepickerHelper.parseDate(this.dates.end); - } else { - return this.datepickerHelper.parseDate(new Date()) >= this.datepickerHelper.parseDate(this.dates.start); } + return this.datepickerHelper.parseDate(new Date()) >= this.datepickerHelper.parseDate(this.dates.start); } /** @@ -273,7 +278,7 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { }, }, undefined, - this.configurationService + this.configurationService, ); } @@ -291,75 +296,74 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { private handleDatePickerChange(dates:Date[]) { switch (dates.length) { - case 0: { + case 0: { // In case we removed the only value by clicking on a already selected date within the datepicker: - if (this.dates.start || this.dates.end) { - this.setDateAndToggleActiveField(this.dates.start || this.dates.end); - } + if (this.dates.start || this.dates.end) { + this.setDateAndToggleActiveField(this.dates.start || this.dates.end); + } - break; - } - case 1: { - if (this.singleDate) { - this.dates.date = this.timezoneService.formattedISODate(dates[0]); - } else { + break; + } + case 1: { + if (this.singleDate) { + this.dates.date = this.timezoneService.formattedISODate(dates[0]); + } else { // In case we removed a value by clicking on a already selected date within the datepicker: - if (this.dates.start && this.dates.end) { + if (this.dates.start && this.dates.end) { // Both dates are the same, so it is correct to only highlight one date - if (this.dates.start === this.dates.end) { - return; - } - - // I wanted to set the new start date to the preselected endDate OR - // I wanted to set the new end date to the preselected startDate - if ((this.datepickerHelper.isStateOfCurrentActivatedField('start') && this.datepickerHelper.areDatesEqual(this.dates.start, dates[0])) || - (this.datepickerHelper.isStateOfCurrentActivatedField('end') && this.datepickerHelper.areDatesEqual(this.dates.end, dates[0]))) { - - const otherDateIndex:DateKeys = this.datepickerHelper.isStateOfCurrentActivatedField('start') ? 'end' : 'start'; - this.setDateAndToggleActiveField(this.dates[otherDateIndex]); - } else { + if (this.dates.start === this.dates.end) { + return; + } + + // I wanted to set the new start date to the preselected endDate OR + // I wanted to set the new end date to the preselected startDate + if ((this.datepickerHelper.isStateOfCurrentActivatedField('start') && this.datepickerHelper.areDatesEqual(this.dates.start, dates[0])) + || (this.datepickerHelper.isStateOfCurrentActivatedField('end') && this.datepickerHelper.areDatesEqual(this.dates.end, dates[0]))) { + const otherDateIndex:DateKeys = this.datepickerHelper.isStateOfCurrentActivatedField('start') ? 'end' : 'start'; + this.setDateAndToggleActiveField(this.dates[otherDateIndex]); + } else { // I clicked on the already set start or end date (and thus removed it): // We restore both values - this.enforceManualChangesToDatepicker(true); - } - } else { + this.enforceManualChangesToDatepicker(true); + } + } else { // It is the first value we set (either start or end date) - this.setDateAndToggleActiveField(this.timezoneService.formattedISODate(dates[0]), false); + this.setDateAndToggleActiveField(this.timezoneService.formattedISODate(dates[0]), false); + } } - } - break; - } - case 2: { - if ((!this.dates.end && this.datepickerHelper.isStateOfCurrentActivatedField('start')) || - (!this.dates.start && this.datepickerHelper.isStateOfCurrentActivatedField('end'))) { + break; + } + case 2: { + if ((!this.dates.end && this.datepickerHelper.isStateOfCurrentActivatedField('start')) + || (!this.dates.start && this.datepickerHelper.isStateOfCurrentActivatedField('end'))) { // If we change a start date when no end date is set, we keep only the newly clicked value and not both - this.overwriteDatePickerWithNewDates([dates[1]]); - } else { + this.overwriteDatePickerWithNewDates([dates[1]]); + } else { // Sort dates so that the start date is always first - if (dates[0] > dates[1]) { - dates = this.datepickerHelper.sortDates(dates); - this.datepickerHelper.setDates(dates, this.datePickerInstance); - } + if (dates[0] > dates[1]) { + dates = this.datepickerHelper.sortDates(dates); + this.datepickerHelper.setDates(dates, this.datePickerInstance); + } - const index = this.datepickerHelper.isStateOfCurrentActivatedField('start') ? 0 : 1; - this.dates[this.datepickerHelper.currentlyActivatedDateField] = this.timezoneService.formattedISODate(dates[index]); + const index = this.datepickerHelper.isStateOfCurrentActivatedField('start') ? 0 : 1; + this.dates[this.datepickerHelper.currentlyActivatedDateField] = this.timezoneService.formattedISODate(dates[index]); - this.setRangeClassesAndToggleActiveField(); - } + this.setRangeClassesAndToggleActiveField(); + } - break; - } - default: { - // Reset the date picker with the two new values - if (this.datepickerHelper.isStateOfCurrentActivatedField('start')) { - this.overwriteDatePickerWithNewDates([dates[2], dates[1]]); - } else { - this.overwriteDatePickerWithNewDates([dates[0], dates[2]]); + break; } + default: { + // Reset the date picker with the two new values + if (this.datepickerHelper.isStateOfCurrentActivatedField('start')) { + this.overwriteDatePickerWithNewDates([dates[2], dates[1]]); + } else { + this.overwriteDatePickerWithNewDates([dates[0], dates[2]]); + } - break; - } + break; + } } this.cdRef.detectChanges(); @@ -390,12 +394,11 @@ export class DatePickerModal extends OpModalComponent implements AfterViewInit { const start = this.dates.start || ''; const end = this.dates.end || ''; - const output = this.singleDate ? date : start + ' - ' + end; + const output = this.singleDate ? date : `${start} - ${end}`; this.onDataUpdated.emit(output); } private initialActivatedField():DateKeys { return this.locals.fieldName === 'dueDate' || (this.dates.start && !this.dates.end) ? 'end' : 'start'; } - } diff --git a/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.spec.ts b/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.spec.ts index b550960356..63ca11d1fd 100644 --- a/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.spec.ts @@ -1,17 +1,12 @@ -import { - ComponentFixture, - fakeAsync, - TestBed, - tick, -} from '@angular/core/testing'; -import { DynamicBootstrapComponent } from './dynamic-bootstrap.component'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ApplicationRef, Component, DebugElement } from '@angular/core'; import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; +import { DynamicBootstrapComponent } from './dynamic-bootstrap.component'; // Stub component to bootstrap dynamically @Component({ selector: 'op-test', - template: `
    `, + template: '
    ', }) export class TestComponent {} @@ -32,7 +27,7 @@ describe('DynamicBootstrapComponent', () => { declarations: [ DynamicBootstrapComponent, TestComponent, - ] + ], }) .compileComponents(); }); diff --git a/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.ts b/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.ts index 9f7a41eb02..93df98f0fc 100644 --- a/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.ts +++ b/frontend/src/app/shared/components/dynamic-bootstrap/component/dynamic-bootstrap/dynamic-bootstrap.component.ts @@ -9,7 +9,9 @@ * @module * @public */ -import { ApplicationRef, Component, ElementRef, Input } from '@angular/core'; +import { + ApplicationRef, Component, ElementRef, Input, +} from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; @@ -28,6 +30,7 @@ export class DynamicBootstrapComponent { } innerHtml:SafeHtml; + dynamicBootstrapper = DynamicBootstrapper; constructor( diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component.ts index 50ec54a6e4..1bcf339256 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component.ts @@ -1,10 +1,10 @@ -import { Component } from "@angular/core"; -import { FieldWrapper } from "@ngx-formly/core"; +import { Component } from '@angular/core'; +import { FieldWrapper } from '@ngx-formly/core'; @Component({ - selector: "op-dynamic-field-group-wrapper", - templateUrl: "./dynamic-field-group-wrapper.component.html", - styleUrls: ["./dynamic-field-group-wrapper.component.scss"] + selector: 'op-dynamic-field-group-wrapper', + templateUrl: './dynamic-field-group-wrapper.component.html', + styleUrls: ['./dynamic-field-group-wrapper.component.scss'], }) export class DynamicFieldGroupWrapperComponent extends FieldWrapper { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component.ts index b3b8b367c9..44c7f2fbac 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component.ts @@ -1,12 +1,12 @@ -import { Component, ChangeDetectionStrategy, Optional } from '@angular/core'; -import { FieldWrapper } from "@ngx-formly/core"; -import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"; +import { ChangeDetectionStrategy, Component, Optional } from '@angular/core'; +import { FieldWrapper } from '@ngx-formly/core'; +import { DynamicFormComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component'; @Component({ selector: 'op-dynamic-field-wrapper', templateUrl: './dynamic-field-wrapper.component.html', styleUrls: ['./dynamic-field-wrapper.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DynamicFieldWrapperComponent extends FieldWrapper { constructor( diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.spec.ts index bc7704dccf..029be39680 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.spec.ts @@ -1,34 +1,34 @@ -import { NgSelectModule } from "@ng-select/ng-select"; -import { NgOptionHighlightModule } from "@ng-select/ng-option-highlight"; -import { Component, forwardRef, ViewChild } from "@angular/core"; +import { NgSelectModule } from '@ng-select/ng-select'; +import { NgOptionHighlightModule } from '@ng-select/ng-option-highlight'; +import { Component, forwardRef, ViewChild } from '@angular/core'; import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing'; -import { By } from "@angular/platform-browser"; -import { defer, of } from "rxjs"; -import { FormControl, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from "@angular/forms"; -import { CommonModule } from "@angular/common"; -import { FormlyModule } from "@ngx-formly/core"; - -import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"; -import { DynamicFormService } from "core-app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service"; -import { DynamicFieldsService } from "core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { TextInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component"; -import { IntegerInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component"; -import { SelectInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component"; -import { BooleanInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component"; -import { DateInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component"; -import { FormattableTextareaInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component"; -import { DynamicFieldGroupWrapperComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component"; -import { OpFormFieldComponent } from "core-app/shared/components/forms/form-field/form-field.component"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { DynamicFieldWrapperComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component"; +import { By } from '@angular/platform-browser'; +import { defer, of } from 'rxjs'; +import { FormControl, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { FormlyModule } from '@ngx-formly/core'; + +import { DynamicFormComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component'; +import { DynamicFormService } from 'core-app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service'; +import { DynamicFieldsService } from 'core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { TextInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component'; +import { IntegerInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component'; +import { SelectInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component'; +import { BooleanInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component'; +import { DateInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component'; +import { FormattableTextareaInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component'; +import { DynamicFieldGroupWrapperComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component'; +import { OpFormFieldComponent } from 'core-app/shared/components/forms/form-field/form-field.component'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { DynamicFieldWrapperComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component'; @Component({ template: ` `, - providers: [] + providers: [], }) class DynamicFormsTestingComponent { control = new FormControl(''); @@ -40,231 +40,231 @@ describe('DynamicFormComponent', () => { let component:DynamicFormComponent; let fixture:ComponentFixture; const formSchema:any = { - "_type": "Form", - "_embedded": { - "payload": { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project" - } - } + _type: 'Form', + _embedded: { + payload: { + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', + }, + }, }, - "schema": { - "_type": "Schema", - "_dependencies": [], - "name": { - "type": "String", - "name": "Name", - "required": true, - "hasDefault": false, - "writable": true, - "minLength": 1, - "maxLength": 255, - "options": {} + schema: { + _type: 'Schema', + _dependencies: [], + name: { + type: 'String', + name: 'Name', + required: true, + hasDefault: false, + writable: true, + minLength: 1, + maxLength: 255, + options: {}, }, - "parent": { - "type": "Project", - "name": "Subproject of", - "required": false, - "hasDefault": false, - "writable": true, - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + parent: { + type: 'Project', + name: 'Subproject of', + required: false, + hasDefault: false, + writable: true, + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, - "_links": {} + _links: {}, }, - "validationErrors": {} + validationErrors: {}, }, - "_links": { - "self": { - "href": "/api/v3/projects/25/form", - "method": "post" + _links: { + self: { + href: '/api/v3/projects/25/form', + method: 'post', }, - "validate": { - "href": "/api/v3/projects/25/form", - "method": "post" + validate: { + href: '/api/v3/projects/25/form', + method: 'post', }, - "commit": { - "href": "/api/v3/projects/25", - "method": "patch" - } - } + commit: { + href: '/api/v3/projects/25', + method: 'patch', + }, + }, }; const dynamicFormSettings:any = { fields: [ { - "type": "textInput", - "key": "name", - "templateOptions": { - "required": true, - "label": "Name", - "type": "text", - "placeholder": "", - "disabled": false + type: 'textInput', + key: 'name', + templateOptions: { + required: true, + label: 'Name', + type: 'text', + placeholder: '', + disabled: false, }, }, { - "type": "integerInput", - "key": "quantity", - "templateOptions": { - "required": true, - "label": "Quantity", - "type": "number", - "placeholder": "", - "disabled": false + type: 'integerInput', + key: 'quantity', + templateOptions: { + required: true, + label: 'Quantity', + type: 'number', + placeholder: '', + disabled: false, }, }, { - "type": "textInput", - "key": "identifier", - "templateOptions": { - "required": true, - "label": "Identifier", - "type": "text", - "placeholder": "", - "disabled": false + type: 'textInput', + key: 'identifier', + templateOptions: { + required: true, + label: 'Identifier', + type: 'text', + placeholder: '', + disabled: false, }, }, { - "type": "formattableInput", - "key": "description", - "templateOptions": { - "required": false, - "label": "Description", - "editorType": "full", - "inlineLabel": true, - "placeholder": "", - "disabled": false + type: 'formattableInput', + key: 'description', + templateOptions: { + required: false, + label: 'Description', + editorType: 'full', + inlineLabel: true, + placeholder: '', + disabled: false, }, }, { - "type": "booleanInput", - "key": "public", - "templateOptions": { - "required": true, - "label": "Public", - "type": "checkbox", - "placeholder": "", - "disabled": false + type: 'booleanInput', + key: 'public', + templateOptions: { + required: true, + label: 'Public', + type: 'checkbox', + placeholder: '', + disabled: false, }, }, { - "type": "booleanInput", - "key": "active", - "templateOptions": { - "required": true, - "label": "Active", - "type": "checkbox", - "placeholder": "", - "disabled": false + type: 'booleanInput', + key: 'active', + templateOptions: { + required: true, + label: 'Active', + type: 'checkbox', + placeholder: '', + disabled: false, }, }, { - "type": "selectInput", - "expressionProperties": {}, - "key": "status", - "templateOptions": { - "required": false, - "label": "Status", - "type": "number", - "locale": "en", - "bindLabel": "title", - "searchable": false, - "virtualScroll": true, - "typeahead": false, - "clearOnBackspace": false, - "clearSearchOnAdd": false, - "hideSelected": false, - "text": { - "add_new_action": "Create" + type: 'selectInput', + expressionProperties: {}, + key: 'status', + templateOptions: { + required: false, + label: 'Status', + type: 'number', + locale: 'en', + bindLabel: 'title', + searchable: false, + virtualScroll: true, + typeahead: false, + clearOnBackspace: false, + clearSearchOnAdd: false, + hideSelected: false, + text: { + add_new_action: 'Create', }, - "placeholder": "", - "disabled": false, - "clearable": true, - "multiple": false + placeholder: '', + disabled: false, + clearable: true, + multiple: false, }, }, { - "type": "formattableInput", - "key": "statusExplanation", - "templateOptions": { - "required": false, - "label": "Status description", - "editorType": "full", - "inlineLabel": true, - "placeholder": "", - "disabled": false + type: 'formattableInput', + key: 'statusExplanation', + templateOptions: { + required: false, + label: 'Status description', + editorType: 'full', + inlineLabel: true, + placeholder: '', + disabled: false, }, }, { - "type": "selectInput", - "expressionProperties": {}, - "key": "_links.parent", - "templateOptions": { - "required": false, - "label": "Subproject of", - "type": "number", - "locale": "en", - "bindLabel": "title", - "searchable": false, - "virtualScroll": true, - "typeahead": false, - "clearOnBackspace": false, - "clearSearchOnAdd": false, - "hideSelected": false, - "text": { - "add_new_action": "Create" + type: 'selectInput', + expressionProperties: {}, + key: '_links.parent', + templateOptions: { + required: false, + label: 'Subproject of', + type: 'number', + locale: 'en', + bindLabel: 'title', + searchable: false, + virtualScroll: true, + typeahead: false, + clearOnBackspace: false, + clearSearchOnAdd: false, + hideSelected: false, + text: { + add_new_action: 'Create', }, - "options": of([]) + options: of([]), }, }, { - "type": "dateInput", - "key": "customField12", - "templateOptions": { - "required": false, - "label": "Date", - "placeholder": "", - "disabled": false + type: 'dateInput', + key: 'customField12', + templateOptions: { + required: false, + label: 'Date', + placeholder: '', + disabled: false, }, - } + }, ], model: { - "identifier": "test11", - "name": "qwe", - "active": true, - "public": false, - "description": { - "format": "markdown", - "raw": "asdadsad", - "html": "

    asdadsad

    " + identifier: 'test11', + name: 'qwe', + active: true, + public: false, + description: { + format: 'markdown', + raw: 'asdadsad', + html: '

    asdadsad

    ', + }, + status: null, + statusExplanation: { + format: 'markdown', + raw: null, + html: '', }, - "status": null, - "statusExplanation": { - "format": "markdown", - "raw": null, - "html": "" + customField12: null, + _links: { + parent: { + href: '/api/v3/projects/23', + title: 'qweqwe', + name: 'qweqwe', + }, }, - "customField12": null, - "_links": { - "parent": { - "href": "/api/v3/projects/23", - "title": "qweqwe", - "name": "qweqwe" - } - } }, form: new FormGroup({}), }; const I18nServiceStub = { - t: function(key:string) { + t(key:string) { return 'test translation'; - } + }, }; const apiV3Base = 'http://www.openproject.com/api/v3/'; const IPathHelperServiceStub = { api: { v3: { apiV3Base } } }; @@ -292,14 +292,14 @@ describe('DynamicFormComponent', () => { ], wrappers: [ { - name: "op-dynamic-field-group-wrapper", + name: 'op-dynamic-field-group-wrapper', component: DynamicFieldGroupWrapperComponent, }, { name: 'op-dynamic-field-wrapper', component: DynamicFieldWrapperComponent, }, - ] + ], }), NgSelectModule, NgOptionHighlightModule, @@ -322,7 +322,7 @@ describe('DynamicFormComponent', () => { { provide: I18nService, useValue: I18nServiceStub }, { provide: PathHelperService, useValue: IPathHelperServiceStub }, { provide: NotificationsService, useValue: notificationsServiceSpy }, - ] + ], }) // Set component providers .overrideComponent( @@ -332,16 +332,16 @@ describe('DynamicFormComponent', () => { providers: [ { provide: DynamicFormService, - useValue: dynamicFormServiceSpy + useValue: dynamicFormServiceSpy, }, { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => DynamicFormComponent), - } - ] - } - } + }, + ], + }, + }, ) .compileComponents(); @@ -360,7 +360,7 @@ describe('DynamicFormComponent', () => { dynamicFormService.getSettingsFromBackend$.and.returnValue(defer(() => Promise.resolve(dynamicFormSettings))); component.resourcePath = '/api/v3/projects/1234/form'; - component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' }} as any); + component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' } } as any); expect(dynamicFormService.getSettingsFromBackend$).toHaveBeenCalled(); @@ -417,7 +417,7 @@ describe('DynamicFormComponent', () => { component.showNotifications = false; component.resourcePath = '/api/v3/projects/1234/form'; - component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' }} as any); + component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' } } as any); flush(); fixture.detectChanges(); submitButton = fixture.debugElement.query(By.css('button[type=submit]')); @@ -442,7 +442,7 @@ describe('DynamicFormComponent', () => { expect(notificationsService.addSuccess).toHaveBeenCalled(); dynamicFormService.submit$.and.returnValue(defer(() => { - throw 'Error' + throw new Error('Error'); })); submitButton.nativeElement.click(); @@ -463,21 +463,21 @@ describe('DynamicFormComponent', () => { ...firstField, expressionProperties: { 'templateOptions.test': expressionPropertiesSpy, - } + }, }; const dynamicFormSettingsForSubmit = { ...dynamicFormSettings, fields: [ firstFieldCopy, ...restOfFields, - ] - } + ], + }; // @ts-ignore dynamicFormService.getSettingsFromBackend$.and.returnValue(defer(() => Promise.resolve(dynamicFormSettingsForSubmit))); dynamicFormService.submit$.and.returnValue(defer(() => Promise.resolve('ok'))); component.resourcePath = '/api/v3/projects/1234/form'; - component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' }} as any); + component.ngOnChanges({ resourcePath: { currentValue: '/api/v3/projects/1234/form' } } as any); flush(); fixture.detectChanges(); const submitButton = fixture.debugElement.query(By.css('button[type=submit]')); @@ -488,4 +488,3 @@ describe('DynamicFormComponent', () => { expect(expressionPropertiesSpy).toHaveBeenCalled(); })); }); - diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.ts index 85587e2f8e..63606c85d8 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component.ts @@ -7,19 +7,19 @@ import { Output, SimpleChanges, ViewChild, -} from "@angular/core"; -import { FormlyForm } from "@ngx-formly/core"; -import { DynamicFormService } from "../../services/dynamic-form/dynamic-form.service"; -import { IDynamicFieldGroupConfig, IOPDynamicFormSettings, IOPFormlyFieldSettings } from "../../typings"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { catchError, finalize } from "rxjs/operators"; -import { HalSource } from "core-app/features/hal/resources/hal-resource"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { DynamicFieldsService } from "core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service"; -import { FormGroup } from "@angular/forms"; -import { HttpErrorResponse } from "@angular/common/http"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +} from '@angular/core'; +import { FormlyForm } from '@ngx-formly/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { catchError, finalize } from 'rxjs/operators'; +import { HalSource } from 'core-app/features/hal/resources/hal-resource'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { DynamicFieldsService } from 'core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service'; +import { FormGroup } from '@angular/forms'; +import { HttpErrorResponse } from '@angular/common/http'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { IDynamicFieldGroupConfig, IOPDynamicFormSettings, IOPFormlyFieldSettings } from '../../typings'; +import { DynamicFormService } from '../../services/dynamic-form/dynamic-form.service'; /** * SETTINGS: @@ -115,9 +115,9 @@ import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destr */ @Component({ - selector: "op-dynamic-form", - templateUrl: "./dynamic-form.component.html", - styleUrls: ["./dynamic-form.component.scss"], + selector: 'op-dynamic-form', + templateUrl: './dynamic-form.component.html', + styleUrls: ['./dynamic-form.component.scss'], providers: [ DynamicFormService, DynamicFieldsService, @@ -126,43 +126,64 @@ import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destr export class DynamicFormComponent extends UntilDestroyedMixin implements OnChanges { /** Backend form URL (e.g. https://community.openproject.org/api/v3/projects/dev-large/form) */ @Input() formUrl?:string; + /** When using the formUrl @Input(), set the http method to use if it is not 'POST' */ @Input() formHttpMethod?:'post'|'patch' = 'post'; + /** Part of the URL that belongs to the resource type (e.g. '/projects' in the previous example) * Use this option when you don't have a form URL, the DynamicForm will build it from the resourcePath * for you (⌐■_■). */ @Input() resourcePath?:string; + /** Pass the resourceId in case you are editing an existing resource and you don't have the Form URL. */ @Input() resourceId?:string; + @Input() settings?:IOPFormSettings; + @Input() dynamicFormGroup?:FormGroup; + /** Initial payload to POST to the form */ @Input() initialPayload:Object = {}; + @Input() set model(payload:IOPFormModel) { - if (!this.innerModel && !payload) { return; } + if (!this.innerModel && !payload) { + return; + } const formattedModel = this._dynamicFormService.formatModelToEdit(payload); this.form.patchValue(formattedModel); } + /** Chance to modify the dynamicFormFields settings before the form is rendered */ @Input() fieldsSettingsPipe?:(dynamicFieldsSettings:IOPFormlyFieldSettings[]) => IOPFormlyFieldSettings[]; + /** Create fieldGroups programmatically */ @Input() fieldGroups?:IDynamicFieldGroupConfig[]; + @Input() showNotifications = true; + @Input() showValidationErrorsOn:'change'|'blur'|'submit'|'never' = 'submit'; + @Input() handleSubmit = true; + @Input() helpTextAttributeScope?:string; @Output() modelChange = new EventEmitter(); + @Output() submitted = new EventEmitter(); + @Output() errored = new EventEmitter(); form:FormGroup; + fields:IOPFormlyFieldSettings[]; + formEndpoint?:string; + inFlight:boolean; + text = { save: this._I18n.t('js.button_save'), load_error_message: this._I18n.t('js.forms.load_error_message'), @@ -170,10 +191,13 @@ export class DynamicFormComponent extends UntilDestroyedMixin implements OnChang successful_create: this._I18n.t('js.notice_successful_create'), job_started: this._I18n.t('js.notice_job_started'), }; + noSettingsSourceErrorMessage = `DynamicFormComponent needs a settings, formUrl or resourcePath @Input in order to fetch its setting. Please provide one.`; + noPathToSubmitToError = `DynamicForm needs a resourcePath or formUrl @Input in order to be submitted and validated. Please provide one.`; + innerModel:IOPFormModel; get model() { @@ -202,15 +226,15 @@ export class DynamicFormComponent extends UntilDestroyedMixin implements OnChang ngOnChanges(changes:SimpleChanges) { if ( - changes.settings || - changes.resourcePath || - changes.resourceId || - changes.formUrl || - changes.formHttpMethod || - changes.dynamicFormGroup || - changes.initialPayload || - changes.fieldsSettingsPipe || - changes.fieldGroups + changes.settings + || changes.resourcePath + || changes.resourceId + || changes.formUrl + || changes.formHttpMethod + || changes.dynamicFormGroup + || changes.initialPayload + || changes.fieldsSettingsPipe + || changes.fieldGroups ) { this.initializeDynamicForm( this.settings, @@ -285,30 +309,30 @@ export class DynamicFormComponent extends UntilDestroyedMixin implements OnChang } } - private getFormEndPoint(formUrl?:string, resourcePath?:string):string|undefined { + private getFormEndPoint(formUrl?:string, resourcePath?:string):string { if (formUrl) { - return formUrl.endsWith(`/form`) ? - formUrl.replace(`/form`, ``) : - formUrl; + return formUrl.endsWith('/form') + ? formUrl.replace('/form', '') + : formUrl; } if (resourcePath) { return resourcePath; } - return; + return ''; } private setupDynamicFormFromBackend(formEndpoint?:string, resourceId?:string, payload?:Object) { this._dynamicFormService .getSettingsFromBackend$(formEndpoint, resourceId, payload) .pipe( - catchError(error => { + catchError((error) => { this._notificationsService.addError(this.text.load_error_message); throw error; }), ) - .subscribe(dynamicFormSettings => this.setupDynamicForm(dynamicFormSettings)); + .subscribe((dynamicFormSettings) => this.setupDynamicForm(dynamicFormSettings)); } private setupDynamicFormFromSettings(settings:IOPFormSettings) { diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.spec.ts index 1996550598..6c75036d2c 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.spec.ts @@ -2,19 +2,19 @@ import { fakeAsync } from '@angular/core/testing'; import { createDynamicInputFixture, testDynamicInputControValueAccessor, -} from "core-app/shared/components/dynamic-forms/spec/helpers"; +} from 'core-app/shared/components/dynamic-forms/spec/helpers'; describe('BooleanInputComponent', () => { it('should load the field', fakeAsync(() => { const fieldsConfig = [ { - "type": "booleanInput" as "booleanInput", - "key": "testControl", - "templateOptions": { - "required": true, - "label": "testControl", + type: 'booleanInput' as const, + key: 'testControl', + templateOptions: { + required: true, + label: 'testControl', }, - } + }, ]; const formModel = { testControl: true, diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.ts index f5114899ec..48fa699511 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; +import { FieldType } from '@ngx-formly/core'; @Component({ selector: 'op-boolean-input', templateUrl: './boolean-input.component.html', - styleUrls: ['./boolean-input.component.scss'] + styleUrls: ['./boolean-input.component.scss'], }) export class BooleanInputComponent extends FieldType { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-adapter/date-picker-adapter.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-adapter/date-picker-adapter.component.ts index cca4f70361..8dfa81ce00 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-adapter/date-picker-adapter.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-adapter/date-picker-adapter.component.ts @@ -1,8 +1,10 @@ -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, NgZone } from '@angular/core'; -import { OpDatePickerComponent } from "core-app/shared/components/op-date-picker/op-date-picker.component"; -import * as moment from "moment"; -import { NG_VALUE_ACCESSOR } from "@angular/forms"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { + AfterViewInit, ChangeDetectorRef, Component, forwardRef, NgZone, +} from '@angular/core'; +import { OpDatePickerComponent } from 'core-app/shared/components/op-date-picker/op-date-picker.component'; +import * as moment from 'moment'; +import { NG_VALUE_ACCESSOR } from '@angular/forms'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ selector: 'op-date-picker-adapter', @@ -11,17 +13,18 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service"; { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatePickerAdapterComponent), - multi: true - } - ] + multi: true, + }, + ], }) export class DatePickerAdapterComponent extends OpDatePickerComponent implements AfterViewInit { - onControlChange = (_:any) => { } - onControlTouch = () => { } + onControlChange = (_:any) => { }; + + onControlTouch = () => { }; constructor( timezoneService:TimezoneService, - private ngZone: NgZone, + private ngZone:NgZone, private changeDetectorRef:ChangeDetectorRef, ) { super(timezoneService); @@ -31,15 +34,15 @@ export class DatePickerAdapterComponent extends OpDatePickerComponent implements this.initialDate = this.formatter(date); } - registerOnChange(fn: (_: any) => void): void { + registerOnChange(fn:(_:any) => void):void { this.onControlChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(fn:any):void { this.onControlTouch = fn; } - setDisabledState(disabled: boolean): void { + setDisabledState(disabled:boolean):void { this.disabled = disabled; } @@ -70,18 +73,16 @@ export class DatePickerAdapterComponent extends OpDatePickerComponent implements public parser(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { return data; - } else { - return null; } + return null; } public formatter(data:any):string { if (moment(data, 'YYYY-MM-DD', true).isValid()) { - var d = this.timezoneService.parseDate(data); + const d = this.timezoneService.parseDate(data); return this.timezoneService.formattedISODate(d); - } else { - return ''; } + return ''; } } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component.ts index e950b821ab..2fd65ba1c7 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component.ts @@ -1,8 +1,10 @@ -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, NgZone } from '@angular/core'; -import * as moment from "moment"; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; -import { OpDatePickerComponent } from "core-app/shared/components/op-date-picker/op-date-picker.component"; +import { + AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, NgZone, +} from '@angular/core'; +import * as moment from 'moment'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; +import { OpDatePickerComponent } from 'core-app/shared/components/op-date-picker/op-date-picker.component'; @Component({ selector: 'op-date-picker-control', @@ -11,20 +13,21 @@ import { OpDatePickerComponent } from "core-app/shared/components/op-date-picker { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DatePickerControlComponent), - multi: true - } - ] + multi: true, + }, + ], }) export class DatePickerControlComponent extends OpDatePickerComponent implements ControlValueAccessor, AfterViewInit { // Avoid Angular warning (It looks like you're using the disabled attribute with a reactive form directive...) @Input('disable') disabled:boolean; - onControlChange = (_:any) => { } - onControlTouch = () => { } + onControlChange = (_:any) => { }; + + onControlTouch = () => { }; constructor( timezoneService:TimezoneService, - private ngZone: NgZone, + private ngZone:NgZone, private changeDetectorRef:ChangeDetectorRef, ) { super(timezoneService); @@ -34,15 +37,15 @@ export class DatePickerControlComponent extends OpDatePickerComponent implements this.initialDate = this.formatter(date); } - registerOnChange(fn: (_: any) => void): void { + registerOnChange(fn:(_:any) => void):void { this.onControlChange = fn; } - registerOnTouched(fn: any): void { + registerOnTouched(fn:any):void { this.onControlTouch = fn; } - setDisabledState(disabled: boolean): void { + setDisabledState(disabled:boolean):void { this.disabled = disabled; } @@ -56,9 +59,9 @@ export class DatePickerControlComponent extends OpDatePickerComponent implements } onInputChange(_event:KeyboardEvent) { - let valueToEmit = this.inputIsValidDate() ? - this.parser(this.currentValue) : - ''; + const valueToEmit = this.inputIsValidDate() + ? this.parser(this.currentValue) + : ''; this.onControlChange(valueToEmit); this.onControlTouch(); @@ -72,18 +75,16 @@ export class DatePickerControlComponent extends OpDatePickerComponent implements public parser(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { return data; - } else { - return null; } + return null; } public formatter(data:any):string { if (moment(data, 'YYYY-MM-DD', true).isValid()) { - var d = this.timezoneService.parseDate(data); + const d = this.timezoneService.parseDate(data); return this.timezoneService.formattedISODate(d); - } else { - return ''; } + return ''; } } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module.ts index a794d42100..7e06ff98fd 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module.ts @@ -1,10 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { DatePickerModule } from "core-app/shared/components/op-date-picker/date-picker.module"; -import { DatePickerControlComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component"; - - - +import { DatePickerModule } from 'core-app/shared/components/op-date-picker/date-picker.module'; +import { DatePickerControlComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.component'; @NgModule({ declarations: [ @@ -16,6 +13,6 @@ import { DatePickerControlComponent } from "core-app/shared/components/dynamic-f ], exports: [ DatePickerControlComponent, - ] + ], }) export class DatePickerControlModule { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.spec.ts index d60a4f59c5..b85cf7ec2a 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.spec.ts @@ -3,14 +3,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DateInputComponent } from './date-input.component'; xdescribe('DateInputComponent', () => { - let component: DateInputComponent; - let fixture: ComponentFixture; + let component:DateInputComponent; + let fixture:ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ DateInputComponent ] + declarations: [DateInputComponent], }) - .compileComponents(); + .compileComponents(); }); beforeEach(() => { diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.ts index bcdb398900..0f9cc3a588 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; +import { FieldType } from '@ngx-formly/core'; @Component({ selector: 'op-date-input', diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.spec.ts index ffad2a0905..fa81058754 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.spec.ts @@ -3,14 +3,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormattableControlComponent } from './formattable-control.component'; xdescribe('FormattableControlComponent', () => { - let component: FormattableControlComponent; - let fixture: ComponentFixture; + let component:FormattableControlComponent; + let fixture:ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ FormattableControlComponent ] + declarations: [FormattableControlComponent], }) - .compileComponents(); + .compileComponents(); }); beforeEach(() => { diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.ts index 9850d47f71..daa1979e9e 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.ts @@ -1,9 +1,14 @@ -import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { FormlyTemplateOptions } from "@ngx-formly/core"; -import { ICKEditorContext, ICKEditorInstance } from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; -import { OpCkeditorComponent } from "core-app/shared/components/editor/components/ckeditor/op-ckeditor.component"; +import { + Component, forwardRef, Input, OnInit, ViewChild, +} from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { FormlyTemplateOptions } from '@ngx-formly/core'; +import { + ICKEditorContext, + ICKEditorInstance, +} from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { OpCkeditorComponent } from 'core-app/shared/components/editor/components/ckeditor/op-ckeditor.component'; @Component({ selector: 'op-formattable-control', @@ -13,9 +18,9 @@ import { OpCkeditorComponent } from "core-app/shared/components/editor/component { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FormattableControlComponent), - multi: true - } - ] + multi: true, + }, + ], }) export class FormattableControlComponent implements ControlValueAccessor, OnInit { @Input() templateOptions:FormlyTemplateOptions; @@ -23,19 +28,25 @@ export class FormattableControlComponent implements ControlValueAccessor, OnInit @ViewChild(OpCkeditorComponent, { static: true }) editor:OpCkeditorComponent; text:{ [key:string]:string }; + value:{ raw:string }; + disabled = false; + touched:boolean; + // Detect when inner component could not be initialized initializationError = false; + onChange:(_any:unknown) => void = () => undefined; + onTouch:() => void = () => undefined; public get ckEditorContext():ICKEditorContext { return { type: this.templateOptions.editorType, macros: 'none', - options: { rtl: this.templateOptions?.rtl } + options: { rtl: this.templateOptions?.rtl }, }; } @@ -48,7 +59,7 @@ export class FormattableControlComponent implements ControlValueAccessor, OnInit this.text = { attachmentLabel: this.I18n.t('js.label_formattable_attachment_hint'), save: this.I18n.t('js.inplace.button_save', { attribute: this.templateOptions?.name }), - cancel: this.I18n.t('js.inplace.button_cancel', { attribute: this.templateOptions?.name }) + cancel: this.I18n.t('js.inplace.button_cancel', { attribute: this.templateOptions?.name }), }; } @@ -84,6 +95,7 @@ export class FormattableControlComponent implements ControlValueAccessor, OnInit this.touched = true; this.onTouch(); } - }); + }, + ); } } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module.ts index 84a4c11bb3..7b372d4a30 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module.ts @@ -1,10 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { FormattableControlComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component"; -import { OpenprojectEditorModule } from "core-app/shared/components/editor/openproject-editor.module"; -import { FormattableEditFieldModule } from "core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module"; - - +import { FormattableControlComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component'; +import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; +import { FormattableEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module'; @NgModule({ declarations: [ @@ -17,6 +15,6 @@ import { FormattableEditFieldModule } from "core-app/shared/components/fields/ed ], exports: [ FormattableControlComponent, - ] + ], }) export class FormattableControlModule { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.spec.ts index 0a19493c42..ec7f034ebe 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.spec.ts @@ -1,8 +1,6 @@ import { discardPeriodicTasks, fakeAsync, flush, tick } from '@angular/core/testing'; -import { - createDynamicInputFixture, -} from "core-app/shared/components/dynamic-forms/spec/helpers"; -import { By } from "@angular/platform-browser"; +import { createDynamicInputFixture } from 'core-app/shared/components/dynamic-forms/spec/helpers'; +import { By } from '@angular/platform-browser'; // @ts-ignore import(/* webpackChunkName: "ckeditor-augmented-textarea" */ 'core-vendor/ckeditor/ckeditor.js'); @@ -10,24 +8,24 @@ describe('FormattableTextareaInputComponent', () => { it('should load the field', fakeAsync(() => { const fieldsConfig = [ { - "type": "formattableInput" as "formattableInput", - "key": "testControl", - "templateOptions": { - "required": true, - "label": "testControl", - "type": "text", - "placeholder": "", - "disabled": false, - "bindLabel": 'name', - "bindValue": 'value', - "noWrapLabel": true + type: 'formattableInput' as const, + key: 'testControl', + templateOptions: { + required: true, + label: 'testControl', + type: 'text', + placeholder: '', + disabled: false, + bindLabel: 'name', + bindValue: 'value', + noWrapLabel: true, }, - } + }, ]; const formModel = { testControl: { html: '

    tesValue

    ', - raw: 'tesValue' + raw: 'tesValue', }, }; const testModel = { @@ -55,4 +53,3 @@ describe('FormattableTextareaInputComponent', () => { discardPeriodicTasks(); })); }); - diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.ts index f0f75f1c6b..97b513ac15 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; +import { FieldType } from '@ngx-formly/core'; @Component({ selector: 'op-formattable-textarea-input', templateUrl: './formattable-textarea-input.component.html', - styleUrls: ['./formattable-textarea-input.component.scss'] + styleUrls: ['./formattable-textarea-input.component.scss'], }) export class FormattableTextareaInputComponent extends FieldType { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.spec.ts index 327553e445..a7f45e259d 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.spec.ts @@ -2,19 +2,19 @@ import { fakeAsync } from '@angular/core/testing'; import { createDynamicInputFixture, testDynamicInputControValueAccessor, -} from "core-app/shared/components/dynamic-forms/spec/helpers"; +} from 'core-app/shared/components/dynamic-forms/spec/helpers'; describe('IntegerInputComponent', () => { it('should load the field', fakeAsync(() => { const fieldsConfig = [ { - "type": "integerInput" as "integerInput", - "key": "testControl", - "templateOptions": { - "required": true, - "label": "testControl", + type: 'integerInput' as const, + key: 'testControl', + templateOptions: { + required: true, + label: 'testControl', }, - } + }, ]; const formModel = { testControl: 'testValue', @@ -29,4 +29,3 @@ describe('IntegerInputComponent', () => { testDynamicInputControValueAccessor(fixture, testModel, 'op-integer-input input'); })); }); - diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.ts index c8892eaecd..25312b1e79 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; +import { FieldType } from '@ngx-formly/core'; @Component({ selector: 'op-integer-input', templateUrl: './integer-input.component.html', - styleUrls: ['./integer-input.component.scss'] + styleUrls: ['./integer-input.component.scss'], }) export class IntegerInputComponent extends FieldType { } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.spec.ts index 41ece9b333..781f645b83 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.spec.ts @@ -1,9 +1,7 @@ import { fakeAsync, flush } from '@angular/core/testing'; -import { - createDynamicInputFixture, -} from "core-app/shared/components/dynamic-forms/spec/helpers"; -import { By } from "@angular/platform-browser"; -import { of } from "rxjs"; +import { createDynamicInputFixture } from 'core-app/shared/components/dynamic-forms/spec/helpers'; +import { By } from '@angular/platform-browser'; +import { of } from 'rxjs'; describe('SelectInputComponent', () => { it('should load the field', fakeAsync(() => { @@ -15,23 +13,23 @@ describe('SelectInputComponent', () => { { name: 'option2', value: 2, - } + }, ]; const fieldsConfig = [ { - "type": "selectInput" as "selectInput", - "key": "testControl", - "templateOptions": { - "required": true, - "label": "testControl", - "type": "text", - "placeholder": "", - "disabled": false, - "options": of(selectOptions), - "bindLabel": 'name', - "bindValue": 'value', + type: 'selectInput' as const, + key: 'testControl', + templateOptions: { + required: true, + label: 'testControl', + type: 'text', + placeholder: '', + disabled: false, + options: of(selectOptions), + bindLabel: 'name', + bindValue: 'value', }, - } + }, ]; const formModel = { testControl: selectOptions[0], diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.ts index 11de180194..9d0f5189db 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component.ts @@ -1,18 +1,18 @@ import { Component, OnInit } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { FieldType } from '@ngx-formly/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ selector: 'op-select-input', templateUrl: './select-input.component.html', - styleUrls: ['./select-input.component.scss'] + styleUrls: ['./select-input.component.scss'], }) export class SelectInputComponent extends FieldType implements OnInit { projectId:string|undefined; public ngOnInit():void { if (this.model?.project) { - this.projectId = HalResource.idFromLink(this.model.project?.href) + this.projectId = HalResource.idFromLink(this.model.project?.href); } } } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component.ts index c8c7bf6670..2a03ca85cb 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component.ts @@ -1,13 +1,13 @@ import { Component } from '@angular/core'; -import { FieldType } from "@ngx-formly/core"; -import { projectStatusCodeCssClass } from "core-app/shared/components/fields/helpers/project-status-helper"; +import { FieldType } from '@ngx-formly/core'; +import { projectStatusCodeCssClass } from 'core-app/shared/components/fields/helpers/project-status-helper'; @Component({ selector: 'op-select-project-status-input', - templateUrl: './select-project-status-input.component.html' + templateUrl: './select-project-status-input.component.html', }) export class SelectProjectStatusInputComponent extends FieldType { cssClass(item:any) { - return projectStatusCodeCssClass(item.id) + return projectStatusCodeCssClass(item.id); } } diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.spec.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.spec.ts index b155bf8938..f7e4342318 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.spec.ts @@ -2,22 +2,22 @@ import { fakeAsync } from '@angular/core/testing'; import { createDynamicInputFixture, testDynamicInputControValueAccessor, -} from "core-app/shared/components/dynamic-forms/spec/helpers"; +} from 'core-app/shared/components/dynamic-forms/spec/helpers'; describe('TextInputComponent', () => { it('should load the field', fakeAsync(() => { const fieldsConfig = [ { - "type": "textInput" as "textInput", - "key": "testControl", - "templateOptions": { - "required": true, - "label": "testControl", - "type": "text", - "placeholder": "", - "disabled": false + type: 'textInput' as const, + key: 'testControl', + templateOptions: { + required: true, + label: 'testControl', + type: 'text', + placeholder: '', + disabled: false, }, - } + }, ]; const formModel = { testControl: 'testValue', diff --git a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.ts b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.ts index fe9db09989..d5190fa31f 100644 --- a/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.ts +++ b/frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component.ts @@ -4,7 +4,7 @@ import { FieldType } from '@ngx-formly/core'; @Component({ selector: 'op-text-input', templateUrl: './text-input.component.html', - styleUrls: ['./text-input.component.scss'] + styleUrls: ['./text-input.component.scss'], }) export class TextInputComponent extends FieldType { } diff --git a/frontend/src/app/shared/components/dynamic-forms/dynamic-forms.module.ts b/frontend/src/app/shared/components/dynamic-forms/dynamic-forms.module.ts index 79cd8982a2..baf85a1b89 100644 --- a/frontend/src/app/shared/components/dynamic-forms/dynamic-forms.module.ts +++ b/frontend/src/app/shared/components/dynamic-forms/dynamic-forms.module.ts @@ -1,26 +1,25 @@ -import { NgModule } from "@angular/core"; -import { CommonModule } from "@angular/common"; -import { TextInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component"; -import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; -import { IntegerInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component"; -import { FormlyModule } from "@ngx-formly/core"; -import { SelectInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component"; -import { SelectProjectStatusInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component"; -import { NgOptionHighlightModule } from "@ng-select/ng-option-highlight"; -import { NgSelectModule } from "@ng-select/ng-select"; -import { DatePickerControlModule } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module"; -import { BooleanInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component"; -import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"; -import { FormattableTextareaInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component"; -import { DynamicFieldWrapperComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component"; -import { InviteUserButtonModule } from "core-app/features/invite-user-modal/button/invite-user-button.module"; -import { DateInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component"; -import { DynamicFieldGroupWrapperComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component"; -import { OpenProjectHeaderInterceptor } from "core-app/features/hal/http/openproject-header-interceptor"; -import { ReactiveFormsModule } from "@angular/forms"; -import { FormattableControlModule } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module"; -import { OPSharedModule } from "core-app/shared/shared.module"; - +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TextInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component'; +import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; +import { IntegerInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component'; +import { FormlyModule } from '@ngx-formly/core'; +import { SelectInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component'; +import { SelectProjectStatusInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component'; +import { NgOptionHighlightModule } from '@ng-select/ng-option-highlight'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { DatePickerControlModule } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/components/date-picker-control/date-picker-control.module'; +import { BooleanInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component'; +import { DynamicFormComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component'; +import { FormattableTextareaInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component'; +import { DynamicFieldWrapperComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-field-wrapper/dynamic-field-wrapper.component'; +import { InviteUserButtonModule } from 'core-app/features/invite-user-modal/button/invite-user-button.module'; +import { DateInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component'; +import { DynamicFieldGroupWrapperComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component'; +import { OpenProjectHeaderInterceptor } from 'core-app/features/hal/http/openproject-header-interceptor'; +import { ReactiveFormsModule } from '@angular/forms'; +import { FormattableControlModule } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.module'; +import { OPSharedModule } from 'core-app/shared/shared.module'; @NgModule({ imports: [ @@ -45,7 +44,7 @@ import { OPSharedModule } from "core-app/shared/shared.module"; name: 'op-dynamic-field-wrapper', component: DynamicFieldWrapperComponent, }, - ] + ], }), HttpClientModule, OPSharedModule, @@ -75,6 +74,6 @@ import { OPSharedModule } from "core-app/shared/shared.module"; ], exports: [ DynamicFormComponent, - ] + ], }) export class DynamicFormsModule {} diff --git a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.spec.ts b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.spec.ts index f5ee76613d..c2ab0a6723 100644 --- a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.spec.ts @@ -1,13 +1,13 @@ import { TestBed } from '@angular/core/testing'; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { HttpClient } from "@angular/common/http"; -import { DynamicFieldsService } from "core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service"; -import { isObservable } from "rxjs"; -import { IOPFormlyFieldSettings } from "core-app/shared/components/dynamic-forms/typings"; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClient } from '@angular/common/http'; +import { DynamicFieldsService } from 'core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service'; +import { isObservable } from 'rxjs'; +import { IOPFormlyFieldSettings } from 'core-app/shared/components/dynamic-forms/typings'; describe('DynamicFieldsService', () => { - let httpClient: HttpClient; - let httpTestingController: HttpTestingController; + let httpClient:HttpClient; + let httpTestingController:HttpTestingController; let service:DynamicFieldsService; beforeEach(() => { @@ -17,7 +17,7 @@ describe('DynamicFieldsService', () => { ], providers: [ DynamicFieldsService, - ] + ], }); httpClient = TestBed.inject(HttpClient); httpTestingController = TestBed.inject(HttpTestingController); @@ -30,45 +30,45 @@ describe('DynamicFieldsService', () => { it('should generate a proper dynamic form schema', () => { const formPayload = { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project" - } - } + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', + }, + }, }; const formSchema = { - "name": { - "type": "String", - "name": "Name", - "required": true, - "hasDefault": false, - "writable": true, - "minLength": 1, - "maxLength": 255, - "options": {} + name: { + type: 'String', + name: 'Name', + required: true, + hasDefault: false, + writable: true, + minLength: 1, + maxLength: 255, + options: {}, }, - "parent": { - "type": "Project", - "name": "Subproject of", - "required": false, - "hasDefault": false, - "location": "_links", - "writable": true, - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + parent: { + type: 'Project', + name: 'Subproject of', + required: false, + hasDefault: false, + location: '_links', + writable: true, + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, - "id": { - "type": "Integer", - "name": "ID", - "required": true, - "hasDefault": false, - "writable": false, - "options": {} + id: { + type: 'Integer', + name: 'ID', + required: true, + hasDefault: false, + writable: false, + options: {}, }, _dependencies: [], }; @@ -83,59 +83,59 @@ describe('DynamicFieldsService', () => { it('should format the form model (add the name property to resources (_links: single and multiple))', () => { const formPayload = { - "title": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project" + title: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', }, - "children": [ + children: [ { - "href": "/api/v3/projects/27", - "title": "Child project 1" + href: '/api/v3/projects/27', + title: 'Child project 1', }, { - "href": "/api/v3/projects/28", - "title": "Child project 2" - } - ] + href: '/api/v3/projects/28', + title: 'Child project 2', + }, + ], }, }; const formSchema = { - "title": { - "type": "String", - "name": "Name", - "required": true, - "hasDefault": false, - "writable": true, - "minLength": 1, - "maxLength": 255, - "options": {} + title: { + type: 'String', + name: 'Name', + required: true, + hasDefault: false, + writable: true, + minLength: 1, + maxLength: 255, + options: {}, }, - "parent": { - "type": "Project", - "name": "Subproject of", - "required": false, - "hasDefault": false, - "writable": true, - "location": "_links", - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + parent: { + type: 'Project', + name: 'Subproject of', + required: false, + hasDefault: false, + writable: true, + location: '_links', + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, - "children": { - "type": "Project", - "name": "Project's children", - "required": false, - "hasDefault": false, - "writable": true, - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + children: { + type: 'Project', + name: "Project's children", + required: false, + hasDefault: false, + writable: true, + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, _dependencies: [], }; @@ -153,53 +153,51 @@ describe('DynamicFieldsService', () => { it('should generate a proper dynamic form config', () => { const formPayload = { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project" + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', }, - } + }, }; const formSchema = { - "parent": { - "type": "Project", - "name": "Subproject of", - "required": false, - "hasDefault": false, - "writable": true, - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + parent: { + type: 'Project', + name: 'Subproject of', + required: false, + hasDefault: false, + writable: true, + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, - "name": { - "type": "String", - "name": "Name", - "required": true, - "hasDefault": false, - "writable": true, - "minLength": 1, - "maxLength": 255, - "options": {}, - "attributeGroup": "People" + name: { + type: 'String', + name: 'Name', + required: true, + hasDefault: false, + writable: true, + minLength: 1, + maxLength: 255, + options: {}, + attributeGroup: 'People', }, _attributeGroups: [ { - "_type": "WorkPackageFormAttributeGroup", - "name": "People", - "attributes": [ - "name", - ] + _type: 'WorkPackageFormAttributeGroup', + name: 'People', + attributes: [ + 'name', + ], }, - ] + ], }; // @ts-ignore const formlyConfig = service.getConfig(formSchema, formPayload); - const formlyFields = formlyConfig.reduce((result, formlyField) => { - return formlyField.fieldGroup ? [...result, ...formlyField.fieldGroup] : [...result, formlyField]; - }, [] as IOPFormlyFieldSettings[]); + const formlyFields = formlyConfig.reduce((result, formlyField) => (formlyField.fieldGroup ? [...result, ...formlyField.fieldGroup] : [...result, formlyField]), [] as IOPFormlyFieldSettings[]); const formGroup = formlyConfig[1]; expect(formlyFields[1].templateOptions!.label).toBe('Name', 'should set the correct label'); diff --git a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.ts b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.ts index 469228ed28..82dd56cbf5 100644 --- a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.ts +++ b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service.ts @@ -1,20 +1,17 @@ import { Injectable } from '@angular/core'; -import { - IDynamicFieldGroupConfig, - IOPDynamicInputTypeSettings, - IOPFormlyFieldSettings, -} from "../../typings"; -import { FormlyFieldConfig } from "@ngx-formly/core"; -import { Observable, of } from "rxjs"; -import { map } from "rxjs/operators"; -import { HttpClient } from "@angular/common/http"; -import { HalLink } from "core-app/features/hal/hal-link/hal-link"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { FormsService } from "core-app/core/forms/forms.service"; +import { FormlyFieldConfig } from '@ngx-formly/core'; +import { Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { FormsService } from 'core-app/core/forms/forms.service'; +import { IDynamicFieldGroupConfig, IOPDynamicInputTypeSettings, IOPFormlyFieldSettings } from '../../typings'; @Injectable() export class DynamicFieldsService { readonly selectDefaultValue = { name: '-', _links: { self: { href: null } } }; + readonly inputsCatalogue:IOPDynamicInputTypeSettings[] = [ { config: { @@ -23,7 +20,7 @@ export class DynamicFieldsService { type: 'text', }, }, - useForFields: ['String'] + useForFields: ['String'], }, { config: { @@ -32,7 +29,7 @@ export class DynamicFieldsService { type: 'password', }, }, - useForFields: ['Password'] + useForFields: ['Password'], }, { config: { @@ -42,7 +39,7 @@ export class DynamicFieldsService { locale: this.I18n.locale, }, }, - useForFields: ['Integer', 'Float'] + useForFields: ['Integer', 'Float'], }, { config: { @@ -51,13 +48,13 @@ export class DynamicFieldsService { type: 'checkbox', }, }, - useForFields: ['Boolean'] + useForFields: ['Boolean'], }, { config: { type: 'dateInput', }, - useForFields: ['Date', 'DateTime'] + useForFields: ['Date', 'DateTime'], }, { config: { @@ -68,7 +65,7 @@ export class DynamicFieldsService { noWrapLabel: true, }, }, - useForFields: ['Formattable'] + useForFields: ['Formattable'], }, { config: { @@ -92,8 +89,8 @@ export class DynamicFieldsService { }, useForFields: [ 'Priority', 'Status', 'Type', 'User', 'Version', 'TimeEntriesActivity', - 'Category', 'CustomOption', 'Project' - ] + 'Category', 'CustomOption', 'Project', + ], }, { config: { @@ -109,8 +106,8 @@ export class DynamicFieldsService { }, }, useForFields: [ - 'ProjectStatus' - ] + 'ProjectStatus', + ], }, ]; @@ -122,14 +119,14 @@ export class DynamicFieldsService { } getConfig(formSchema:IOPFormSchema, formPayload:IOPFormModel):IOPFormlyFieldSettings[] { - const formFieldGroups = formSchema._attributeGroups?.map(fieldGroup => ({ + const formFieldGroups = formSchema._attributeGroups?.map((fieldGroup) => ({ name: fieldGroup.name, fieldsFilter: (field:IOPFormlyFieldSettings) => fieldGroup.attributes?.includes(field.templateOptions?.property!), })); const fieldSchemas = this.getFieldsSchemasWithKey(formSchema); const formlyFields = fieldSchemas - .map(fieldSchema => this.getFormlyFieldConfig(fieldSchema, formPayload)) - .filter(f => f !== null) as IOPFormlyFieldSettings[]; + .map((fieldSchema) => this.getFormlyFieldConfig(fieldSchema, formPayload)) + .filter((f) => f !== null) as IOPFormlyFieldSettings[]; const formlyFormWithFieldGroups = this.getFormlyFormWithFieldGroups(formFieldGroups, formlyFields); return formlyFormWithFieldGroups; @@ -141,10 +138,8 @@ export class DynamicFieldsService { getFormlyFormWithFieldGroups(fieldGroups:IDynamicFieldGroupConfig[] = [], formFields:IOPFormlyFieldSettings[] = []):IOPFormlyFieldSettings[] { // Remove previous grouping - formFields = formFields.reduce((result:IOPFormlyFieldSettings[], formField) => { - return formField.fieldGroup ? [...result, ...formField.fieldGroup] : [...result, formField]; - }, []); - const formFieldsWithoutGroup = formFields.filter(formField => fieldGroups.every(fieldGroup => !fieldGroup.fieldsFilter || !fieldGroup.fieldsFilter(formField))); + formFields = formFields.reduce((result:IOPFormlyFieldSettings[], formField) => (formField.fieldGroup ? [...result, ...formField.fieldGroup] : [...result, formField]), []); + const formFieldsWithoutGroup = formFields.filter((formField) => fieldGroups.every((fieldGroup) => !fieldGroup.fieldsFilter || !fieldGroup.fieldsFilter(formField))); const formFieldGroups = this.getDynamicFormFieldGroups(fieldGroups, formFields); return [...formFieldsWithoutGroup, ...formFieldGroups]; @@ -152,20 +147,20 @@ export class DynamicFieldsService { private getFieldsSchemasWithKey(formSchema:IOPFormSchema):IOPFieldSchemaWithKey[] { return Object.keys(formSchema) - .map(fieldSchemaKey => { + .map((fieldSchemaKey) => { const fieldSchema = { ...formSchema[fieldSchemaKey], - key: this.getAttributeKey(formSchema[fieldSchemaKey], fieldSchemaKey) + key: this.getAttributeKey(formSchema[fieldSchemaKey], fieldSchemaKey), }; return fieldSchema; }) - .filter(fieldSchema => this.isFieldSchema(fieldSchema) && fieldSchema.writable); + .filter((fieldSchema) => this.isFieldSchema(fieldSchema) && fieldSchema.writable); } private getAttributeKey(fieldSchema:IOPFieldSchema, key:string):string { switch (fieldSchema.location) { - case "_meta": + case '_meta': return `${fieldSchema.location}.${key}`; default: return key; @@ -177,14 +172,16 @@ export class DynamicFieldsService { } private getFormlyFieldConfig(fieldSchema:IOPFieldSchemaWithKey, formPayload:IOPFormModel):IOPFormlyFieldSettings|null { - const { key, name: label, required, hasDefault, minLength, maxLength } = fieldSchema; + const { + key, name: label, required, hasDefault, minLength, maxLength, + } = fieldSchema; const fieldTypeConfigSearch = this.getFieldTypeConfig(fieldSchema); if (!fieldTypeConfigSearch) { return null; } const { templateOptions, ...fieldTypeConfig } = fieldTypeConfigSearch; const property = this.getFieldProperty(key); - const payloadValue = property && (formPayload[property] || formPayload['_links'] && formPayload['_links'][property]); + const payloadValue = property && (formPayload[property] || formPayload._links && formPayload._links[property]); const fieldOptions = this.getFieldOptions(fieldSchema, payloadValue); const formlyFieldConfig = { ...fieldTypeConfig, @@ -209,16 +206,16 @@ export class DynamicFieldsService { private getFieldTypeConfig(field:IOPFieldSchemaWithKey):IOPFormlyFieldSettings|null { const fieldType = field.type.replace('[]', '') as OPFieldType; - let inputType = this.inputsCatalogue.find(inputType => inputType.useForFields.includes(fieldType))!; + const inputType = this.inputsCatalogue.find((inputType) => inputType.useForFields.includes(fieldType))!; if (!inputType) { console.warn( - `Could not find a input definition for a field with the folowing type: ${fieldType}. The full field configuration is`, field + `Could not find a input definition for a field with the folowing type: ${fieldType}. The full field configuration is`, field, ); return null; } - let inputConfig = inputType.config; + const inputConfig = inputType.config; let configCustomizations; if (inputConfig.type === 'integerInput' || inputConfig.type === 'selectInput' || inputConfig.type === 'selectProjectStatusInput') { @@ -252,23 +249,23 @@ export class DynamicFieldsService { } if (Array.isArray(allowedValues)) { - const optionsValues = allowedValues[0]?._links?.self?.title ? - this.formatAllowedValues(allowedValues) : - allowedValues; + const optionsValues = allowedValues[0]?._links?.self?.title + ? this.formatAllowedValues(allowedValues) + : allowedValues; options = of(optionsValues); - } else if (allowedValues!.href) { + } else if (allowedValues.href) { options = this.httpClient - .get(allowedValues!.href!) + .get(allowedValues.href) .pipe( map((response:api.v3.Result) => response._embedded.elements), - map(options => this.formatAllowedValues(options)), + map((options) => this.formatAllowedValues(options)), ); } return options?.pipe( - map(options => this.prependCurrentValue(options, currentValue)), - map(options => this.prependDefaultValue(options, field)) + map((options) => this.prependCurrentValue(options, currentValue)), + map((options) => this.prependDefaultValue(options, field)), ); } @@ -297,8 +294,8 @@ export class DynamicFieldsService { expressionProperties: { ...newFormFieldGroup.expressionProperties, ...fieldGroup.settings.expressionProperties && fieldGroup.settings.expressionProperties, - } - } + }, + }; } if (newFormFieldGroup?.fieldGroup?.length) { @@ -321,56 +318,52 @@ export class DynamicFieldsService { }, fieldGroup: this.getGroupFields(fieldGroupConfig, formFields), expressionProperties: { - 'templateOptions.collapsibleFieldGroupsCollapsed': this.collapsibleFieldGroupsCollapsedExpressionProperty - } + 'templateOptions.collapsibleFieldGroupsCollapsed': this.collapsibleFieldGroupsCollapsedExpressionProperty, + }, }; return defaultFieldGroupSettings; } private getGroupFields(fieldGroupConfig:IDynamicFieldGroupConfig, formFields:IOPFormlyFieldSettings[]) { - return formFields.filter(formField => { + return formFields.filter((formField) => { const formFieldKey = formField.key && this.getFieldProperty(formField.key); if (!formFieldKey) { return false; - } else if (fieldGroupConfig.fieldsFilter) { + } if (fieldGroupConfig.fieldsFilter) { return fieldGroupConfig.fieldsFilter(formField); - } else { - return true; } - }) + return true; + }); } private collapsibleFieldGroupsCollapsedExpressionProperty(model:any, formState:any, field:FormlyFieldConfig) { // Uncollapse field groups when the form has errors and is submitted if ( - field.type !== 'formly-group' || - !field.templateOptions?.collapsibleFieldGroups || - !field.templateOptions?.collapsibleFieldGroupsCollapsed + field.type !== 'formly-group' + || !field.templateOptions?.collapsibleFieldGroups + || !field.templateOptions?.collapsibleFieldGroupsCollapsed ) { - return; - } else { - return !( - field.fieldGroup?.some((groupField:IOPFormlyFieldSettings) => - groupField.formControl?.errors && - !groupField.hide && - field.options?.parentForm?.submitted - )); + return false; } + + return !( + field.fieldGroup?.some((groupField:IOPFormlyFieldSettings) => groupField.formControl?.errors + && !groupField.hide + && field.options?.parentForm?.submitted)); } // Invalid values, ones that are not in the list of allowedValues (Array or backend fetched) do occur, e.g. // if constraints change or in case a value is undisclosed as for a project's parent. private prependCurrentValue(options:IOPAllowedValue[], currentValue:HalLink|null):IOPAllowedValue[] { - if (!currentValue?.href || options.some(option => option?._links?.self?.href === currentValue.href)) { + if (!currentValue?.href || options.some((option) => option?._links?.self?.href === currentValue.href)) { return options; - } else { - return [ - { name: currentValue.title, _links: { self: currentValue } }, - ...options - ]; } + return [ + { name: currentValue.title, _links: { self: currentValue } }, + ...options, + ]; } // So select properties that are not required always get a default ('-'/'none') option. @@ -379,14 +372,11 @@ export class DynamicFieldsService { private prependDefaultValue(options:IOPAllowedValue[], field:IOPFieldSchemaWithKey):IOPAllowedValue[] { if (field.required || this.isMultiSelectField(field)) { return options; - } else { - return [this.selectDefaultValue, ...options]; } + return [this.selectDefaultValue, ...options]; } private isMultiSelectField(field:IOPFieldSchemaWithKey) { return field?.type?.startsWith('[]'); } } - - diff --git a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.spec.ts b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.spec.ts index 39a0b0c808..493ed13acf 100644 --- a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.spec.ts +++ b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.spec.ts @@ -1,11 +1,11 @@ import { TestBed } from '@angular/core/testing'; -import { DynamicFormService } from "core-app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service"; -import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; -import { HttpClient } from "@angular/common/http"; -import { DynamicFieldsService } from "core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service"; -import { FormGroup } from "@angular/forms"; -import { of } from "rxjs"; -import { FormsService } from "core-app/core/forms/forms.service"; +import { DynamicFormService } from 'core-app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { HttpClient } from '@angular/common/http'; +import { DynamicFieldsService } from 'core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service'; +import { FormGroup } from '@angular/forms'; +import { of } from 'rxjs'; +import { FormsService } from 'core-app/core/forms/forms.service'; describe('DynamicFormService', () => { let httpClient:HttpClient; @@ -14,105 +14,105 @@ describe('DynamicFormService', () => { let formsService:FormsService; const testFormUrl = 'http://op.com/form'; const formSchema = { - "_type": "Form", - "_embedded": { - "payload": { - "name": "Project 1", - "_links": { - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project" - } - } + _type: 'Form', + _embedded: { + payload: { + name: 'Project 1', + _links: { + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', + }, + }, }, - "schema": { - "_type": "Schema", - "_dependencies": [], - "name": { - "type": "String", - "name": "Name", - "required": true, - "hasDefault": false, - "writable": true, - "minLength": 1, - "maxLength": 255, - "options": {} + schema: { + _type: 'Schema', + _dependencies: [], + name: { + type: 'String', + name: 'Name', + required: true, + hasDefault: false, + writable: true, + minLength: 1, + maxLength: 255, + options: {}, }, - "parent": { - "type": "Project", - "name": "Subproject of", - "required": false, - "hasDefault": false, - "writable": true, - "_links": { - "allowedValues": { - "href": "/api/v3/projects/available_parent_projects?of=25" - } - } + parent: { + type: 'Project', + name: 'Subproject of', + required: false, + hasDefault: false, + writable: true, + _links: { + allowedValues: { + href: '/api/v3/projects/available_parent_projects?of=25', + }, + }, }, - "_links": {} + _links: {}, }, - "validationErrors": {} + validationErrors: {}, }, - "_links": { - "self": { - "href": "/api/v3/projects/25/form", - "method": "post" + _links: { + self: { + href: '/api/v3/projects/25/form', + method: 'post', + }, + validate: { + href: '/api/v3/projects/25/form', + method: 'post', }, - "validate": { - "href": "/api/v3/projects/25/form", - "method": "post" + commit: { + href: '/api/v3/projects/25', + method: 'patch', }, - "commit": { - "href": "/api/v3/projects/25", - "method": "patch" - } - } + }, }; const dynamicFormConfig = { - "fields": [ + fields: [ { - "type": "textInput", - "key": "name", - "templateOptions": { - "required": true, - "label": "Name", - "type": "text" - } + type: 'textInput', + key: 'name', + templateOptions: { + required: true, + label: 'Name', + type: 'text', + }, }, { - "type": "selectInput", - "expressionProperties": {}, - "key": "_links.parent", - "templateOptions": { - "required": false, - "label": "Subproject of", - "type": "number", - "locale": "en", - "bindLabel": "title", - "searchable": false, - "virtualScroll": true, - "typeahead": false, - "clearOnBackspace": false, - "clearSearchOnAdd": false, - "hideSelected": false, - "text": { - "add_new_action": "Create" + type: 'selectInput', + expressionProperties: {}, + key: '_links.parent', + templateOptions: { + required: false, + label: 'Subproject of', + type: 'number', + locale: 'en', + bindLabel: 'title', + searchable: false, + virtualScroll: true, + typeahead: false, + clearOnBackspace: false, + clearSearchOnAdd: false, + hideSelected: false, + text: { + add_new_action: 'Create', }, - "options": of([]), - } - } + options: of([]), + }, + }, ], - "model": { - "name": "Project 1", - "parent": { - "href": "/api/v3/projects/26", - "title": "Parent project", - "name": "Parent project" + model: { + name: 'Project 1', + parent: { + href: '/api/v3/projects/26', + title: 'Parent project', + name: 'Parent project', }, - "_meta": undefined + _meta: undefined, }, - "form": new FormGroup({}), + form: new FormGroup({}), }; beforeEach(() => { @@ -139,10 +139,10 @@ describe('DynamicFormService', () => { it('should return the dynamic form config from the backend response', () => { dynamicFormService .getSettingsFromBackend$(testFormUrl) - .subscribe(dynamicFormConfigResponse => { + .subscribe((dynamicFormConfigResponse) => { expect(dynamicFormConfigResponse.fields.length).toEqual(dynamicFormConfig.fields.length, 'should return one dynamic field per schema field'); expect( - dynamicFormConfigResponse.fields.every((field, index) => field.type === dynamicFormConfig.fields[index].type) + dynamicFormConfigResponse.fields.every((field, index) => field.type === dynamicFormConfig.fields[index].type), ) .toBe(true, 'should return the dynamic fields in the schema order'); expect(dynamicFormConfigResponse.model).toEqual(dynamicFormConfig.model, 'should return the form model formatted'); diff --git a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.ts b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.ts index 342267ba89..b118556490 100644 --- a/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.ts +++ b/frontend/src/app/shared/components/dynamic-forms/services/dynamic-form/dynamic-form.service.ts @@ -1,19 +1,17 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { FormGroup } from "@angular/forms"; -import { FormlyForm } from "@ngx-formly/core"; -import { Observable } from "rxjs"; -import { - map, -} from "rxjs/operators"; -import { - IOPDynamicFormSettings, -} from "../../typings"; -import { DynamicFieldsService } from "core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service"; -import { FormsService } from "core-app/core/forms/forms.service"; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { FormlyForm } from '@ngx-formly/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { DynamicFieldsService } from 'core-app/shared/components/dynamic-forms/services/dynamic-fields/dynamic-fields.service'; +import { FormsService } from 'core-app/core/forms/forms.service'; +import { IOPDynamicFormSettings } from '../../typings'; + @Injectable() export class DynamicFormService { dynamicForm:FormlyForm; + formSchema:IOPFormSchema; constructor( @@ -26,22 +24,22 @@ export class DynamicFormService { this.dynamicForm = dynamicForm; } - getSettingsFromBackend$(formEndpoint?:string, resourceId?:string, payload:Object = {}):Observable{ + getSettingsFromBackend$(formEndpoint?:string, resourceId?:string, payload:Object = {}):Observable { const resourcePath = resourceId ? `/${resourceId}` : ''; const formPath = formEndpoint?.endsWith('/form') ? '' : '/form'; const url = `${formEndpoint}${resourcePath}${formPath}`; return this._httpClient .post( - url, - payload, - { - withCredentials: true, - responseType: 'json' - } - ) + url, + payload, + { + withCredentials: true, + responseType: 'json', + }, + ) .pipe( - map((formConfig => this.getSettings(formConfig))), + map(((formConfig) => this.getSettings(formConfig))), ); } @@ -63,9 +61,9 @@ export class DynamicFormService { validateForm$(form:FormGroup, resourceEndpoint:string) { return this._formsService.validateForm$(form, resourceEndpoint, this.formSchema); - }; + } - submit$(form:FormGroup, resourceEndpoint:string, resourceId?:string, formHttpMethod?: 'post' | 'patch') { + submit$(form:FormGroup, resourceEndpoint:string, resourceId?:string, formHttpMethod?:'post' | 'patch') { return this._formsService.submit$(form, resourceEndpoint, resourceId, formHttpMethod, this.formSchema); } -} \ No newline at end of file +} diff --git a/frontend/src/app/shared/components/dynamic-forms/spec/helpers.ts b/frontend/src/app/shared/components/dynamic-forms/spec/helpers.ts index ee2b96f5c9..0210297ea8 100644 --- a/frontend/src/app/shared/components/dynamic-forms/spec/helpers.ts +++ b/frontend/src/app/shared/components/dynamic-forms/spec/helpers.ts @@ -1,31 +1,31 @@ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { Component, forwardRef, ViewChild } from "@angular/core"; -import { FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from "@angular/forms"; -import { CommonModule } from "@angular/common"; -import { TextInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component"; -import { IntegerInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component"; -import { SelectInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component"; -import { SelectProjectStatusInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component"; -import { BooleanInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component"; -import { DateInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component"; -import { FormattableTextareaInputComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component"; -import { DynamicFieldGroupWrapperComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component"; -import { NgSelectModule } from "@ng-select/ng-select"; -import { NgOptionHighlightModule } from "@ng-select/ng-option-highlight"; -import { FormlyModule } from "@ngx-formly/core"; -import { IOPFormlyFieldSettings } from "core-app/shared/components/dynamic-forms/typings"; -import { FormlyForm } from "@ngx-formly/core"; -import { By } from "@angular/platform-browser"; -import { FormattableControlComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component"; -import { OpCkeditorComponent } from "core-app/shared/components/editor/components/ckeditor/op-ckeditor.component"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; -import { CKEditorSetupService } from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { OpFormFieldComponent } from "core-app/shared/components/forms/form-field/form-field.component"; - -export function createDynamicInputFixture(fields: IOPFormlyFieldSettings[], model:any, providers?:any[]): ComponentFixture { +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, forwardRef, ViewChild } from '@angular/core'; +import { FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { TextInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/text-input/text-input.component'; +import { IntegerInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/integer-input/integer-input.component'; +import { SelectInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-input/select-input.component'; +import { SelectProjectStatusInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/select-project-status-input/select-project-status-input.component'; +import { BooleanInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/boolean-input/boolean-input.component'; +import { DateInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/date-input/date-input.component'; +import { FormattableTextareaInputComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/formattable-textarea-input.component'; +import { DynamicFieldGroupWrapperComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-field-group-wrapper/dynamic-field-group-wrapper.component'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { NgOptionHighlightModule } from '@ng-select/ng-option-highlight'; +import { FormlyForm, FormlyModule } from '@ngx-formly/core'; +import { IOPFormlyFieldSettings } from 'core-app/shared/components/dynamic-forms/typings'; + +import { By } from '@angular/platform-browser'; +import { FormattableControlComponent } from 'core-app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component'; +import { OpCkeditorComponent } from 'core-app/shared/components/editor/components/ckeditor/op-ckeditor.component'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; +import { CKEditorSetupService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { OpFormFieldComponent } from 'core-app/shared/components/forms/form-field/form-field.component'; + +export function createDynamicInputFixture(fields:IOPFormlyFieldSettings[], model:any, providers?:any[]):ComponentFixture { @Component({ - template:` + template: `
    , model:any, selector:string) { - const dynamicForm: FormGroup = fixture.componentInstance.dynamicForm.form; + const dynamicForm:FormGroup = fixture.componentInstance.dynamicForm.form; const dynamicInput = fixture.debugElement.query(By.css(selector)).nativeElement; // Test ControlValueAccessor @@ -147,4 +149,4 @@ export function testDynamicInputControValueAccessor(fixture:ComponentFixture(); + @Output() public onEmptySubmit = new EventEmitter(); @ViewChild('editableTitleInput') inputField?:ElementRef; public selectedTitle:string; + public selectableTitleIdentifier = selectableTitleIdentifier; @InjectField() protected readonly elementRef:ElementRef; + @InjectField() I18n!:I18nService; public text = { @@ -77,14 +85,14 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { input_placeholder: this.I18n.t('js.work_packages.query.rename_query_placeholder'), search_query_title: this.I18n.t('js.toolbar.search_query_title'), confirm_edit_cancel: this.I18n.t('js.work_packages.query.confirm_edit_cancel'), - duplicate_query_title: this.I18n.t('js.work_packages.query.errors.duplicate_query_title') + duplicate_query_title: this.I18n.t('js.work_packages.query.errors.duplicate_query_title'), }; constructor(readonly injector:Injector) { } ngOnInit() { - this.text['input_title'] = `${this.text.click_to_edit} ${this.text.press_enter_to_save}`; + this.text.input_title = `${this.text.click_to_edit} ${this.text.press_enter_to_save}`; jQuery(this.elementRef.nativeElement).on(triggerEditingEvent, (evt:Event, val = '') => { // In case we're not editable, ignore request @@ -103,16 +111,14 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { } ngOnChanges(changes:SimpleChanges):void { - if (changes.inputTitle) { this.selectedTitle = changes.inputTitle.currentValue; } if (changes.initialFocus && changes.initialFocus.firstChange && this.inputField!) { - const field:HTMLInputElement = this.inputField!.nativeElement; + const field:HTMLInputElement = this.inputField.nativeElement; this.selectInputOnInitalFocus(field); } - } public onFocus(event:FocusEvent) { diff --git a/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts b/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts index d8b1f1d740..24e1816fa2 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,63 +26,73 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { + Component, ElementRef, OnInit, ViewChild, +} from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { States } from 'core-app/core/states/states.service'; import { filter, takeUntil } from 'rxjs/operators'; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ICKEditorContext, ICKEditorInstance, ICKEditorType, -} from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { OpCkeditorComponent } from "core-app/shared/components/editor/components/ckeditor/op-ckeditor.component"; -import { componentDestroyed } from "@w11k/ngx-componentdestroyed"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; - +} from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { OpCkeditorComponent } from 'core-app/shared/components/editor/components/ckeditor/op-ckeditor.component'; +import { componentDestroyed } from '@w11k/ngx-componentdestroyed'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; export const ckeditorAugmentedTextareaSelector = 'ckeditor-augmented-textarea'; @Component({ selector: ckeditorAugmentedTextareaSelector, - templateUrl: './ckeditor-augmented-textarea.html' + templateUrl: './ckeditor-augmented-textarea.html', }) export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin implements OnInit { public textareaSelector:string; + public previewContext:string; // Which template to include public $element:JQuery; + public formElement:JQuery; + public wrappedTextArea:JQuery; + public $attachmentsElement:JQuery; // Remember if the user changed public changed = false; + public inFlight = false; public initialContent:string; + public resource?:HalResource; + public context:ICKEditorContext; + public macros:boolean; // Reference to the actual ckeditor instance component @ViewChild(OpCkeditorComponent, { static: true }) private ckEditorInstance:OpCkeditorComponent; private attachments:HalResource[]; + private isEditing = false; constructor(protected elementRef:ElementRef, - protected pathHelper:PathHelperService, - protected halResourceService:HalResourceService, - protected Notifications:NotificationsService, - protected I18n:I18nService, - protected states:States, - protected ConfigurationService:ConfigurationService) { + protected pathHelper:PathHelperService, + protected halResourceService:HalResourceService, + protected Notifications:NotificationsService, + protected I18n:I18nService, + protected states:States, + protected ConfigurationService:ConfigurationService) { super(); } @@ -110,10 +120,10 @@ export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin impl this.context = { type: editorType, resource: this.resource, - previewContext: this.previewContext + previewContext: this.previewContext, }; if (!this.macros) { - this.context['macros'] = 'none'; + this.context.macros = 'none'; } } @@ -161,7 +171,7 @@ export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin impl private setupAttachmentAddedCallback(editor:ICKEditorInstance) { editor.model.on('op:attachment-added', () => { - this.states.forResource(this.resource!)!.putValue(this.resource!); + this.states.forResource(this.resource!)!.putValue(this.resource); }); } @@ -171,20 +181,20 @@ export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin impl this.states.forResource(this.resource!)!.changes$() .pipe( takeUntil(componentDestroyed(this)), - filter(resource => !!resource) - ).subscribe(resource => { - const missingAttachments = _.differenceBy(this.attachments, - resource!.attachments.elements, - (attachment:HalResource) => attachment.id); + filter((resource) => !!resource), + ).subscribe((resource) => { + const missingAttachments = _.differenceBy(this.attachments, + resource!.attachments.elements, + (attachment:HalResource) => attachment.id); - const removedUrls = missingAttachments.map(attachment => attachment.downloadLocation.href); + const removedUrls = missingAttachments.map((attachment) => attachment.downloadLocation.href); - if (removedUrls.length) { - editor.model.fire('op:attachment-removed', removedUrls); - } + if (removedUrls.length) { + editor.model.fire('op:attachment-removed', removedUrls); + } - this.attachments = _.clone(resource!.attachments.elements); - }); + this.attachments = _.clone(resource!.attachments.elements); + }); } private setLabel() { @@ -206,14 +216,13 @@ export class CkeditorAugmentedTextareaComponent extends UntilDestroyedMixin impl return; } - const takenIds = this.$attachmentsElement.find('input[type=\'file\']').map((index, input) => { - const match = (input.getAttribute('name') || '').match(/attachments\[(\d+)\]\[(?:file|id)\]/); + const takenIds = this.$attachmentsElement.find("input[type='file']").map((index, input) => { + const match = /attachments\[(\d+)\]\[(?:file|id)\]/.exec((input.getAttribute('name') || '')); if (match) { return parseInt(match[1]); - } else { - return 0; } + return 0; }); const maxValue:number = takenIds.toArray().sort().pop() || 0; diff --git a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts index 2358442dba..e7671ce59d 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-preview.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,16 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector } from "@angular/core"; -import { DynamicBootstrapper } from "core-app/core/setup/globals/dynamic-bootstrapper"; +import { + ApplicationRef, ComponentFactoryResolver, ComponentRef, Injectable, Injector, +} from '@angular/core'; +import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; @Injectable() export class CKEditorPreviewService { - constructor(private readonly componentFactoryResolver:ComponentFactoryResolver, - private readonly appRef:ApplicationRef, - private readonly injector:Injector) { + private readonly appRef:ApplicationRef, + private readonly injector:Injector) { } /** @@ -65,7 +66,7 @@ export class CKEditorPreviewService { }); return () => { - refs.forEach(ref => { + refs.forEach((ref) => { this.appRef.detachView(ref.hostView); ref.destroy(); }); diff --git a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts index 18bfd32c80..71ba21b10b 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor/ckeditor-setup.service.ts @@ -1,6 +1,6 @@ -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { Injectable } from "@angular/core"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { Injectable } from '@angular/core'; export interface ICKEditorInstance { getData(options:{ trim:boolean }):string; @@ -68,23 +68,22 @@ export class CKEditorSetupService { // Load the bundle await this.load(); - const type = context.type; + const { type } = context; const editorClass = type === 'constrained' ? window.OPConstrainedEditor : window.OPClassicEditor; wrapper.classList.add(`ckeditor-type-${type}`); const toolbarWrapper = wrapper.querySelector('.document-editor__toolbar') as HTMLElement; const contentWrapper = wrapper.querySelector('.document-editor__editable') as HTMLElement; - var contentLanguage = context.options && context.options.rtl ? 'ar' : 'en'; - + const contentLanguage = context.options && context.options.rtl ? 'ar' : 'en'; const editor:ICKEditorInstance = await editorClass .createCustomized(contentWrapper, { openProject: this.createConfig(context), - initialData: initialData, + initialData, language: { - content: contentLanguage - } + content: contentLanguage, + }, }); toolbarWrapper.appendChild(editor.ui.view.toolbar.element); @@ -114,16 +113,16 @@ export class CKEditorSetupService { context.macros = [ 'OPMacroToc', 'OPMacroEmbeddedTable', - 'OPMacroWpButton' + 'OPMacroWpButton', ]; } else { context.macros = context.macros; } return { - context: context, + context, helpURL: this.PathHelper.textFormattingHelp(), - pluginContext: window.OpenProject.pluginContext.value + pluginContext: window.OpenProject.pluginContext.value, }; } } diff --git a/frontend/src/app/shared/components/editor/components/ckeditor/op-ckeditor.component.ts b/frontend/src/app/shared/components/editor/components/ckeditor/op-ckeditor.component.ts index 579773d21b..9ef3d7437c 100644 --- a/frontend/src/app/shared/components/editor/components/ckeditor/op-ckeditor.component.ts +++ b/frontend/src/app/shared/components/editor/components/ckeditor/op-ckeditor.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,15 +26,17 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { + Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild, +} from '@angular/core'; import { CKEditorSetupService, ICKEditorContext, - ICKEditorInstance -} from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { ConfigurationService } from "core-app/core/config/configuration.service"; + ICKEditorInstance, +} from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { ConfigurationService } from 'core-app/core/config/configuration.service'; declare module 'codemirror'; @@ -43,16 +45,17 @@ const manualModeLocalStorageKey = 'op-ckeditor-uses-manual-mode'; @Component({ selector: 'op-ckeditor', templateUrl: './op-ckeditor.html', - styleUrls: ['./op-ckeditor.sass'] + styleUrls: ['./op-ckeditor.sass'], }) export class OpCkeditorComponent implements OnInit { @Input() context:ICKEditorContext; + @Input() public set content(newVal:string) { this._content = newVal; if (this.initialized) { - this.ckEditorInstance!.setData(newVal); + this.ckEditorInstance.setData(newVal); } } @@ -67,17 +70,22 @@ export class OpCkeditorComponent implements OnInit { // View container of the replacement used to initialize CKEditor5 @ViewChild('opCkeditorReplacementContainer', { static: true }) opCkeditorReplacementContainer:ElementRef; + @ViewChild('codeMirrorPane') codeMirrorPane:ElementRef; // CKEditor instance once initialized public ckEditorInstance:ICKEditorInstance; + public error:string|null = null; + public allowManualMode = false; + public manualMode = false; + private _content:string; public text = { - errorTitle: this.I18n.t('js.editor.error_initialization_failed') + errorTitle: this.I18n.t('js.editor.error_initialization_failed'), }; // Codemirror instance, initialized lazily when running source mode @@ -88,21 +96,21 @@ export class OpCkeditorComponent implements OnInit { private debouncedEmitter = _.debounce( () => { this.getTransformedContent(false) - .then(val => { + .then((val) => { this.onContentChange.emit(val); }); }, 1000, - { leading: true } + { leading: true }, ); private $element:JQuery; constructor(private readonly elementRef:ElementRef, - private readonly Notifications:NotificationsService, - private readonly I18n:I18nService, - private readonly configurationService:ConfigurationService, - private readonly ckEditorSetup:CKEditorSetupService) { + private readonly Notifications:NotificationsService, + private readonly I18n:I18nService, + private readonly configurationService:ConfigurationService, + private readonly ckEditorSetup:CKEditorSetupService) { } /** @@ -111,10 +119,9 @@ export class OpCkeditorComponent implements OnInit { */ public getRawData():string { if (this.manualMode) { - return this._content = this.codeMirrorInstance!.getValue(); - } else { - return this._content = this.ckEditorInstance!.getData({ trim: false }); + return this._content = this.codeMirrorInstance.getValue(); } + return this._content = this.ckEditorInstance.getData({ trim: false }); } /** @@ -123,7 +130,7 @@ export class OpCkeditorComponent implements OnInit { */ public getTransformedContent(notificationOnError = true):Promise { if (!this.initialized) { - throw "Tried to access CKEditor instance before initialization."; + throw new Error('Tried to access CKEditor instance before initialization.'); } return new Promise((resolve, reject) => { @@ -133,7 +140,7 @@ export class OpCkeditorComponent implements OnInit { console.error(`Failed to save CKEditor content: ${e}.`); const error = this.I18n.t( 'js.editor.error_saving_failed', - { error: e || this.I18n.t('js.error.internal') } + { error: e || this.I18n.t('js.error.internal') }, ); if (notificationOnError) { @@ -176,10 +183,10 @@ export class OpCkeditorComponent implements OnInit { .create( this.opCkeditorReplacementContainer.nativeElement, this.context, - this.content + this.content, ) .catch((error:string) => { - throw(error); + throw (error); }) .then((editor:ICKEditorInstance) => { this.ckEditorInstance = editor; @@ -198,7 +205,6 @@ export class OpCkeditorComponent implements OnInit { this.$element.data('editor', editorPromise); } - /** * Disable the manual mode, kill the codeMirror instance and switch back to CKEditor */ @@ -221,7 +227,7 @@ export class OpCkeditorComponent implements OnInit { Promise .all([ import('codemirror'), - import(/* webpackChunkName: "codemirror-mode" */ `codemirror/mode/${cmMode}/${cmMode}.js`) + import(/* webpackChunkName: "codemirror-mode" */ `codemirror/mode/${cmMode}/${cmMode}.js`), ]) .then((imported:any[]) => { const CodeMirror = imported[0].default; @@ -231,8 +237,8 @@ export class OpCkeditorComponent implements OnInit { lineNumbers: true, smartIndent: true, value: current, - mode: '' - } + mode: '', + }, ); this.codeMirrorInstance.on('change', this.debouncedEmitter); diff --git a/frontend/src/app/shared/components/editor/openproject-editor.module.ts b/frontend/src/app/shared/components/editor/openproject-editor.module.ts index 84f2461255..52aa4a30df 100644 --- a/frontend/src/app/shared/components/editor/openproject-editor.module.ts +++ b/frontend/src/app/shared/components/editor/openproject-editor.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,17 +28,17 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { CommonModule } from "@angular/common"; +import { CommonModule } from '@angular/common'; import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; import { CkeditorAugmentedTextareaComponent } from 'core-app/shared/components/editor/components/ckeditor-augmented-textarea/ckeditor-augmented-textarea.component'; import { OpCkeditorComponent } from 'core-app/shared/components/editor/components/ckeditor/op-ckeditor.component'; import { CKEditorSetupService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; import { CKEditorPreviewService } from 'core-app/shared/components/editor/components/ckeditor/ckeditor-preview.service'; -import { EditorMacrosService } from "core-app/shared/components/modals/editor/editor-macros.service"; -import { WikiIncludePageMacroModal } from "core-app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal"; -import { ChildPagesMacroModal } from "core-app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal"; -import { CodeBlockMacroModal } from "core-app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal"; +import { EditorMacrosService } from 'core-app/shared/components/modals/editor/editor-macros.service'; +import { WikiIncludePageMacroModalComponent } from 'core-app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal'; +import { ChildPagesMacroModalComponent } from 'core-app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal'; +import { CodeBlockMacroModalComponent } from 'core-app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal'; @NgModule({ imports: [ @@ -61,10 +61,10 @@ import { CodeBlockMacroModal } from "core-app/shared/components/modals/editor/ma // CKEditor and Macros CkeditorAugmentedTextareaComponent, OpCkeditorComponent, - WikiIncludePageMacroModal, - CodeBlockMacroModal, - ChildPagesMacroModal, - ] + WikiIncludePageMacroModalComponent, + CodeBlockMacroModalComponent, + ChildPagesMacroModalComponent, + ], }) export class OpenprojectEditorModule { } diff --git a/frontend/src/app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component.ts b/frontend/src/app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component.ts index 2dcb1a16f6..1044ad94f6 100644 --- a/frontend/src/app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component.ts +++ b/frontend/src/app/shared/components/enterprise-banner/enterprise-banner-bootstrap.component.ts @@ -1,5 +1,5 @@ -import { Component, ElementRef, OnInit } from "@angular/core"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { Component, ElementRef, OnInit } from '@angular/core'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; export const enterpriseBannerSelector = 'enterprise-banner-bootstrap'; @@ -11,15 +11,17 @@ export const enterpriseBannerSelector = 'enterprise-banner-bootstrap'; [linkMessage]="linkMessage" [opReferrer]="referrer"> - ` + `, }) export class EnterpriseBannerBootstrapComponent implements OnInit { public textMessage:string; + public linkMessage:string; + public referrer:string; constructor(protected elementRef:ElementRef, - protected i18n:I18nService) { + protected i18n:I18nService) { } ngOnInit() { diff --git a/frontend/src/app/shared/components/enterprise-banner/enterprise-banner.component.ts b/frontend/src/app/shared/components/enterprise-banner/enterprise-banner.component.ts index 3642ecc365..9110938747 100644 --- a/frontend/src/app/shared/components/enterprise-banner/enterprise-banner.component.ts +++ b/frontend/src/app/shared/components/enterprise-banner/enterprise-banner.component.ts @@ -1,6 +1,6 @@ -import {Component, Input} from "@angular/core"; -import {BannersService} from "core-app/core/enterprise/banners.service"; -import {I18nService} from "core-app/core/i18n/i18n.service"; +import { Component, Input } from '@angular/core'; +import { BannersService } from 'core-app/core/enterprise/banners.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ selector: 'enterprise-banner', @@ -16,12 +16,15 @@ import {I18nService} from "core-app/core/i18n/i18n.service"; [textContent]="linkMessage">
    - ` + `, }) export class EnterpriseBannerComponent { - @Input() public leftMargin:boolean = false; + @Input() public leftMargin = false; + @Input() public textMessage:string; + @Input() public linkMessage:string; + @Input() public opReferrer:string; public text:any = { diff --git a/frontend/src/app/shared/components/fields/changeset/changeset.ts b/frontend/src/app/shared/components/fields/changeset/changeset.ts index df54dfbf8c..b831febafe 100644 --- a/frontend/src/app/shared/components/fields/changeset/changeset.ts +++ b/frontend/src/app/shared/components/fields/changeset/changeset.ts @@ -51,7 +51,7 @@ export class Changeset { public set(key:string, value:unknown, pristineValue:unknown):void { this.changes[key] = { from: pristineValue, - to: value + to: value, }; } diff --git a/frontend/src/app/shared/components/fields/changeset/resource-changeset.ts b/frontend/src/app/shared/components/fields/changeset/resource-changeset.ts index c81e42bd3a..76e78a5fdf 100644 --- a/frontend/src/app/shared/components/fields/changeset/resource-changeset.ts +++ b/frontend/src/app/shared/components/fields/changeset/resource-changeset.ts @@ -1,14 +1,13 @@ -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ChangeItem, ChangeMap, Changeset } from "core-app/shared/components/fields/changeset/changeset"; -import { input, InputState } from "reactivestates"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { take } from "rxjs/operators"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { Injector } from '@angular/core'; -import { SchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ChangeItem, ChangeMap, Changeset } from 'core-app/shared/components/fields/changeset/changeset'; +import { input, InputState } from 'reactivestates'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { take } from 'rxjs/operators'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { SchemaProxy } from 'core-app/features/hal/schemas/schema-proxy'; export const PROXY_IDENTIFIER = '__is_changeset_proxy'; @@ -47,11 +46,11 @@ export class ResourceChangeset { protected schemaCache:SchemaCacheService; constructor(pristineResource:T, - public readonly state?:InputState>, - loadedForm:FormResource|null = null) { + public readonly state?:InputState>, + loadedForm:FormResource|null = null) { this.updatePristineResource(pristineResource); - this.schemaCache = (pristineResource.injector as Injector).get(SchemaCacheService); + this.schemaCache = (pristineResource.injector).get(SchemaCacheService); if (loadedForm) { this.form$.putValue(loadedForm); @@ -85,7 +84,7 @@ export class ResourceChangeset { public updatePristineResource(resource:T) { // Ensure we're not passing in a proxy if ((resource as any)[PROXY_IDENTIFIER]) { - throw "You're trying to pass proxy object as a pristine resource. This will cause errors"; + throw new Error("You're trying to pass proxy object as a pristine resource. This will cause errors"); } this._pristineResource = resource; @@ -97,7 +96,7 @@ export class ResourceChangeset { this.setValue(key, val); return true; }, - } + }, ); } @@ -310,9 +309,8 @@ export class ResourceChangeset { public get schema():SchemaResource { if (this.form$.hasValue()) { return SchemaProxy.create(this.form$.value!.schema, this.projectedResource); - } else { - return this.schemaCache.of(this.pristineResource); } + return this.schemaCache.of(this.pristineResource); } /** @@ -383,14 +381,11 @@ export class ResourceChangeset { // They will already be created on the server but now // we need to claim them for the newly created work package. if (this.pristineResource.attachments) { - payload['_links']['attachments'] = this.pristineResource + payload._links.attachments = this.pristineResource .attachments .elements - .map((a:HalResource) => { - return { href: a.href }; - }); + .map((a:HalResource) => ({ href: a.href })); } - } else { // Otherwise, simply use the bare minimum payload = this.minimalPayload; @@ -432,9 +427,8 @@ export class ResourceChangeset { } return links; - } else { - return { href: _.get(val, 'href', null) }; } + return { href: _.get(val, 'href', null) }; } /** @@ -457,7 +451,7 @@ export class ResourceChangeset { */ protected setNewDefaultFor(key:string, val:unknown) { if (!this.valueExists(key)) { - debugLog("Taking over default value from form for " + key); + debugLog(`Taking over default value from form for ${key}`); this.setValue(key, val); } } diff --git a/frontend/src/app/shared/components/fields/display/display-field-renderer.ts b/frontend/src/app/shared/components/fields/display/display-field-renderer.ts index 69e0739403..ec6f238110 100644 --- a/frontend/src/app/shared/components/fields/display/display-field-renderer.ts +++ b/frontend/src/app/shared/components/fields/display/display-field-renderer.ts @@ -1,19 +1,21 @@ import { Injector } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { DisplayFieldContext, DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { MultipleLinesCustomOptionsDisplayField } from "core-app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module"; -import { ProgressTextDisplayField } from "core-app/shared/components/fields/display/field-types/progress-text-display-field.module"; -import { MultipleLinesUserFieldModule } from "core-app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { ISchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { DateDisplayField } from "core-app/shared/components/fields/display/field-types/date-display-field.module"; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { + DisplayFieldContext, + DisplayFieldService, +} from 'core-app/shared/components/fields/display/display-field.service'; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { MultipleLinesCustomOptionsDisplayField } from 'core-app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module'; +import { ProgressTextDisplayField } from 'core-app/shared/components/fields/display/field-types/progress-text-display-field.module'; +import { MultipleLinesUserFieldModule } from 'core-app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { DateDisplayField } from 'core-app/shared/components/fields/display/field-types/date-display-field.module'; export const editableClassName = '-editable'; export const requiredClassName = '-required'; @@ -24,25 +26,26 @@ export const editFieldContainerClass = 'inline-edit--container'; export const cellEmptyPlaceholder = '-'; export class DisplayFieldRenderer { - @InjectField() displayFieldService:DisplayFieldService; + @InjectField() schemaCache:SchemaCacheService; + @InjectField() halEditing:HalResourceEditingService; + @InjectField() I18n!:I18nService; /** We cache the previously used fields to avoid reinitialization */ private fieldCache:{ [key:string]:DisplayField } = {}; constructor(public readonly injector:Injector, - public readonly container:'table'|'single-view'|'timeline', - public readonly options:{ [key:string]:any } = {}) { + public readonly container:'table'|'single-view'|'timeline', + public readonly options:{ [key:string]:any } = {}) { } public render(resource:T, name:string, change:ResourceChangeset|null, placeholder?:string):HTMLSpanElement { - const [field, span] = this.renderFieldValue(resource, name, change, placeholder); if (field === null) { @@ -72,7 +75,7 @@ export class DisplayFieldRenderer { const field = this.getField(resource, fieldSchema, attributeName, change); field.render(span, this.getText(field, fieldSchema, placeholder), fieldSchema.options); - const title = field.title; + const { title } = field; if (title) { span.setAttribute('title', title); } @@ -127,9 +130,8 @@ export class DisplayFieldRenderer { private getText(field:DisplayField, fieldSchema:IFieldSchema, placeholder?:string):string { if (field.isEmpty()) { return placeholder || this.getDefaultPlaceholder(fieldSchema); - } else { - return field.valueString; } + return field.valueString; } private setSpanAttributes(span:HTMLElement, field:DisplayField, name:string, resource:T, change:ResourceChangeset|null):void { @@ -173,27 +175,24 @@ export class DisplayFieldRenderer { try { titleContent = _.escape(jQuery(`
    ${labelContent}
    `).text()); } catch (e) { - console.error("Failed to parse formattable labelContent"); - titleContent = "Label for " + field.displayName; + console.error('Failed to parse formattable labelContent'); + titleContent = `Label for ${field.displayName}`; } - } else { titleContent = labelContent; } if (field.writable && schema.isAttributeEditable(field.name)) { return this.I18n.t('js.inplace.button_edit', { attribute: `${field.displayName} ${titleContent}` }); - } else { - return `${field.displayName} ${titleContent}`; } + return `${field.displayName} ${titleContent}`; } private getLabelContent(field:DisplayField):string { if (field.isEmpty()) { return this.I18n.t('js.inplace.null_value_label'); - } else { - return field.valueString; } + return field.valueString; } /** @@ -206,9 +205,8 @@ export class DisplayFieldRenderer { private attributeName(attribute:string, schema:SchemaResource) { if (schema.mappedName) { return schema.mappedName(attribute); - } else { - return attribute; } + return attribute; } private getDefaultPlaceholder(fieldSchema:IFieldSchema):string { @@ -222,10 +220,9 @@ export class DisplayFieldRenderer { private schema(resource:T, change:ResourceChangeset|null) { if (change) { return change.schema; - } else if (this.halEditing.typedState(resource).hasValue()) { + } if (this.halEditing.typedState(resource).hasValue()) { return this.halEditing.typedState(resource).value!.schema; - } else { - return this.schemaCache.of(resource) as ISchemaProxy; } + return this.schemaCache.of(resource); } } diff --git a/frontend/src/app/shared/components/fields/display/display-field.component.ts b/frontend/src/app/shared/components/fields/display/display-field.component.ts index cf65666605..4a27dfffde 100644 --- a/frontend/src/app/shared/components/fields/display/display-field.component.ts +++ b/frontend/src/app/shared/components/fields/display/display-field.component.ts @@ -1,35 +1,40 @@ -import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, ViewChild } from '@angular/core'; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { Constructor } from "@angular/cdk/table"; -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { + ChangeDetectionStrategy, Component, ElementRef, Injector, Input, OnInit, ViewChild, +} from '@angular/core'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { Constructor } from '@angular/cdk/table'; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; @Component({ selector: 'display-field', template: '', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DisplayFieldComponent implements OnInit { @Input() resource:HalResource; + @Input() fieldName:string; + @Input() displayClass?:Constructor; @Input() containerType:'table'|'single-view'|'timeline' = 'table'; - @Input() displayFieldOptions:{[key:string]:unknown} = {}; + + @Input() displayFieldOptions:{ [key:string]:unknown } = {}; @ViewChild('displayFieldContainer') container:ElementRef; constructor(private injector:Injector, - private displayFieldService:DisplayFieldService, - private schemaCache:SchemaCacheService) { + private displayFieldService:DisplayFieldService, + private schemaCache:SchemaCacheService) { } ngOnInit() { this.schemaCache .ensureLoaded(this.resource) - .then(schema => { + .then((schema) => { this.render(schema[this.fieldName]); }); } @@ -59,7 +64,7 @@ export class DisplayFieldComponent implements OnInit { this.resource, this.fieldName, fieldSchema, - this.displayFieldContext + this.displayFieldContext, ); } diff --git a/frontend/src/app/shared/components/fields/display/display-field.initializer.ts b/frontend/src/app/shared/components/fields/display/display-field.initializer.ts index de24650b87..e725372395 100644 --- a/frontend/src/app/shared/components/fields/display/display-field.initializer.ts +++ b/frontend/src/app/shared/components/fields/display/display-field.initializer.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,30 +26,30 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { TextDisplayField } from "core-app/shared/components/fields/display/field-types/text-display-field.module"; -import { FloatDisplayField } from "core-app/shared/components/fields/display/field-types/float-display-field.module"; -import { IntegerDisplayField } from "core-app/shared/components/fields/display/field-types/integer-display-field.module"; -import { ResourceDisplayField } from "core-app/shared/components/fields/display/field-types/resource-display-field.module"; -import { ResourcesDisplayField } from "core-app/shared/components/fields/display/field-types/resources-display-field.module"; -import { FormattableDisplayField } from "core-app/shared/components/fields/display/field-types/formattable-display-field.module"; -import { DurationDisplayField } from "core-app/shared/components/fields/display/field-types/duration-display-field.module"; -import { DateDisplayField } from "core-app/shared/components/fields/display/field-types/date-display-field.module"; -import { DateTimeDisplayField } from "core-app/shared/components/fields/display/field-types/datetime-display-field.module"; -import { BooleanDisplayField } from "core-app/shared/components/fields/display/field-types/boolean-display-field.module"; -import { ProgressDisplayField } from "core-app/shared/components/fields/display/field-types/progress-display-field.module"; -import { WorkPackageDisplayField } from "core-app/shared/components/fields/display/field-types/work-package-display-field.module"; -import { WorkPackageSpentTimeDisplayField } from "core-app/shared/components/fields/display/field-types/wp-spent-time-display-field.module"; -import { IdDisplayField } from "core-app/shared/components/fields/display/field-types/id-display-field.module"; -import { HighlightedResourceDisplayField } from "core-app/shared/components/fields/display/field-types/highlighted-resource-display-field.module"; -import { TypeDisplayField } from "core-app/shared/components/fields/display/field-types/type-display-field.module"; -import { UserDisplayField } from "core-app/shared/components/fields/display/field-types/user-display-field.module"; -import { MultipleUserFieldModule } from "core-app/shared/components/fields/display/field-types/multiple-user-display-field.module"; -import { WorkPackageIdDisplayField } from "core-app/shared/components/fields/display/field-types/wp-id-display-field.module"; -import { ProjectStatusDisplayField } from "core-app/shared/components/fields/display/field-types/project-status-display-field.module"; -import { PlainFormattableDisplayField } from "core-app/shared/components/fields/display/field-types/plain-formattable-display-field.module"; -import { LinkedWorkPackageDisplayField } from "core-app/shared/components/fields/display/field-types/linked-work-package-display-field.module"; -import { CombinedDateDisplayField } from "core-app/shared/components/fields/display/field-types/combined-date-display.field"; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { TextDisplayField } from 'core-app/shared/components/fields/display/field-types/text-display-field.module'; +import { FloatDisplayField } from 'core-app/shared/components/fields/display/field-types/float-display-field.module'; +import { IntegerDisplayField } from 'core-app/shared/components/fields/display/field-types/integer-display-field.module'; +import { ResourceDisplayField } from 'core-app/shared/components/fields/display/field-types/resource-display-field.module'; +import { ResourcesDisplayField } from 'core-app/shared/components/fields/display/field-types/resources-display-field.module'; +import { FormattableDisplayField } from 'core-app/shared/components/fields/display/field-types/formattable-display-field.module'; +import { DurationDisplayField } from 'core-app/shared/components/fields/display/field-types/duration-display-field.module'; +import { DateDisplayField } from 'core-app/shared/components/fields/display/field-types/date-display-field.module'; +import { DateTimeDisplayField } from 'core-app/shared/components/fields/display/field-types/datetime-display-field.module'; +import { BooleanDisplayField } from 'core-app/shared/components/fields/display/field-types/boolean-display-field.module'; +import { ProgressDisplayField } from 'core-app/shared/components/fields/display/field-types/progress-display-field.module'; +import { WorkPackageDisplayField } from 'core-app/shared/components/fields/display/field-types/work-package-display-field.module'; +import { WorkPackageSpentTimeDisplayField } from 'core-app/shared/components/fields/display/field-types/wp-spent-time-display-field.module'; +import { IdDisplayField } from 'core-app/shared/components/fields/display/field-types/id-display-field.module'; +import { HighlightedResourceDisplayField } from 'core-app/shared/components/fields/display/field-types/highlighted-resource-display-field.module'; +import { TypeDisplayField } from 'core-app/shared/components/fields/display/field-types/type-display-field.module'; +import { UserDisplayField } from 'core-app/shared/components/fields/display/field-types/user-display-field.module'; +import { MultipleUserFieldModule } from 'core-app/shared/components/fields/display/field-types/multiple-user-display-field.module'; +import { WorkPackageIdDisplayField } from 'core-app/shared/components/fields/display/field-types/wp-id-display-field.module'; +import { ProjectStatusDisplayField } from 'core-app/shared/components/fields/display/field-types/project-status-display-field.module'; +import { PlainFormattableDisplayField } from 'core-app/shared/components/fields/display/field-types/plain-formattable-display-field.module'; +import { LinkedWorkPackageDisplayField } from 'core-app/shared/components/fields/display/field-types/linked-work-package-display-field.module'; +import { CombinedDateDisplayField } from 'core-app/shared/components/fields/display/field-types/combined-date-display.field'; export function initializeCoreDisplayFields(displayFieldService:DisplayFieldService) { return () => { @@ -60,7 +60,7 @@ export function initializeCoreDisplayFields(displayFieldService:DisplayFieldServ .addFieldType(IntegerDisplayField, 'integer', ['Integer']) .addFieldType(HighlightedResourceDisplayField, 'highlight', [ 'Status', - 'Priority' + 'Priority', ]) .addFieldType(TypeDisplayField, 'type', ['Type']) .addFieldType(ResourceDisplayField, 'resource', [ diff --git a/frontend/src/app/shared/components/fields/display/display-field.module.ts b/frontend/src/app/shared/components/fields/display/display-field.module.ts index 3a437d0588..a4748c6d3f 100644 --- a/frontend/src/app/shared/components/fields/display/display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,20 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Field, IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { DisplayFieldContext } from "core-app/shared/components/fields/display/display-field.service"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { Field, IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { DisplayFieldContext } from 'core-app/shared/components/fields/display/display-field.service'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export const cssClassCustomOption = 'custom-option'; export class DisplayField extends Field { public static type:string; + public mode:string | null = null; + public activeChange:ResourceChangeset|null = null; @InjectField() I18n!:I18nService; @@ -58,7 +60,7 @@ export class DisplayField extends Field { public texts = { empty: this.I18n.t('js.label_no_value'), - placeholder: this.I18n.t('js.placeholders.default') + placeholder: this.I18n.t('js.placeholders.default'), }; public get isFormattable():boolean { @@ -81,9 +83,8 @@ export class DisplayField extends Field { if (this.activeChange) { return this.activeChange.projectedResource[this.name]; - } else { - return this.attribute; } + return this.attribute; } protected get attribute() { @@ -107,7 +108,6 @@ export class DisplayField extends Field { } public get title():string|null { - // Don't return a value for long text fields, // since they shouldn't / won't be truncated. if (this.isFormattable) { diff --git a/frontend/src/app/shared/components/fields/display/display-field.service.ts b/frontend/src/app/shared/components/fields/display/display-field.service.ts index a35111f068..5f334cfb40 100644 --- a/frontend/src/app/shared/components/fields/display/display-field.service.ts +++ b/frontend/src/app/shared/components/fields/display/display-field.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import { Injectable, Injector } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { AbstractFieldService, IFieldType } from "core-app/shared/components/fields/field.service"; -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { AbstractFieldService, IFieldType } from 'core-app/shared/components/fields/field.service'; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; export interface IDisplayFieldType extends IFieldType { new(resource:HalResource, attributeType:string, schema:IFieldSchema, context:DisplayFieldContext):DisplayField; @@ -49,7 +49,6 @@ export interface DisplayFieldContext { @Injectable({ providedIn: 'root' }) export class DisplayFieldService extends AbstractFieldService { - /** * Create an instance of the field type T given the required arguments * with either in descending order: diff --git a/frontend/src/app/shared/components/fields/display/field-types/boolean-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/boolean-display-field.module.ts index 959b5ce7fc..b7ef932b06 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/boolean-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/boolean-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class BooleanDisplayField extends DisplayField { - public get valueString() { return this.translatedValue(); } @@ -41,9 +40,8 @@ export class BooleanDisplayField extends DisplayField { public translatedValue() { if (this.value) { return this.I18n.t('js.general_text_yes'); - } else { - return this.I18n.t('js.general_text_no'); } + return this.I18n.t('js.general_text_no'); } public isEmpty():boolean { diff --git a/frontend/src/app/shared/components/fields/display/field-types/combined-date-display.field.ts b/frontend/src/app/shared/components/fields/display/field-types/combined-date-display.field.ts index 30fbfac499..bc9751dec4 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/combined-date-display.field.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/combined-date-display.field.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,13 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DateDisplayField } from "core-app/shared/components/fields/display/field-types/date-display-field.module"; +import { DateDisplayField } from 'core-app/shared/components/fields/display/field-types/date-display-field.module'; export class CombinedDateDisplayField extends DateDisplayField { text = { placeholder: { startDate: this.I18n.t('js.label_no_start_date'), - dueDate: this.I18n.t('js.label_no_due_date') + dueDate: this.I18n.t('js.label_no_due_date'), }, }; @@ -53,9 +53,9 @@ export class CombinedDateDisplayField extends DateDisplayField { private createDateDisplayField(date:'dueDate'|'startDate'):HTMLElement { const dateElement = document.createElement('span'); const dateDisplayField = new DateDisplayField(date, this.context); - const text = this.resource[date] ? - this.timezoneService.formattedDate(this.resource[date]) : - this.text.placeholder[date]; + const text = this.resource[date] + ? this.timezoneService.formattedDate(this.resource[date]) + : this.text.placeholder[date]; dateDisplayField.apply(this.resource, this.schema); dateDisplayField.render(dateElement, text); diff --git a/frontend/src/app/shared/components/fields/display/field-types/date-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/date-display-field.module.ts index 63e70d0e5c..4f15b3d7d7 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/date-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/date-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,15 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { HighlightableDisplayField } from "core-app/shared/components/fields/display/field-types/highlightable-display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { HighlightableDisplayField } from 'core-app/shared/components/fields/display/field-types/highlightable-display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; export class DateDisplayField extends HighlightableDisplayField { @InjectField() timezoneService:TimezoneService; + @InjectField() apiV3Service:APIV3Service; public render(element:HTMLElement, displayText:string):void { @@ -76,9 +77,8 @@ export class DateDisplayField extends HighlightableDisplayField { public get valueString() { if (this.value) { return this.timezoneService.formattedDate(this.value); - } else { - return ''; } + return ''; } private showSchedulingMode():boolean { diff --git a/frontend/src/app/shared/components/fields/display/field-types/datetime-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/datetime-display-field.module.ts index dd659c414e..cd35a65bd0 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/datetime-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/datetime-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; export class DateTimeDisplayField extends DisplayField { @InjectField() timezoneService:TimezoneService; diff --git a/frontend/src/app/shared/components/fields/display/field-types/duration-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/duration-display-field.module.ts index 5e6f2a7455..7db90002a7 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/duration-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/duration-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; export class DurationDisplayField extends DisplayField { @InjectField() timezoneService:TimezoneService; @@ -43,7 +43,7 @@ export class DurationDisplayField extends DisplayField { * Duration fields may have an additional derived value */ public get derivedPropertyName() { - return "derived" + this.name.charAt(0).toUpperCase() + this.name.slice(1); + return `derived${this.name.charAt(0).toUpperCase()}${this.name.slice(1)}`; } public get derivedValue():string|null { @@ -55,9 +55,8 @@ export class DurationDisplayField extends DisplayField { if (value) { return this.timezoneService.formattedDuration(value); - } else { - return this.placeholder; } + return this.placeholder; } public render(element:HTMLElement, displayText:string):void { @@ -67,7 +66,7 @@ export class DurationDisplayField extends DisplayField { } element.classList.add('split-time-field'); - const value = this.value; + const { value } = this; const actual:number = (value && this.timezoneService.toHours(value)) || 0; if (actual !== 0) { @@ -94,7 +93,7 @@ export class DurationDisplayField extends DisplayField { const span = document.createElement('span'); span.setAttribute('title', this.texts.empty); - span.textContent = '(' + (actualPresent ? '+' : '') + displayText + ')'; + span.textContent = `(${actualPresent ? '+' : ''}${displayText})`; span.title = `${this.derivedValueString} ${this.derivedText}`; span.classList.add('-derived-value'); @@ -110,13 +109,12 @@ export class DurationDisplayField extends DisplayField { } public isEmpty():boolean { - const value = this.value; + const { value } = this; const derived = this.derivedValue; const valueEmpty = !value || this.timezoneService.toHours(value) === 0; const derivedEmpty = !derived || this.timezoneService.toHours(derived) === 0; - return valueEmpty && derivedEmpty; } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/float-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/float-display-field.module.ts index 9e94f4ae97..804606ffe6 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/float-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/float-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class FloatDisplayField extends DisplayField { - public get valueString():string { if (this.value == null) { return ''; @@ -37,7 +36,7 @@ export class FloatDisplayField extends DisplayField { return this.value.toLocaleString( this.I18n.locale, - { useGrouping: true, maximumFractionDigits: 20 } + { useGrouping: true, maximumFractionDigits: 20 }, ); } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/formattable-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/formattable-display-field.module.ts index e63d404cb6..886f2e417c 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/formattable-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/formattable-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { ApplicationRef } from "@angular/core"; -import { DynamicBootstrapper } from "core-app/core/setup/globals/dynamic-bootstrapper"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { ExpressionService } from "core-app/core/expression/expression.service"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { ApplicationRef } from '@angular/core'; +import { DynamicBootstrapper } from 'core-app/core/setup/globals/dynamic-bootstrapper'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { ExpressionService } from 'core-app/core/expression/expression.service'; export class FormattableDisplayField extends DisplayField { - @InjectField() readonly appRef:ApplicationRef; public render(element:HTMLElement, displayText:string, options:any = {}):void { @@ -80,8 +79,7 @@ export class FormattableDisplayField extends DisplayField { protected unescape(html:string) { if (html) { return ExpressionService.unescape(html); - } else { - return ''; } + return ''; } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/highlightable-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/highlightable-display-field.module.ts index 4607b9a854..777f9d3690 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/highlightable-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/highlightable-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { WorkPackageViewHighlightingService } from "core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { WorkPackageViewHighlightingService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class HighlightableDisplayField extends DisplayField { /** Optionally test if we can inject highlighting service */ @InjectField(WorkPackageViewHighlightingService, null) viewHighlighting:WorkPackageViewHighlightingService; - // DisplayFieldRenderer.attributeName returns the 'date' name for the // 'dueDate' field because it is its schema.mappedName (that allows to display // the correct input type). In the query.highlightedAttributes (used to decide // if a field is highlighted) the attribute has the name 'dueDate', so we need // to return the original name to get it highlighted. - get highlightName () { + get highlightName() { if (this.name === 'date') { return 'dueDate'; - } else { - return this.name; } + return this.name; } public get shouldHighlight() { diff --git a/frontend/src/app/shared/components/fields/display/field-types/highlighted-resource-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/highlighted-resource-display-field.module.ts index 54fb6c7e02..f3caa516af 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/highlighted-resource-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/highlighted-resource-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Highlighting } from "core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions"; -import { HighlightableDisplayField } from "core-app/shared/components/fields/display/field-types/highlightable-display-field.module"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; +import { HighlightableDisplayField } from 'core-app/shared/components/fields/display/field-types/highlightable-display-field.module'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export class HighlightedResourceDisplayField extends HighlightableDisplayField { - public render(element:HTMLElement, displayText:string):void { super.render(element, displayText); @@ -43,9 +42,8 @@ export class HighlightedResourceDisplayField extends HighlightableDisplayField { public get value() { if (this.schema) { return this.attribute && this.attribute.name; - } else { - return null; } + return null; } private addHighlight(element:HTMLElement):void { diff --git a/frontend/src/app/shared/components/fields/display/field-types/id-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/id-display-field.module.ts index 5adf1b9d73..74532065c8 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/id-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/id-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class IdDisplayField extends DisplayField { public text = { - linkTitle: this.I18n.t('js.work_packages.message_successful_show_in_fullscreen') + linkTitle: this.I18n.t('js.work_packages.message_successful_show_in_fullscreen'), }; public get value() { if (this.resource.isNew) { return null; - } else { - return this.resource[this.name]; } + return this.resource[this.name]; } public render(element:HTMLElement, displayText:string):void { diff --git a/frontend/src/app/shared/components/fields/display/field-types/integer-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/integer-display-field.module.ts index f25c83c399..62d202aea5 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/integer-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/integer-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class IntegerDisplayField extends DisplayField { public get value() { diff --git a/frontend/src/app/shared/components/fields/display/field-types/linked-work-package-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/linked-work-package-display-field.module.ts index 43878fc5e2..566f7b72d4 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/linked-work-package-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/linked-work-package-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,18 +28,18 @@ import { StateService } from '@uirouter/core'; import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; -import { UiStateLinkBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder"; -import { WorkPackageDisplayField } from "core-app/shared/components/fields/display/field-types/work-package-display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { UiStateLinkBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder'; +import { WorkPackageDisplayField } from 'core-app/shared/components/fields/display/field-types/work-package-display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class LinkedWorkPackageDisplayField extends WorkPackageDisplayField { - public text = { linkTitle: this.I18n.t('js.work_packages.message_successful_show_in_fullscreen'), - none: this.I18n.t('js.filter.noneElement') + none: this.I18n.t('js.filter.noneElement'), }; @InjectField() $state!:StateService; + @InjectField() keepTab!:KeepTabService; private uiStateBuilder:UiStateLinkBuilder = new UiStateLinkBuilder(this.$state, this.keepTab); @@ -53,11 +53,11 @@ export class LinkedWorkPackageDisplayField extends WorkPackageDisplayField { const link = this.uiStateBuilder.linkToShow( this.wpId, this.text.linkTitle, - this.valueString + this.valueString, ); const title = document.createElement('span'); - title.textContent = ' ' + _.truncate(this.title, { length: 40 }); + title.textContent = ` ${_.truncate(this.title, { length: 40 })}`; element.innerHTML = ''; element.appendChild(link); @@ -69,6 +69,6 @@ export class LinkedWorkPackageDisplayField extends WorkPackageDisplayField { } public get valueString() { - return '#' + this.wpId; + return `#${this.wpId}`; } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module.ts index b6244b69f4..9426c00a7b 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-custom-options-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { ResourcesDisplayField } from "./resources-display-field.module"; -import { cssClassCustomOption } from "core-app/shared/components/fields/display/display-field.module"; +import { cssClassCustomOption } from 'core-app/shared/components/fields/display/display-field.module'; +import { ResourcesDisplayField } from './resources-display-field.module'; export class MultipleLinesCustomOptionsDisplayField extends ResourcesDisplayField { - public render(element:HTMLElement, displayText:string):void { const values = this.value; element.setAttribute('title', displayText); diff --git a/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module.ts index 6745d76007..13e0489c07 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/multiple-lines-user-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,10 +26,10 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {ResourcesDisplayField} from "./resources-display-field.module"; -import {UserResource} from "core-app/features/hal/resources/user-resource"; -import {InjectField} from "core-app/shared/helpers/angular/inject-field.decorator"; -import {PrincipalRendererService} from "core-app/shared/components/principal/principal-renderer.service"; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { PrincipalRendererService } from 'core-app/shared/components/principal/principal-renderer.service'; +import { ResourcesDisplayField } from './resources-display-field.module'; export class MultipleLinesUserFieldModule extends ResourcesDisplayField { @InjectField() principalRenderer:PrincipalRendererService; diff --git a/frontend/src/app/shared/components/fields/display/field-types/multiple-user-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/multiple-user-display-field.module.ts index 9645d44984..9cb9bc7d1c 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/multiple-user-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/multiple-user-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {ResourcesDisplayField} from "./resources-display-field.module"; -import {UserResource} from "core-app/features/hal/resources/user-resource"; -import {InjectField} from "core-app/shared/helpers/angular/inject-field.decorator"; -import {PrincipalRendererService} from "core-app/shared/components/principal/principal-renderer.service"; -import {cssClassCustomOption} from "core-app/shared/components/fields/display/display-field.module"; +import { UserResource } from 'core-app/features/hal/resources/user-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { PrincipalRendererService } from 'core-app/shared/components/principal/principal-renderer.service'; +import { cssClassCustomOption } from 'core-app/shared/components/fields/display/display-field.module'; +import { ResourcesDisplayField } from './resources-display-field.module'; export class MultipleUserFieldModule extends ResourcesDisplayField { @InjectField() principalRenderer:PrincipalRendererService; @@ -69,7 +69,6 @@ export class MultipleUserFieldModule extends ResourcesDisplayField { } element.appendChild(content); - } public renderAbridgedValues(element:HTMLElement, values:UserResource[]) { diff --git a/frontend/src/app/shared/components/fields/display/field-types/plain-formattable-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/plain-formattable-display-field.module.ts index 15c92cb628..3d8006fe5c 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/plain-formattable-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/plain-formattable-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class PlainFormattableDisplayField extends DisplayField { public get value() { diff --git a/frontend/src/app/shared/components/fields/display/field-types/progress-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/progress-display-field.module.ts index 879f2f0f1f..ea11a22de8 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/progress-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/progress-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,19 +26,18 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class ProgressDisplayField extends DisplayField { public get value() { if (this.schema) { return this.resource[this.name] || 0; - } else { - return null; } + return null; } public get percentLabel() { - return this.roundedProgress + '%'; + return `${this.roundedProgress}%`; } public get roundedProgress() { diff --git a/frontend/src/app/shared/components/fields/display/field-types/progress-text-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/progress-text-display-field.module.ts index 2a984db4c5..806fb05f39 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/progress-text-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/progress-text-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // diff --git a/frontend/src/app/shared/components/fields/display/field-types/project-status-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/project-status-display-field.module.ts index c33ba1ad8a..4530bb0f1a 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/project-status-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/project-status-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; -import { projectStatusCodeCssClass, projectStatusI18n } from "core-app/shared/components/fields/helpers/project-status-helper"; - +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { + projectStatusCodeCssClass, + projectStatusI18n, +} from 'core-app/shared/components/fields/helpers/project-status-helper'; export class ProjectStatusDisplayField extends DisplayField { public render(element:HTMLElement, displayText:string):void { @@ -38,7 +40,7 @@ export class ProjectStatusDisplayField extends DisplayField { bulb.classList.add('project-status--bulb', projectStatusCodeCssClass(code)); const name = document.createElement('span'); - name.classList.add('project-status--name', projectStatusCodeCssClass(code)); + name.classList.add('project-status--name', projectStatusCodeCssClass(code)); name.textContent = projectStatusI18n(code, this.I18n); element.innerHTML = ''; diff --git a/frontend/src/app/shared/components/fields/display/field-types/resource-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/resource-display-field.module.ts index d5f1d84cb1..0cabf90df8 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/resource-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/resource-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,13 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class ResourceDisplayField extends DisplayField { public get value() { if (this.schema) { return this.attribute && this.attribute.name; - } else { - return null; } + return null; } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/resources-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/resources-display-field.module.ts index cfbdcf32e0..e7e17f3628 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/resources-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/resources-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { cssClassCustomOption, DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { cssClassCustomOption, DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class ResourcesDisplayField extends DisplayField { public isEmpty():boolean { @@ -36,16 +36,14 @@ export class ResourcesDisplayField extends DisplayField { public get value() { const cf = this.resource[this.name]; if (this.schema && cf) { - if (cf.elements) { return cf.elements.map((e:any) => e.name); - } else if (cf.map) { + } if (cf.map) { return cf.map((e:any) => e.name); - } else if (cf.name) { + } if (cf.name) { return [cf.name]; - } else { - return ["error: " + JSON.stringify(cf)]; } + return [`error: ${JSON.stringify(cf)}`]; } return []; diff --git a/frontend/src/app/shared/components/fields/display/field-types/text-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/text-display-field.module.ts index 762f3d2273..c3e65f7d5a 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/text-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/text-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class TextDisplayField extends DisplayField { } diff --git a/frontend/src/app/shared/components/fields/display/field-types/type-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/type-display-field.module.ts index d87f443580..1f609a0f67 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/type-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/type-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,7 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HighlightedResourceDisplayField } from "core-app/shared/components/fields/display/field-types/highlighted-resource-display-field.module"; +import { HighlightedResourceDisplayField } from 'core-app/shared/components/fields/display/field-types/highlighted-resource-display-field.module'; export class TypeDisplayField extends HighlightedResourceDisplayField { // Type will always be highlighted diff --git a/frontend/src/app/shared/components/fields/display/field-types/user-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/user-display-field.module.ts index 45ca0947eb..613f93aa26 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/user-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/user-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,9 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import {DisplayField} from "core-app/shared/components/fields/display/display-field.module"; -import {InjectField} from "core-app/shared/helpers/angular/inject-field.decorator"; -import {PrincipalRendererService} from "core-app/shared/components/principal/principal-renderer.service"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { PrincipalRendererService } from 'core-app/shared/components/principal/principal-renderer.service'; export class UserDisplayField extends DisplayField { @InjectField() principalRenderer:PrincipalRendererService; @@ -36,9 +36,8 @@ export class UserDisplayField extends DisplayField { public get value() { if (this.schema) { return this.attribute && this.attribute.name; - } else { - return null; } + return null; } public render(element:HTMLElement, displayText:string):void { @@ -49,7 +48,7 @@ export class UserDisplayField extends DisplayField { element, this.attribute, { hide: false, link: false }, - { hide: false, size: 'medium' } + { hide: false, size: 'medium' }, ); } } diff --git a/frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.ts index 75084cbad2..ea854420f0 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/work-package-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DisplayField } from "core-app/shared/components/fields/display/display-field.module"; +import { DisplayField } from 'core-app/shared/components/fields/display/display-field.module'; export class WorkPackageDisplayField extends DisplayField { - public text = { - none: this.I18n.t('js.filter.noneElement') + none: this.I18n.t('js.filter.noneElement'), }; public get value() { @@ -41,9 +40,8 @@ export class WorkPackageDisplayField extends DisplayField { public get title() { if (this.isEmpty()) { return this.text.none; - } else { - return this.value.name; } + return this.value.name; } public get wpId() { @@ -61,7 +59,7 @@ export class WorkPackageDisplayField extends DisplayField { public get valueString() { // cannot display the type name easily here as it may not be loaded - return `#${ this.wpId } ${ this.title }`; + return `#${this.wpId} ${this.title}`; } public isEmpty():boolean { diff --git a/frontend/src/app/shared/components/fields/display/field-types/wp-id-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/wp-id-display-field.module.ts index aee4d8702a..9ead7f4f64 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/wp-id-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/wp-id-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,12 +28,13 @@ import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; import { StateService } from '@uirouter/core'; -import { UiStateLinkBuilder } from "core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder"; -import { IdDisplayField } from "core-app/shared/components/fields/display/field-types/id-display-field.module"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { UiStateLinkBuilder } from 'core-app/features/work-packages/components/wp-fast-table/builders/ui-state-link-builder'; +import { IdDisplayField } from 'core-app/shared/components/fields/display/field-types/id-display-field.module'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class WorkPackageIdDisplayField extends IdDisplayField { @InjectField() $state!:StateService; + @InjectField() keepTab!:KeepTabService; private uiStateBuilder:UiStateLinkBuilder = new UiStateLinkBuilder(this.$state, this.keepTab); @@ -45,7 +46,7 @@ export class WorkPackageIdDisplayField extends IdDisplayField { const link = this.uiStateBuilder.linkToShow( this.value, displayText, - this.value + this.value, ); element.appendChild(link); diff --git a/frontend/src/app/shared/components/fields/display/field-types/wp-spent-time-display-field.module.ts b/frontend/src/app/shared/components/fields/display/field-types/wp-spent-time-display-field.module.ts index 51eb48562e..8e8d2c7307 100644 --- a/frontend/src/app/shared/components/fields/display/field-types/wp-spent-time-display-field.module.ts +++ b/frontend/src/app/shared/components/fields/display/field-types/wp-spent-time-display-field.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,23 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { DurationDisplayField } from './duration-display-field.module'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { ProjectResource } from "core-app/features/hal/resources/project-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { ProjectResource } from 'core-app/features/hal/resources/project-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import * as URI from 'urijs'; import { TimeEntryCreateService } from 'core-app/shared/components/time_entries/create/create.service'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { DurationDisplayField } from './duration-display-field.module'; export class WorkPackageSpentTimeDisplayField extends DurationDisplayField { public text = { linkTitle: this.I18n.t('js.work_packages.message_view_spent_time'), - logTime: this.I18n.t('js.button_log_time') + logTime: this.I18n.t('js.button_log_time'), }; @InjectField() PathHelper:PathHelperService; + @InjectField(TimeEntryCreateService, null) timeEntryCreateService:TimeEntryCreateService; + @InjectField() apiV3Service:APIV3Service; public render(element:HTMLElement, displayText:string):void { diff --git a/frontend/src/app/shared/components/fields/edit/edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/edit-field.component.ts index 96ef074dff..64056b096c 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -34,14 +34,13 @@ import { InjectionToken, Injector, OnDestroy, - OnInit -} from "@angular/core"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { Field, IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; + OnInit, +} from '@angular/core'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { Field, IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export const OpEditingPortalSchemaToken = new InjectionToken('editing-portal--schema'); export const OpEditingPortalHandlerToken = new InjectionToken('editing-portal--handler'); @@ -61,12 +60,12 @@ export abstract class EditFieldComponent extends Field implements OnInit, OnDest protected $element:JQuery; constructor(readonly I18n:I18nService, - readonly elementRef:ElementRef, - @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, - @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, - @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, - readonly cdRef:ChangeDetectorRef, - readonly injector:Injector) { + readonly elementRef:ElementRef, + @Inject(OpEditingPortalChangesetToken) protected change:ResourceChangeset, + @Inject(OpEditingPortalSchemaToken) public schema:IFieldSchema, + @Inject(OpEditingPortalHandlerToken) readonly handler:EditFieldHandler, + readonly cdRef:ChangeDetectorRef, + readonly injector:Injector) { super(); this.updateFromChangeset(change); @@ -75,7 +74,7 @@ export abstract class EditFieldComponent extends Field implements OnInit, OnDest this.change.state .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe((change) => { const fieldSchema = change.schema.ofProperty(this.name); @@ -101,9 +100,8 @@ export abstract class EditFieldComponent extends Field implements OnInit, OnDest return this.$element .closest(overflowingContainerSelector) .data(overflowingContainerAttribute); - } else { - return null; } + return null; } public get inFlight() { diff --git a/frontend/src/app/shared/components/fields/edit/edit-field.initializer.ts b/frontend/src/app/shared/components/fields/edit/edit-field.initializer.ts index bf353c29cd..b7dcc20cec 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-field.initializer.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-field.initializer.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,26 +26,25 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { EditFieldService } from "core-app/shared/components/fields/edit/edit-field.service"; -import { TextEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component"; -import { IntegerEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component"; -import { DurationEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/duration-edit-field.component"; -import { SelectEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component"; -import { MultiSelectEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/multi-select-edit-field.component"; -import { FloatEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/float-edit-field.component"; -import { BooleanEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component"; -import { WorkPackageEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/work-package-edit-field.component"; -import { DateEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component"; -import { FormattableEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component"; -import { SelectAutocompleterRegisterService } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service"; -import { ProjectStatusEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/project-status-edit-field.component"; -import { PlainFormattableEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component"; -import { TimeEntryWorkPackageEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/te-work-package-edit-field.component"; -import { CombinedDateEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/combined-date-edit-field.component"; -import { VersionAutocompleterComponent } from "core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component"; -import { WorkPackageAutocompleterComponent } from "core-app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component"; -import { WorkPackageCommentFieldComponent } from "core-app/features/work-packages/components/work-package-comment/wp-comment-field.component"; - +import { EditFieldService } from 'core-app/shared/components/fields/edit/edit-field.service'; +import { TextEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component'; +import { IntegerEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component'; +import { DurationEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/duration-edit-field.component'; +import { SelectEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component'; +import { MultiSelectEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/multi-select-edit-field.component'; +import { FloatEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/float-edit-field.component'; +import { BooleanEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component'; +import { WorkPackageEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/work-package-edit-field.component'; +import { DateEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component'; +import { FormattableEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component'; +import { SelectAutocompleterRegisterService } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service'; +import { ProjectStatusEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/project-status-edit-field.component'; +import { PlainFormattableEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component'; +import { TimeEntryWorkPackageEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/te-work-package-edit-field.component'; +import { CombinedDateEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/combined-date-edit-field.component'; +import { VersionAutocompleterComponent } from 'core-app/shared/components/autocompleter/version-autocompleter/version-autocompleter.component'; +import { WorkPackageAutocompleterComponent } from 'core-app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component'; +import { WorkPackageCommentFieldComponent } from 'core-app/features/work-packages/components/work-package-comment/wp-comment-field.component'; export function initializeCoreEditFields(editFieldService:EditFieldService, selectAutocompleterRegisterService:SelectAutocompleterRegisterService) { return () => { @@ -65,7 +64,7 @@ export function initializeCoreEditFields(editFieldService:EditFieldService, sele 'Project']) .addFieldType(MultiSelectEditFieldComponent, 'multi-select', [ '[]CustomOption', - '[]User' + '[]User', ]) .addFieldType(FloatEditFieldComponent, 'float', ['Float']) .addFieldType(WorkPackageEditFieldComponent, 'workPackage', ['WorkPackage']) diff --git a/frontend/src/app/shared/components/fields/edit/edit-field.service.ts b/frontend/src/app/shared/components/fields/edit/edit-field.service.ts index b921b5763b..11b3a456c0 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-field.service.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-field.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,8 @@ //++ import { Injectable } from '@angular/core'; -import { AbstractFieldService, IFieldType } from "core-app/shared/components/fields/field.service"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { AbstractFieldService, IFieldType } from 'core-app/shared/components/fields/field.service'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; export interface IEditFieldType extends IFieldType { new():EditFieldComponent; diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form-routing.service.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form-routing.service.ts index 98482d224d..37a42492cd 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form-routing.service.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form-routing.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,8 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Transition } from "@uirouter/core"; -import { Injectable } from "@angular/core"; +import { Transition } from '@uirouter/core'; +import { Injectable } from '@angular/core'; @Injectable() export class EditFormRoutingService { @@ -42,4 +42,3 @@ export class EditFormRoutingService { return true; } } - diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts index 93f7559e11..85a9d352d2 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,50 +26,64 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, ElementRef, EventEmitter, Injector, Input, OnDestroy, OnInit, Optional, Output } from '@angular/core'; +import { + Component, + ElementRef, + EventEmitter, + Injector, + Input, + OnDestroy, + OnInit, + Optional, + Output, +} from '@angular/core'; import { StateService, Transition, TransitionService } from '@uirouter/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { EditableAttributeFieldComponent } from 'core-app/shared/components/fields/edit/field/editable-attribute-field.component'; import { input } from 'reactivestates'; import { filter, map, take } from 'rxjs/operators'; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { activeFieldClassName, activeFieldContainerClassName, - EditForm -} from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { EditingPortalService } from "core-app/shared/components/fields/edit/editing-portal/editing-portal-service"; -import { EditFormRoutingService } from "core-app/shared/components/fields/edit/edit-form/edit-form-routing.service"; -import { ResourceChangesetCommit } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { GlobalEditFormChangesTrackerService } from "core-app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service"; + EditForm, +} from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { EditingPortalService } from 'core-app/shared/components/fields/edit/editing-portal/editing-portal-service'; +import { EditFormRoutingService } from 'core-app/shared/components/fields/edit/edit-form/edit-form-routing.service'; +import { ResourceChangesetCommit } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { GlobalEditFormChangesTrackerService } from 'core-app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service'; @Component({ selector: 'edit-form,[edit-form]', - template: '' + template: '', }) export class EditFormComponent extends EditForm implements OnInit, OnDestroy { @Input('resource') resource:HalResource; + @Input('inEditMode') initializeEditMode = false; + @Input('skippedFields') skippedFields:string[] = []; @Output('onSaved') onSavedEmitter = new EventEmitter<{ savedResource:HalResource, isInitial:boolean }>(); public fields:{ [attribute:string]:EditableAttributeFieldComponent } = {}; + private registeredFields = input(); + private unregisterListener:Function; constructor(public readonly injector:Injector, - protected readonly elementRef:ElementRef, - protected readonly $transitions:TransitionService, - protected readonly ConfigurationService:ConfigurationService, - protected readonly editingPortalService:EditingPortalService, - protected readonly $state:StateService, - protected readonly I18n:I18nService, - @Optional() protected readonly editFormRouting:EditFormRoutingService, - private globalEditFormChangesTrackerService:GlobalEditFormChangesTrackerService) { + protected readonly elementRef:ElementRef, + protected readonly $transitions:TransitionService, + protected readonly ConfigurationService:ConfigurationService, + protected readonly editingPortalService:EditingPortalService, + protected readonly $state:StateService, + protected readonly I18n:I18nService, + @Optional() protected readonly editFormRouting:EditFormRoutingService, + private globalEditFormChangesTrackerService:GlobalEditFormChangesTrackerService) { super(injector); const confirmText = I18n.t('js.work_packages.confirm_edit_cancel'); @@ -118,7 +132,7 @@ export class EditFormComponent extends EditForm implements OnInit, form, schema, fieldName, - errors + errors, ); }); } @@ -164,8 +178,7 @@ export class EditFormComponent extends EditForm implements OnInit, this.fields[field.fieldName] = field; this.registeredFields.putValue(_.keys(this.fields)); - const shouldActivate = - (this.editMode && !this.skipField(field) || this.activeFields[field.fieldName]); + const shouldActivate = (this.editMode && !this.skipField(field) || this.activeFields[field.fieldName]); if (shouldActivate) { field.activateOnForm(true); @@ -176,15 +189,15 @@ export class EditFormComponent extends EditForm implements OnInit, return this.registeredFields .values$() .pipe( - filter(keys => keys.indexOf(name) >= 0), + filter((keys) => keys.indexOf(name) >= 0), take(1), - map(() => this.fields[name]) + map(() => this.fields[name]), ) .toPromise(); } public start() { - _.each(this.fields, ctrl => this.activate(ctrl.fieldName)); + _.each(this.fields, (ctrl) => this.activate(ctrl.fieldName)); } protected focusOnFirstError():void { @@ -196,7 +209,7 @@ export class EditFormComponent extends EditForm implements OnInit, } private skipField(field:EditableAttributeFieldComponent) { - const fieldName = field.fieldName; + const { fieldName } = field; const isSkipField = this.skippedFields.indexOf(fieldName) !== -1; diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts index 2ce3b43a57..976761809c 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -28,28 +28,30 @@ import { Injector } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { HalResourceEditingService, - ResourceChangesetCommit -} from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { ErrorResource } from "core-app/features/hal/resources/error-resource"; + ResourceChangesetCommit, +} from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; export const activeFieldContainerClassName = 'inline-edit--active-field'; export const activeFieldClassName = 'inline-edit--field'; export abstract class EditForm { - // Injections @InjectField() states:States; + @InjectField() halEditing:HalResourceEditingService; + @InjectField() halNotification:HalResourceNotificationService; + @InjectField() halEvents:HalEventsService; // All current active (open) edit fields @@ -98,7 +100,6 @@ export abstract class EditForm { return !_.isEmpty(this.activeFields); } - /** * Return the current or a new change object for the given resource. * This will always return a valid (potentially empty) change. @@ -136,9 +137,7 @@ export abstract class EditForm { return Promise.resolve(); } - return this.requireVisible(fieldName).then(() => { - return this.activate(fieldName, true); - }); + return this.requireVisible(fieldName).then(() => this.activate(fieldName, true)); } /** @@ -179,7 +178,7 @@ export abstract class EditForm { return new Promise((resolve, reject) => { this.halEditing.save>(this.change) - .then(result => { + .then((result) => { // Close all current fields this.closeEditFields(openFields); @@ -246,15 +245,13 @@ export abstract class EditForm { private setErrorsForFields(erroneousFields:string[]) { // Accumulate errors for the given response - const promises:Promise[] = erroneousFields.map((fieldName:string) => { - return this.requireVisible(fieldName).then(() => { - if (this.activeFields[fieldName]) { - this.activeFields[fieldName].setErrors(this.errorsPerAttribute[fieldName] || []); - } + const promises:Promise[] = erroneousFields.map((fieldName:string) => this.requireVisible(fieldName).then(() => { + if (this.activeFields[fieldName]) { + this.activeFields[fieldName].setErrors(this.errorsPerAttribute[fieldName] || []); + } - return this.activateWhenNeeded(fieldName) as any; - }); - }); + return this.activateWhenNeeded(fieldName) as any; + })); Promise.all(promises) .then(() => { @@ -319,7 +316,7 @@ export abstract class EditForm { return fieldHandler; }) .catch((error) => { - console.error('Failed to render edit field:' + error); + console.error(`Failed to render edit field:${error}`); this.halNotification.handleRawError(error); }); } diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-field-handler.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-field-handler.ts index 69207c3b50..2ae2a0d174 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-field-handler.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-field-handler.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,8 +27,8 @@ //++ import { Subject } from 'rxjs'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; export abstract class EditFieldHandler extends UntilDestroyedMixin { /** diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts index 539d1fdedf..5c2e0258ab 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts @@ -7,41 +7,49 @@ import { Input, OnDestroy, OnInit, - Output -} from "@angular/core"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; + Output, +} from '@angular/core'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; import { OpEditingPortalChangesetToken, OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken -} from "core-app/shared/components/fields/edit/edit-field.component"; -import { createLocalInjector } from "core-app/shared/components/fields/edit/editing-portal/edit-form-portal.injector"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { EditFieldService, IEditFieldType } from "core-app/shared/components/fields/edit/edit-field.service"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; + OpEditingPortalSchemaToken, +} from 'core-app/shared/components/fields/edit/edit-field.component'; +import { createLocalInjector } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.injector'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { EditFieldService, IEditFieldType } from 'core-app/shared/components/fields/edit/edit-field.service'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; @Component({ selector: 'edit-form-portal', - templateUrl: './edit-form-portal.component.html' + templateUrl: './edit-form-portal.component.html', }) export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit { @Input() schemaInput:IFieldSchema; + @Input() changeInput:ResourceChangeset; + @Input() editFieldHandler:EditFieldHandler; + @Output() public onEditFieldReady = new EventEmitter(); public handler:EditFieldHandler; + public schema:IFieldSchema; + public change:ResourceChangeset; + public fieldInjector:Injector; public componentClass:IEditFieldType; + public htmlId:string; + public label:string; constructor(readonly injector:Injector, - readonly editField:EditFieldService, - readonly elementRef:ElementRef) { + readonly editField:EditFieldService, + readonly elementRef:ElementRef) { } ngOnInit() { @@ -49,7 +57,6 @@ export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit this.handler = this.editFieldHandler; this.schema = this.schemaInput; this.change = this.changeInput; - } else { this.handler = this.injector.get(OpEditingPortalHandlerToken); this.schema = this.injector.get(OpEditingPortalSchemaToken); diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.injector.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.injector.ts index 7258023c49..d179133ae7 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.injector.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.injector.ts @@ -1,13 +1,13 @@ -import { Injector } from "@angular/core"; +import { Injector } from '@angular/core'; import { OpEditingPortalChangesetToken, OpEditingPortalHandlerToken, - OpEditingPortalSchemaToken -} from "core-app/shared/components/fields/edit/edit-field.component"; -import { PortalInjector } from "@angular/cdk/portal"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; + OpEditingPortalSchemaToken, +} from 'core-app/shared/components/fields/edit/edit-field.component'; +import { PortalInjector } from '@angular/cdk/portal'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; /** * Creates an injector for the edit field portal to pass data into. diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts index dec476bd72..4762f201bb 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts @@ -1,23 +1,24 @@ /** * A CDK portal implementation to wrap edit-fields in non-angular contexts. */ -import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from "@angular/core"; -import { ComponentPortal, DomPortalOutlet } from "@angular/cdk/portal"; -import { EditFormPortalComponent } from "core-app/shared/components/fields/edit/editing-portal/edit-form-portal.component"; -import { createLocalInjector } from "core-app/shared/components/fields/edit/editing-portal/edit-form-portal.injector"; -import { take } from "rxjs/operators"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { HalResourceEditFieldHandler } from "core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler"; +import { + ApplicationRef, ComponentFactoryResolver, Injectable, Injector, +} from '@angular/core'; +import { ComponentPortal, DomPortalOutlet } from '@angular/cdk/portal'; +import { EditFormPortalComponent } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.component'; +import { createLocalInjector } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.injector'; +import { take } from 'rxjs/operators'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler'; @Injectable({ providedIn: 'root' }) export class EditingPortalService { - constructor(private readonly appRef:ApplicationRef, - private readonly componentFactoryResolver:ComponentFactoryResolver, - private readonly pathHelper:PathHelperService) { + private readonly componentFactoryResolver:ComponentFactoryResolver, + private readonly pathHelper:PathHelperService) { } @@ -27,7 +28,6 @@ export class EditingPortalService { schema:IFieldSchema, fieldName:string, errors:string[]):Promise { - // Create the portal outlet const outlet = this.createDomOutlet(container, injector); @@ -39,7 +39,7 @@ export class EditingPortalService { schema, container, this.pathHelper, - errors + errors, ); fieldHandler @@ -65,7 +65,7 @@ export class EditingPortalService { .instance .onEditFieldReady .pipe( - take(1) + take(1), ) .toPromise() .then(() => fieldHandler); @@ -82,9 +82,7 @@ export class EditingPortalService { hostElement, this.componentFactoryResolver, this.appRef, - injector + injector, ); } } - - diff --git a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts index 6484baf6b3..7be378d0ac 100644 --- a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,18 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, Input, Output, EventEmitter } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { + Component, EventEmitter, Input, Output, +} from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ selector: 'edit-field-controls', - templateUrl: './edit-field-controls.component.html' + templateUrl: './edit-field-controls.component.html', }) export class EditFieldControlsComponent { @Input() public cancelTitle:string; + @Input() public saveTitle:string; + @Input('fieldController') public field:EditFieldComponent; + @Output() public onSave = new EventEmitter(); + @Output() public onCancel = new EventEmitter(); public save() { diff --git a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.module.ts b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.module.ts index b4dcc74a78..1071a923f7 100644 --- a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.module.ts @@ -1,8 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { EditFieldControlsComponent } from "core-app/shared/components/fields/edit/field-controls/edit-field-controls.component"; -import { OPSharedModule } from "core-app/shared/shared.module"; - +import { EditFieldControlsComponent } from 'core-app/shared/components/fields/edit/field-controls/edit-field-controls.component'; +import { OPSharedModule } from 'core-app/shared/shared.module'; @NgModule({ declarations: [ @@ -14,6 +13,6 @@ import { OPSharedModule } from "core-app/shared/shared.module"; ], exports: [ EditFieldControlsComponent, - ] + ], }) export class EditFieldControlsModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts b/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts index 51871fa9c8..823f13c7d3 100644 --- a/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts +++ b/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,25 +26,27 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { keyCodes } from 'core-app/shared/helpers/keyCodes.enum'; +import { KeyCodes } from 'core-app/shared/helpers/keyCodes.enum'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; import { Injector } from '@angular/core'; import { FocusHelperService } from 'core-app/shared/directives/focus/focus-helper'; -import { EditFieldHandler } from "core-app/shared/components/fields/edit/editing-portal/edit-field-handler"; -import { ClickPositionMapper } from "core-app/shared/helpers/set-click-position/set-click-position"; -import { debugLog } from "core-app/shared/helpers/debug_output"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { setPosition } from 'core-app/shared/helpers/set-click-position/set-click-position'; +import { debugLog } from 'core-app/shared/helpers/debug_output'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { Subject } from 'rxjs'; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; -import { EditForm } from "core-app/shared/components/fields/edit/edit-form/edit-form"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; export class HalResourceEditFieldHandler extends EditFieldHandler { // Injections @InjectField() FocusHelper:FocusHelperService; + @InjectField() ConfigurationService:ConfigurationService; + @InjectField() I18n!:I18nService; // Subject to fire when user demanded activation @@ -54,13 +56,12 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { public errors:string[]; constructor(public injector:Injector, - public form:EditForm, - public fieldName:string, - public schema:IFieldSchema, - public element:HTMLElement, - protected pathHelper:PathHelperService, - protected withErrors?:string[]) { - + public form:EditForm, + public fieldName:string, + public schema:IFieldSchema, + public element:HTMLElement, + protected pathHelper:PathHelperService, + protected withErrors?:string[]) { super(); if (withErrors !== undefined) { @@ -100,14 +101,14 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { // Set selection state if input element if (setClickOffset && target.tagName === 'INPUT') { - ClickPositionMapper.setPosition(target as HTMLInputElement, setClickOffset); + setPosition(target as HTMLInputElement, setClickOffset); } } public onFocusOut() { // In case of inline create or erroneous forms: do not save on focus loss // const specialField = this.resource.shouldCloseOnFocusOut(this.fieldName); - if (this.resource.subject && this.withErrors && this.withErrors!.length === 0) { + if (this.resource.subject && this.withErrors && this.withErrors.length === 0) { this.handleUserSubmit(); } } @@ -141,7 +142,7 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { public handleUserKeydown(event:JQuery.TriggeredEvent, onlyCancel = false) { // Only handle submission in edit mode if (this.inEditMode && !onlyCancel) { - if (event.which === keyCodes.ENTER) { + if (event.which === KeyCodes.ENTER) { this.form.submit(); return false; } @@ -149,7 +150,7 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { } // Escape editing when not in edit mode - if (event.which === keyCodes.ESCAPE) { + if (event.which === KeyCodes.ESCAPE) { this.handleUserCancel(); return false; } @@ -215,10 +216,9 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { public errorMessageOnLabel() { if (!this.isErrorenous) { return ''; - } else { - return this.I18n.t('js.inplace.errors.messages_on_field', - { messages: this.errors.join(' ') }); } + return this.I18n.t('js.inplace.errors.messages_on_field', + { messages: this.errors.join(' ') }); } public previewContext(resource:HalResource) { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts index 83966ec038..5a61f96d2b 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,8 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; - +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ template: ` @@ -40,7 +39,7 @@ import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit- (keydown)="handler.handleUserKeydown($event)" [disabled]="inFlight" [id]="handler.htmlId" /> - ` + `, }) export class BooleanEditFieldComponent extends EditFieldComponent { public updateValue(newValue:boolean) { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module.ts index d60a2cdb88..37592beb82 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module.ts @@ -1,18 +1,16 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { BooleanEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component"; - - +import { BooleanEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component'; @NgModule({ declarations: [ BooleanEditFieldComponent, ], imports: [ - CommonModule + CommonModule, ], exports: [ BooleanEditFieldComponent, - ] + ], }) export class BooleanEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts index 9c765c5474..8ed150d90e 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,14 +26,14 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { take } from "rxjs/operators"; -import { DateEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component"; -import { OpModalComponent } from "core-app/shared/components/modal/modal.component"; -import { DatePickerModal } from "core-app/shared/components/datepicker/datepicker.modal"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { take } from 'rxjs/operators'; +import { DateEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component'; +import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; +import { DatePickerModalComponent } from 'core-app/shared/components/datepicker/datepicker.modal'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ template: ` @@ -41,14 +41,17 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service"; (click)="handleClick()" class="op-input" type="text" /> - ` + `, }) export class CombinedDateEditFieldComponent extends DateEditFieldComponent implements OnInit, OnDestroy { @InjectField() readonly timezoneService:TimezoneService; + @InjectField() opModalService:OpModalService; dates = ''; + text_no_start_date = this.I18n.t('js.label_no_start_date'); + text_no_due_date = this.I18n.t('js.label_no_due_date'); private modal:OpModalComponent; @@ -59,7 +62,7 @@ export class CombinedDateEditFieldComponent extends DateEditFieldComponent imple this.handler .$onUserActivate .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.showDatePickerModal(); @@ -78,7 +81,7 @@ export class CombinedDateEditFieldComponent extends DateEditFieldComponent imple private showDatePickerModal():void { const modal = this.modal = this .opModalService - .show(DatePickerModal, this.injector, { changeset: this.change, fieldName: this.name }, true); + .show(DatePickerModalComponent, this.injector, { changeset: this.change, fieldName: this.name }, true); setTimeout(() => { const modalElement = jQuery(modal.elementRef.nativeElement).find('.datepicker-modal'); diff --git a/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.ts index ab6ebfc22f..0bc2bc6710 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,12 +26,12 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component, OnInit } from "@angular/core"; -import * as moment from "moment"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { Component, OnInit } from '@angular/core'; +import * as moment from 'moment'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ template: ` @@ -45,10 +45,11 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service"; [id]="handler.htmlId" classes="inline-edit--field"> - ` + `, }) export class DateEditFieldComponent extends EditFieldComponent implements OnInit { @InjectField() readonly timezoneService:TimezoneService; + @InjectField() opModalService:OpModalService; ngOnInit() { @@ -67,17 +68,15 @@ export class DateEditFieldComponent extends EditFieldComponent implements OnInit public parser(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { return data; - } else { - return null; } + return null; } public formatter(data:any) { if (moment(data, 'YYYY-MM-DD', true).isValid()) { - var d = this.timezoneService.parseDate(data); + const d = this.timezoneService.parseDate(data); return this.timezoneService.formattedISODate(d); - } else { - return null; } + return null; } } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module.ts index a1f852bfcb..03c30b2548 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module.ts @@ -1,9 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { DateEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component"; -import { DatePickerModule } from "core-app/shared/components/op-date-picker/date-picker.module"; - - +import { DateEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.component'; +import { DatePickerModule } from 'core-app/shared/components/op-date-picker/date-picker.module'; @NgModule({ declarations: [ @@ -16,6 +14,6 @@ import { DatePickerModule } from "core-app/shared/components/op-date-picker/date ], exports: [ DateEditFieldComponent, - ] + ], }) export class DateEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/duration-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/duration-edit-field.component.ts index ce583c2d44..8f82e5fd0b 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/duration-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/duration-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,10 +27,10 @@ //++ import * as moment from 'moment'; -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { TimezoneService } from "core-app/core/datetime/timezone.service"; +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ template: ` @@ -45,7 +45,7 @@ import { TimezoneService } from "core-app/core/datetime/timezone.service"; (keydown)="handler.handleUserKeydown($event)" [disabled]="inFlight" [id]="handler.htmlId" /> - ` + `, }) export class DurationEditFieldComponent extends EditFieldComponent { @InjectField() TimezoneService:TimezoneService; @@ -89,4 +89,3 @@ export class DurationEditFieldComponent extends EditFieldComponent { return parsedValue; } } - diff --git a/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts index d6cb4cb25d..d26541a38e 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts @@ -25,8 +25,8 @@ // See docs/COPYRIGHT.rdoc for more details. // ++ -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ template: ` @@ -41,7 +41,7 @@ import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit- (focusout)="handler.onFocusOut()" [attr.lang]="locale" [id]="handler.htmlId" /> - ` + `, }) export class FloatEditFieldComponent extends EditFieldComponent { public locale = I18n.locale; diff --git a/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.ts index d04d5e0753..4a344aa080 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component.ts @@ -25,18 +25,19 @@ // See docs/COPYRIGHT.rdoc for more details. // ++ -import { Component, OnInit, ViewChild, ChangeDetectionStrategy } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; -import { OpCkeditorComponent } from "core-app/shared/components/editor/components/ckeditor/op-ckeditor.component"; +import { + ChangeDetectionStrategy, Component, OnInit, ViewChild, +} from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { OpCkeditorComponent } from 'core-app/shared/components/editor/components/ckeditor/op-ckeditor.component'; import { ICKEditorContext, ICKEditorInstance, -} from "core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service"; - +} from 'core-app/shared/components/editor/components/ckeditor/ckeditor-setup.service'; @Component({ - templateUrl: "./formattable-edit-field.component.html", - changeDetection: ChangeDetectionStrategy.OnPush + templateUrl: './formattable-edit-field.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class FormattableEditFieldComponent extends EditFieldComponent implements OnInit { public readonly field = this; @@ -48,8 +49,11 @@ export class FormattableEditFieldComponent extends EditFieldComponent implements // Values used in template public isPreview = false; + public previewHtml = ''; + public text:Record = {}; + public initialContent:string; public ckEditorContext:ICKEditorContext = { @@ -58,7 +62,7 @@ export class FormattableEditFieldComponent extends EditFieldComponent implements previewContext: this.previewContext, options: { rtl: this.schema.options && this.schema.options.rtl }, type: 'constrained', - ...this.resource.getEditorContext(this.field.name) + ...this.resource.getEditorContext(this.field.name), }; ngOnInit():void { @@ -68,7 +72,7 @@ export class FormattableEditFieldComponent extends EditFieldComponent implements this.text = { attachmentLabel: this.I18n.t('js.label_formattable_attachment_hint'), save: this.I18n.t('js.inplace.button_save', { attribute: this.schema.name }), - cancel: this.I18n.t('js.inplace.button_cancel', { attribute: this.schema.name }) + cancel: this.I18n.t('js.inplace.button_cancel', { attribute: this.schema.name }), }; } @@ -118,9 +122,8 @@ export class FormattableEditFieldComponent extends EditFieldComponent implements public get rawValue():string { if (this.value && this.value.raw) { return this.value.raw; - } else { - return ''; } + return ''; } public set rawValue(val:string) { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module.ts index 01b010ba4a..24973a92a4 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module.ts @@ -1,9 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { FormattableEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component"; -import { OpenprojectEditorModule } from "core-app/shared/components/editor/openproject-editor.module"; -import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/field-controls/edit-field-controls.module"; - +import { FormattableEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.component'; +import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; +import { EditFieldControlsModule } from 'core-app/shared/components/fields/edit/field-controls/edit-field-controls.module'; @NgModule({ declarations: [ @@ -16,6 +15,6 @@ import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/ ], exports: [ FormattableEditFieldComponent, - ] + ], }) export class FormattableEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts index db4b81a51c..11775c22da 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts @@ -25,8 +25,8 @@ // See docs/COPYRIGHT.rdoc for more details. // ++ -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ template: ` @@ -40,7 +40,7 @@ import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit- (keydown)="handler.handleUserKeydown($event)" (focusout)="handler.onFocusOut()" [id]="handler.htmlId" /> - ` + `, }) export class IntegerEditFieldComponent extends EditFieldComponent { public locale = I18n.locale; diff --git a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module.ts index 05ba23adff..05c698b2be 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module.ts @@ -1,9 +1,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { IntegerEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component"; -import { FormsModule } from "@angular/forms"; - - +import { IntegerEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component'; +import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [ @@ -15,6 +13,6 @@ import { FormsModule } from "@angular/forms"; ], exports: [ IntegerEditFieldComponent, - ] + ], }) export class IntegerEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts index 28ed25c930..43c33f317d 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,39 +26,47 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Component, OnInit, ViewChild } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; -import { ValueOption } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { ValueOption } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; @Component({ - templateUrl: './multi-select-edit-field.component.html' + templateUrl: './multi-select-edit-field.component.html', }) export class MultiSelectEditFieldComponent extends EditFieldComponent implements OnInit { @ViewChild(NgSelectComponent, { static: true }) public ngSelectComponent:NgSelectComponent; + @InjectField() I18n!:I18nService; public availableOptions:any[] = []; + public valueOptions:ValueOption[]; + public text = { requiredPlaceholder: this.I18n.t('js.placeholders.selection'), placeholder: this.I18n.t('js.placeholders.default'), save: this.I18n.t('js.inplace.button_save', { attribute: this.schema.name }), cancel: this.I18n.t('js.inplace.button_cancel', { attribute: this.schema.name }), }; + public appendTo:any = null; + public currentValueInvalid = false; + public showAddNewUserButton:boolean; private hiddenOverflowContainer = '.__hidden_overflow_container'; + private nullOption:ValueOption; + private _selectedOption:ValueOption[]; - /** Since we need to wait for values to be loaded, remember if the user activated this field*/ + /** Since we need to wait for values to be loaded, remember if the user activated this field */ private requestFocus = false; ngOnInit() { @@ -68,7 +76,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements this.handler .$onUserActivate .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.requestFocus = this.availableOptions.length === 0; @@ -95,7 +103,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements */ public buildSelectedOption() { const value:HalResource[] = this.resource[this.name]; - return value ? _.castArray(value).map(val => this.findValueOption(val)) : []; + return value ? _.castArray(value).map((val) => this.findValueOption(val)) : []; } public get selectedOption() { @@ -109,7 +117,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements public set selectedOption(val:ValueOption[]) { this._selectedOption = val; const mapper = (val:ValueOption) => { - const option = _.find(this.availableOptions, o => o.href === val.href) || this.nullOption; + const option = _.find(this.availableOptions, (o) => o.href === val.href) || this.nullOption; // Special case 'null' value, which angular // only understands in ng-options as an empty string. @@ -120,7 +128,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements return option; }; - this.resource[this.name] = _.castArray(val).map(el => mapper(el)); + this.resource[this.name] = _.castArray(val).map((el) => mapper(el)); } public onOpen() { @@ -143,7 +151,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements // The timeout takes care that the opening is added to the end of the current call stack. // Thus we can be sure that the autocompleter is rendered and ready to be opened. const that = this; - window.setTimeout(function () { + window.setTimeout(() => { that.ngSelectComponent.open(); }, 0); } @@ -160,7 +168,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements private setValues(availableValues:any[], sortValuesByName = false) { if (sortValuesByName) { - availableValues.sort(function (a:any, b:any) { + availableValues.sort((a:any, b:any) => { const nameA = a.name.toLowerCase(); const nameB = b.name.toLowerCase(); return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; @@ -168,9 +176,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements } this.availableOptions = availableValues || []; - this.valueOptions = this.availableOptions.map(el => { - return { name: el.name, href: el.href }; - }); + this.valueOptions = this.availableOptions.map((el) => ({ name: el.name, href: el.href })); this._selectedOption = this.buildSelectedOption(); this.checkCurrentValueValidity(); @@ -186,7 +192,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements } private loadValues() { - const allowedValues = this.schema.allowedValues; + const { allowedValues } = this.schema; if (Array.isArray(allowedValues)) { this.setValues(allowedValues); } else if (this.schema.allowedValues) { @@ -209,9 +215,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements this.currentValueInvalid = !!( // (If value AND) // MultiSelect AND there is no value which href is not in the options hrefs - (!_.some(this.value, (value:HalResource) => { - return _.some(this.availableOptions, (option) => (option.href === value.href)); - })) + (!_.some(this.value, (value:HalResource) => _.some(this.availableOptions, (option) => (option.href === value.href)))) ); } else { // If no value but required diff --git a/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts index 5d595dc004..2b37bd324a 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ - templateUrl: './text-edit-field.component.html' + templateUrl: './text-edit-field.component.html', }) export class PlainFormattableEditFieldComponent extends EditFieldComponent { // only exists because the template is reused and the property is required there. diff --git a/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts index ea6c841a55..213a2115bf 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,12 +27,15 @@ //++ import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Component, OnInit, ViewChild } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; -import { NgSelectComponent } from "@ng-select/ng-select"; -import { projectStatusCodeCssClass, projectStatusI18n } from "core-app/shared/components/fields/helpers/project-status-helper"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; +import { NgSelectComponent } from '@ng-select/ng-select'; +import { + projectStatusCodeCssClass, + projectStatusI18n, +} from 'core-app/shared/components/fields/helpers/project-status-helper'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; interface ProjectStatusOption { href:string @@ -42,46 +45,49 @@ interface ProjectStatusOption { @Component({ templateUrl: './project-status-edit-field.component.html', - styleUrls: ['./project-status-edit-field.component.sass'] + styleUrls: ['./project-status-edit-field.component.sass'], }) export class ProjectStatusEditFieldComponent extends EditFieldComponent implements OnInit { @ViewChild(NgSelectComponent, { static: true }) public ngSelectComponent:NgSelectComponent; + @InjectField() I18n!:I18nService; public availableStatuses:ProjectStatusOption[] = [{ - href: 'not_set', - name: projectStatusI18n('not_set', this.I18n), - colorClass: projectStatusCodeCssClass('not_set') - }]; + href: 'not_set', + name: projectStatusI18n('not_set', this.I18n), + colorClass: projectStatusCodeCssClass('not_set'), + }]; public currentStatusCode:string; + public hiddenOverflowContainer = '#content-wrapper'; + public appendToContainer = 'body'; ngOnInit() { - this.currentStatusCode = this.resource['status'] === null ? this.availableStatuses[0].href : this.resource['status'].href; + this.currentStatusCode = this.resource.status === null ? this.availableStatuses[0].href : this.resource.status.href; this.change.getForm().then((form) => { - form.schema['status'].allowedValues.forEach((status:HalResource) => { + form.schema.status.allowedValues.forEach((status:HalResource) => { this.availableStatuses = [...this.availableStatuses, - { - href: status.href!, - name: status.name, - colorClass: projectStatusCodeCssClass(status.id) - }]; + { + href: status.href!, + name: status.name, + colorClass: projectStatusCodeCssClass(status.id), + }]; }); // The timeout takes care that the opening is added to the end of the current call stack. // Thus we can be sure that the select box is rendered and ready to be opened. const that = this; - window.setTimeout(function () { + window.setTimeout(() => { that.ngSelectComponent.open(); }, 0); }); } public onChange() { - this.resource['status'] = this.currentStatusCode === this.availableStatuses[0].href ? null : { href: this.currentStatusCode }; + this.resource.status = this.currentStatusCode === this.availableStatuses[0].href ? null : { href: this.currentStatusCode }; this.handler.handleUserSubmit(); } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service.ts b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service.ts index 7d8034dd07..ed6e8a6e13 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -38,11 +38,11 @@ export class SelectAutocompleterRegisterService { private _fields:SelectAutocompleterAssignment[] = []; public register(component:any, attribute:string) { - this._fields.push({ attribute: attribute, component: component, }); + this._fields.push({ attribute, component }); } public getAutocompleterOfAttribute(attribute:string) { - const assignment = _.find(this._fields, field => field.attribute === attribute); + const assignment = _.find(this._fields, (field) => field.attribute === attribute); return assignment ? assignment.component : undefined; } } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts index 9a867db325..a7c5b6bdcd 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,18 +27,18 @@ //++ import { Component, InjectFlags, OnInit } from '@angular/core'; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { EditFieldComponent } from '../../edit-field.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { SelectAutocompleterRegisterService } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service'; import { from } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; -import { CreateAutocompleterComponent } from "core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component"; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { StateService } from "@uirouter/core"; -import { CollectionResource } from "core-app/features/hal/resources/collection-resource"; -import { HalResourceNotificationService } from "core-app/features/hal/services/hal-resource-notification.service"; -import { HalResourceSortingService } from "core-app/features/hal/services/hal-resource-sorting.service"; +import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { StateService } from '@uirouter/core'; +import { CollectionResource } from 'core-app/features/hal/resources/collection-resource'; +import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; +import { HalResourceSortingService } from 'core-app/features/hal/services/hal-resource-sorting.service'; +import { EditFieldComponent } from '../../edit-field.component'; export interface ValueOption { name:string; @@ -50,27 +50,37 @@ export interface ValueOption { }) export class SelectEditFieldComponent extends EditFieldComponent implements OnInit { @InjectField() selectAutocompleterRegister:SelectAutocompleterRegisterService; + @InjectField() halNotification:HalResourceNotificationService; + @InjectField() halSorting:HalResourceSortingService; + @InjectField() $state:StateService; + @InjectField(EditFormComponent, null, InjectFlags.Optional) editFormComponent:EditFormComponent; public availableOptions:any[]; + public valueOptions:ValueOption[]; + public text:{ [key:string]:string }; + public appendTo:any = null; + public referenceOutputs:{ [key:string]:Function } = { onCreate: (newElement:HalResource) => this.onCreate(newElement), onChange: (value:HalResource) => this.onChange(value), onKeydown: (event:JQuery.TriggeredEvent) => this.handler.handleUserKeydown(event, true), onOpen: () => this.onOpen(), onClose: () => this.onClose(), - onAfterViewInit: (component:CreateAutocompleterComponent) => this._autocompleterComponent = component + onAfterViewInit: (component:CreateAutocompleterComponent) => this._autocompleterComponent = component, }; + public get selectedOption() { const href = this.value ? this.value.href : null; - return _.find(this.valueOptions, o => o.href === href)!; + return _.find(this.valueOptions, (o) => o.href === href)!; } + public set selectedOption(val:ValueOption|HalResource) { // The InviteUserModal gives us a resource that is not in availableOptions yet, // but we also don't want to wait for a refresh of the options every time we want to @@ -80,7 +90,7 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn return; } - const option = _.find(this.availableOptions, o => o.href === val.href); + const option = _.find(this.availableOptions, (o) => o.href === val.href); // Special case 'null' value, which angular // only understands in ng-options as an empty string. @@ -90,12 +100,15 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn this.value = option; } + public showAddNewButton:boolean; protected valuesLoaded = false; + protected _autocompleterComponent:CreateAutocompleterComponent; private hiddenOverflowContainer = '.__hidden_overflow_container'; + /** Remember the values loading promise which changes as soon as the changeset is updated * (e.g., project or type is changed). */ @@ -108,7 +121,7 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn this.handler .$onUserActivate .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) .subscribe(() => { this.valuesLoadingPromise.then(() => { @@ -117,18 +130,15 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn }); this._syncUrlParamsOnChangeIfNeeded(this.handler.fieldName, this.editFormComponent?.editMode); - } protected initialize() { this.text = { requiredPlaceholder: this.I18n.t('js.placeholders.selection'), - placeholder: this.I18n.t('js.placeholders.default') + placeholder: this.I18n.t('js.placeholders.default'), }; - this.valuesLoadingPromise = this.change.getForm().then(() => { - return this.initialValueLoading(); - }); + this.valuesLoadingPromise = this.change.getForm().then(() => this.initialValueLoading()); this.initializeShowAddButton(); } @@ -143,18 +153,18 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn } public autocompleterComponent() { - const type = this.schema.type; + const { type } = this.schema; return this.selectAutocompleterRegister.getAutocompleterOfAttribute(type) || CreateAutocompleterComponent; } private setValues(availableValues:HalResource[]) { this.availableOptions = this.sortValues(availableValues); this.addEmptyOption(); - this.valueOptions = this.availableOptions.map(el => this.mapAllowedValue(el)); + this.valueOptions = this.availableOptions.map((el) => this.mapAllowedValue(el)); } protected loadValues(query?:string) { - const allowedValues = this.schema.allowedValues; + const { allowedValues } = this.schema; if (Array.isArray(allowedValues)) { this.setValues(allowedValues); @@ -170,24 +180,23 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn protected loadValuesFromBackend(query?:string) { return from( - this.loadAllowedValues(query) + this.loadAllowedValues(query), ).pipe( - tap(collection => { + tap((collection) => { // if it is an unpaginated collection or if we get all possible entries when fetching with a blank // query, we do not need to load the values again; if (collection.count === undefined || collection.total === undefined || (!query && collection.total === collection.count)) { this.valuesLoaded = true; } }), - map(collection => { + map((collection) => { if (collection.count === undefined || collection.total === undefined || (!query && collection.total === collection.count) || !this.value) { return collection.elements; - } else { - return collection.elements.concat([this.value]); } + return collection.elements.concat([this.value]); }), - tap(elements => this.setValues(elements)), - map(() => this.valueOptions) + tap((elements) => this.setValues(elements)), + map(() => this.valueOptions), ); } @@ -213,8 +222,7 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn public get currentValueInvalid():boolean { return !!( (this.value && !_.some(this.availableOptions, (option:HalResource) => (option.href === this.value.href))) - || - (!this.value && this.schema.required) + || (!this.value && this.schema.required) ); } @@ -261,7 +269,7 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn if (emptyOption === undefined) { this.availableOptions.unshift({ name: this.text.placeholder, - href: '' + href: '', }); } } @@ -285,7 +293,7 @@ export class SelectEditFieldComponent extends EditFieldComponent implements OnIn } private getEmptyOption():ValueOption|undefined { - return _.find(this.availableOptions, el => el.name === this.text.placeholder); + return _.find(this.availableOptions, (el) => el.name === this.text.placeholder); } private _syncUrlParamsOnChangeIfNeeded(fieldName:string, editMode:boolean) { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module.ts index a0eda33c40..5b0da85af5 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module.ts @@ -1,10 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { SelectEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component"; +import { SelectEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.component'; import { DynamicModule } from 'ng-dynamic-component'; - - @NgModule({ imports: [ CommonModule, @@ -15,6 +13,6 @@ import { DynamicModule } from 'ng-dynamic-component'; ], exports: [ SelectEditFieldComponent, - ] + ], }) export class SelectEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/te-work-package-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/te-work-package-edit-field.component.ts index 623af26bcc..0c8b7b0dc3 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/te-work-package-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/te-work-package-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,21 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { WorkPackageEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/work-package-edit-field.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { InjectField } from "core-app/shared/helpers/angular/inject-field.decorator"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; +import { Component } from '@angular/core'; +import { WorkPackageEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/work-package-edit-field.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; import { TimeEntryWorkPackageAutocompleterComponent, - TimeEntryWorkPackageAutocompleterMode -} from "core-app/shared/components/autocompleter/te-work-package-autocompleter/te-work-package-autocompleter.component"; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; + TimeEntryWorkPackageAutocompleterMode, +} from 'core-app/shared/components/autocompleter/te-work-package-autocompleter/te-work-package-autocompleter.component'; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; const RECENT_TIME_ENTRIES_MAGIC_NUMBER = 30; @Component({ - templateUrl: './work-package-edit-field.component.html' + templateUrl: './work-package-edit-field.component.html', }) export class TimeEntryWorkPackageEditFieldComponent extends WorkPackageEditFieldComponent { @InjectField() apiV3Service:APIV3Service; @@ -53,7 +53,7 @@ export class TimeEntryWorkPackageEditFieldComponent extends WorkPackageEditField // For reasons beyond me, the referenceOutputs variable is not defined at first when editing // existing values. if (this.referenceOutputs) { - this.referenceOutputs['modeSwitch'] = (mode:TimeEntryWorkPackageAutocompleterMode) => { + this.referenceOutputs.modeSwitch = (mode:TimeEntryWorkPackageAutocompleterMode) => { this.valuesLoaded = false; const lastValue = this.requests.lastRequestedValue!; @@ -87,9 +87,9 @@ export class TimeEntryWorkPackageEditFieldComponent extends WorkPackageEditField return this .apiV3Service .time_entries - .list({ filters: [['user_id', '=', ['me']]], sortBy: [["updated_at", "desc"]], pageSize: RECENT_TIME_ENTRIES_MAGIC_NUMBER }) + .list({ filters: [['user_id', '=', ['me']]], sortBy: [['updated_at', 'desc']], pageSize: RECENT_TIME_ENTRIES_MAGIC_NUMBER }) .toPromise() - .then(collection => { + .then((collection) => { this.recentWorkPackageIds = collection .elements .map((timeEntry) => timeEntry.workPackage.idFromLink) @@ -97,9 +97,8 @@ export class TimeEntryWorkPackageEditFieldComponent extends WorkPackageEditField return this.fetchAllowedValueQuery(query); }); - } else { - return this.fetchAllowedValueQuery(query); } + return this.fetchAllowedValueQuery(query); } protected allowedValuesFilter(query?:string):{} { @@ -119,15 +118,12 @@ export class TimeEntryWorkPackageEditFieldComponent extends WorkPackageEditField protected sortValues(availableValues:HalResource[]) { if ((this._autocompleterComponent as TimeEntryWorkPackageAutocompleterComponent).mode === 'recent') { return this.sortValuesByRecentIds(availableValues); - } else { - return super.sortValues(availableValues); } + return super.sortValues(availableValues); } protected sortValuesByRecentIds(availableValues:HalResource[]) { return availableValues - .sort((a, b) => { - return this.recentWorkPackageIds.indexOf(a.id!) - this.recentWorkPackageIds.indexOf(b.id!); - }); + .sort((a, b) => this.recentWorkPackageIds.indexOf(a.id!) - this.recentWorkPackageIds.indexOf(b.id!)); } } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts index c79624869a..fe96e44f67 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,11 +26,11 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { EditFieldComponent } from "core-app/shared/components/fields/edit/edit-field.component"; +import { Component } from '@angular/core'; +import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ - templateUrl: '../text-edit-field.component.html' + templateUrl: '../text-edit-field.component.html', }) export class TextEditFieldComponent extends EditFieldComponent { // ToDo: Work package specific diff --git a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module.ts b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module.ts index 1d24188bfd..eeaa61bc3e 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { FormsModule } from "@angular/forms"; -import { TextEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component"; -import { FocusModule } from "core-app/shared/directives/focus/focus.module"; +import { FormsModule } from '@angular/forms'; +import { TextEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component'; +import { FocusModule } from 'core-app/shared/directives/focus/focus.module'; @NgModule({ imports: [ @@ -15,6 +15,6 @@ import { FocusModule } from "core-app/shared/directives/focus/focus.module"; ], exports: [ TextEditFieldComponent, - ] + ], }) export class TextEditFieldModule { } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts index 34eb20f319..f60cda6533 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,21 +26,24 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { Component } from "@angular/core"; -import { SelectEditFieldComponent, ValueOption } from './select-edit-field/select-edit-field.component'; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { DebouncedRequestSwitchmap, errorNotificationHandler } from "core-app/shared/helpers/rxjs/debounced-input-switchmap"; +import { Component } from '@angular/core'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { + DebouncedRequestSwitchmap, + errorNotificationHandler, +} from 'core-app/shared/helpers/rxjs/debounced-input-switchmap'; import { take } from 'rxjs/operators'; -import { ApiV3FilterBuilder } from "core-app/shared/helpers/api-v3/api-v3-filter-builder"; +import { ApiV3FilterBuilder } from 'core-app/shared/helpers/api-v3/api-v3-filter-builder'; +import { SelectEditFieldComponent, ValueOption } from './select-edit-field/select-edit-field.component'; @Component({ - templateUrl: './work-package-edit-field.component.html' + templateUrl: './work-package-edit-field.component.html', }) export class WorkPackageEditFieldComponent extends SelectEditFieldComponent { /** Keep a switchmap for search term and loading state */ public requests = new DebouncedRequestSwitchmap( (searchTerm:string) => this.loadValues(searchTerm), - errorNotificationHandler(this.halNotification) + errorNotificationHandler(this.halNotification), ); protected initialValueLoading() { @@ -49,7 +52,7 @@ export class WorkPackageEditFieldComponent extends SelectEditFieldComponent { // Using this hack with the empty value to have the values loaded initially // while avoiding loading it multiple times. return new Promise((resolve) => { - this.requests.output$.pipe(take(1)).subscribe(options => { + this.requests.output$.pipe(take(1)).subscribe((options) => { resolve(options); }); @@ -60,9 +63,8 @@ export class WorkPackageEditFieldComponent extends SelectEditFieldComponent { public get typeahead() { if (this.valuesLoaded) { return false; - } else { - return this.requests.input$; } + return this.requests.input$; } protected allowedValuesFilter(query?:string):{} { @@ -81,16 +83,14 @@ export class WorkPackageEditFieldComponent extends SelectEditFieldComponent { protected mapAllowedValue(value:WorkPackageResource|ValueOption):ValueOption { if ((value as WorkPackageResource).id) { - const prefix = (value as WorkPackageResource).type ? `${(value as WorkPackageResource).type.name} ` : ''; const suffix = (value as WorkPackageResource).subject || value.name; return { - name: `${prefix}#${ (value as WorkPackageResource).id } ${suffix}`, - href: value.href + name: `${prefix}#${(value as WorkPackageResource).id} ${suffix}`, + href: value.href, }; - } else { - return value; } + return value; } } diff --git a/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts b/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts index 0458e5ada4..15890d3da7 100644 --- a/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field/editable-attribute-field.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,9 +26,7 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { SelectionHelpers } from '../../../../helpers/selection-helpers'; -import { debugLog } from '../../../../helpers/debug_output'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -37,62 +35,72 @@ import { Injector, Input, OnDestroy, - OnInit, Optional, - ViewChild + OnInit, + Optional, + ViewChild, } from '@angular/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { OPContextMenuService } from "core-app/shared/components/op-context-menu/op-context-menu.service"; +import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ClickPositionMapper } from "core-app/shared/helpers/set-click-position/set-click-position"; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { ISchemaProxy } from "core-app/features/hal/schemas/schema-proxy"; +import { getPosition } from 'core-app/shared/helpers/set-click-position/set-click-position'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import { displayClassName, DisplayFieldRenderer, - editFieldContainerClass -} from "core-app/shared/components/fields/display/display-field-renderer"; -import { States } from "core-app/core/states/states.service"; -import ClickEvent = JQuery.ClickEvent; + editFieldContainerClass, +} from 'core-app/shared/components/fields/display/display-field-renderer'; +import { States } from 'core-app/core/states/states.service'; +import { debugLog } from '../../../../helpers/debug_output'; +import { hasSelectionWithin } from '../../../../helpers/selection-helpers'; @Component({ selector: 'editable-attribute-field', changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './editable-attribute-field.component.html' + templateUrl: './editable-attribute-field.component.html', }) export class EditableAttributeFieldComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { @Input() public fieldName:string; + @Input() public resource:HalResource; + @Input() public wrapperClasses?:string; + @Input() public displayFieldOptions:any = {}; + @Input() public displayPlaceholder?:string; + @Input() public isDropTarget?:boolean = false; @ViewChild('displayContainer', { static: true }) readonly displayContainer:ElementRef; + @ViewChild('editContainer', { static: true }) readonly editContainer:ElementRef; public fieldRenderer:DisplayFieldRenderer; + public editFieldContainerClass = editFieldContainerClass; + public active = false; + private $element:JQuery; public destroyed = false; constructor(protected states:States, - protected injector:Injector, - protected elementRef:ElementRef, - protected ConfigurationService:ConfigurationService, - protected opContextMenu:OPContextMenuService, - protected halEditing:HalResourceEditingService, - protected schemaCache:SchemaCacheService, - // Get parent field group from injector if we're in a form - @Optional() protected editForm:EditFormComponent, - protected NotificationsService:NotificationsService, - protected cdRef:ChangeDetectorRef, - protected I18n:I18nService) { + protected injector:Injector, + protected elementRef:ElementRef, + protected ConfigurationService:ConfigurationService, + protected opContextMenu:OPContextMenuService, + protected halEditing:HalResourceEditingService, + protected schemaCache:SchemaCacheService, + // Get parent field group from injector if we're in a form + @Optional() protected editForm:EditFormComponent, + protected NotificationsService:NotificationsService, + protected cdRef:ChangeDetectorRef, + protected I18n:I18nService) { super(); } @@ -114,9 +122,9 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme .temporaryEditResource(this.resource) .values$() .pipe( - this.untilDestroyed() + this.untilDestroyed(), ) - .subscribe(resource => { + .subscribe((resource) => { this.resource = resource; this.render(); }); @@ -155,7 +163,7 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme public activateIfEditable(event:MouseEvent|KeyboardEvent) { // Ignore selections - if (SelectionHelpers.hasSelectionWithin(event.target as HTMLElement)) { + if (hasSelectionWithin(event.target as HTMLElement)) { debugLog(`Not activating ${this.fieldName} because of active selection within`); return true; } @@ -191,7 +199,7 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme if (evt?.type === 'click') { // Get the position where the user clicked. - positionOffset = ClickPositionMapper.getPosition(evt); + positionOffset = getPosition(evt); } this.activateOnForm() @@ -215,8 +223,7 @@ export class EditableAttributeFieldComponent extends UntilDestroyedMixin impleme private get schema() { if (this.halEditing.typedState(this.resource).hasValue()) { return this.halEditing.typedState(this.resource).value!.schema; - } else { - return this.schemaCache.of(this.resource) as ISchemaProxy; } + return this.schemaCache.of(this.resource); } } diff --git a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts index e5d26dbd82..85223cfa11 100644 --- a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts +++ b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts @@ -1,16 +1,14 @@ import { TestBed } from '@angular/core/testing'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; import { GlobalEditFormChangesTrackerService } from './global-edit-form-changes-tracker.service'; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; describe('GlobalEditFormChangesTrackerService', () => { let service:GlobalEditFormChangesTrackerService; - const createForm = (changed?:boolean) => { - return { - change: { - isEmpty: () => !changed - } - } as EditFormComponent; - }; + const createForm = (changed?:boolean) => ({ + change: { + isEmpty: () => !changed, + }, + } as EditFormComponent); beforeEach(() => { TestBed.configureTestingModule({}); diff --git a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts index 138f1175a2..07fa4924af 100644 --- a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts +++ b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.ts @@ -1,17 +1,15 @@ import { Injectable } from '@angular/core'; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class GlobalEditFormChangesTrackerService { private activeForms = new Map(); - get thereAreFormsWithUnsavedChanges () { - return Array.from(this.activeForms.keys()).some(form => { - return !form.change.isEmpty(); - }); + get thereAreFormsWithUnsavedChanges() { + return Array.from(this.activeForms.keys()).some((form) => !form.change.isEmpty()); } constructor( diff --git a/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts b/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts index 99cb5eab93..d5cd0d8455 100644 --- a/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts +++ b/frontend/src/app/shared/components/fields/edit/services/hal-resource-editing.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -26,17 +26,19 @@ // See docs/COPYRIGHT.rdoc for more details. //++ -import { combine, deriveRaw, InputState, multiInput, MultiInputState, State, StatesGroup } from 'reactivestates'; +import { + combine, deriveRaw, InputState, multiInput, State, StatesGroup, +} from 'reactivestates'; import { filter, map } from 'rxjs/operators'; import { Injectable, Injector } from '@angular/core'; -import { Subject } from "rxjs"; -import { FormResource } from "core-app/features/hal/resources/form-resource"; -import { ChangeMap } from "core-app/shared/components/fields/changeset/changeset"; -import { ResourceChangeset } from "core-app/shared/components/fields/changeset/resource-changeset"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { HookService } from "core-app/features/plugins/hook-service"; -import { HalEventsService } from "core-app/features/hal/services/hal-events.service"; -import { StateCacheService } from "core-app/core/apiv3/cache/state-cache.service"; +import { Subject } from 'rxjs'; +import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { ChangeMap } from 'core-app/shared/components/fields/changeset/changeset'; +import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/resource-changeset'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { HookService } from 'core-app/features/plugins/hook-service'; +import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; +import { StateCacheService } from 'core-app/core/apiv3/cache/state-cache.service'; class ChangesetStates extends StatesGroup { name = 'Changesets'; @@ -91,13 +93,12 @@ export interface ResourceChangesetClass { @Injectable() export class HalResourceEditingService extends StateCacheService { - /** Committed / saved changes to work packages observable */ public committedChanges = new Subject(); constructor(protected readonly injector:Injector, - protected readonly halEvents:HalEventsService, - protected readonly hook:HookService) { + protected readonly halEvents:HalEventsService, + protected readonly hook:HookService) { super(new ChangesetStates().changesets); } @@ -217,7 +218,7 @@ export class HalResourceEditingService extends StateCacheService} */ public temporaryEditResource>(resource:V):State { - const combined = combine(resource.state! as State, this.typedState(resource) as State); + const combined = combine(resource.state! as State, this.typedState(resource)); return deriveRaw(combined, ($) => $ @@ -230,9 +231,8 @@ export class HalResourceEditingService extends StateCacheService { fieldType:string; @@ -40,10 +40,10 @@ export abstract class AbstractFieldService field identifier */ - protected fields:{[attributeType:string]:string} = {}; + protected fields:{ [attributeType:string]:string } = {}; /** Registered field classes */ - protected classes:{[type:string]:C} = {}; + protected classes:{ [type:string]:C } = {}; /** * Get the field type for the given attribute type. @@ -68,8 +68,8 @@ export abstract class AbstractFieldService - this.I18n.t('js.editor.macro.attribute_reference.invalid_attribute', { name: attr }), + invalid_attribute: (attr:string) => this.I18n.t('js.editor.macro.attribute_reference.invalid_attribute', { name: attr }), }; @HostBinding('title') hostTitle = this.text.help; // The loaded resource, required for help text resource:HalResource|null = null; + // The scope to load for attribute help text attributeScope:string; + // The attribute name, normalized from schema attribute:string; + // The label to render label:string; constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly resourceLoader:AttributeModelLoaderService, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef) { + readonly injector:Injector, + readonly resourceLoader:AttributeModelLoaderService, + readonly schemaCache:SchemaCacheService, + readonly displayField:DisplayFieldService, + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef) { } @@ -99,7 +95,7 @@ export class AttributeLabelMacroComponent { const model:SupportedAttributeModels = element.dataset.model as any; const id:string = element.dataset.id!; const attributeName:string = element.dataset.attribute!; - this.attributeScope = StringHelpers.capitalize(model); + this.attributeScope = capitalize(model); this.loadResourceAttribute(model, id, attributeName); } @@ -110,7 +106,7 @@ export class AttributeLabelMacroComponent { try { this.resource = resource = await this.resourceLoader.require(model, id); } catch (e) { - console.error("Failed to render macro " + e); + console.error(`Failed to render macro ${e}`); return this.markError(this.text.not_found); } @@ -131,7 +127,7 @@ export class AttributeLabelMacroComponent { } markError(message:string) { - this.error = this.I18n.t('js.editor.macro.error', { message: message }); + this.error = this.I18n.t('js.editor.macro.error', { message }); this.cdRef.detectChanges(); } } diff --git a/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts b/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts index 914508ee5b..322a3e871d 100644 --- a/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts +++ b/frontend/src/app/shared/components/fields/macros/attribute-model-loader.service.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -24,25 +24,26 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See docs/COPYRIGHT.rdoc for more details. -//++ Ng1FieldControlsWrapper, - -import { Injectable } from "@angular/core"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { NEVER, Observable, throwError } from "rxjs"; -import { filter, map, take, tap } from "rxjs/operators"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { multiInput } from "reactivestates"; -import { TransitionService } from "@uirouter/core"; -import { CurrentProjectService } from "core-app/core/current-project/current-project.service"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; +// ++ Ng1FieldControlsWrapper, + +import { Injectable } from '@angular/core'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { NEVER, Observable, throwError } from 'rxjs'; +import { + filter, map, take, tap, +} from 'rxjs/operators'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { multiInput } from 'reactivestates'; +import { TransitionService } from '@uirouter/core'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; export type SupportedAttributeModels = 'project'|'workPackage'; -@Injectable({ providedIn: "root" }) +@Injectable({ providedIn: 'root' }) export class AttributeModelLoaderService { - text = { - not_found: this.I18n.t('js.editor.macro.attribute_reference.not_found') + not_found: this.I18n.t('js.editor.macro.attribute_reference.not_found'), }; // Cache the required model/id values because @@ -50,10 +51,9 @@ export class AttributeModelLoaderService { private cache$ = multiInput(); constructor(readonly apiV3Service:APIV3Service, - readonly transitions:TransitionService, - readonly currentProject:CurrentProjectService, - readonly I18n:I18nService) { - + readonly transitions:TransitionService, + readonly currentProject:CurrentProjectService, + readonly I18n:I18nService) { // Clear cached values whenever leaving the page transitions.onStart({}, () => { this.cache$.clear(); @@ -77,7 +77,7 @@ export class AttributeModelLoaderService { const promise = this .load(model, id) .pipe( - filter(response => !!response) + filter((response) => !!response), ) .toPromise(); state.clearAndPutFromPromise(promise as PromiseLike); @@ -89,19 +89,19 @@ export class AttributeModelLoaderService { .values$() .pipe( take(1), - tap(val => console.log("VAL " + val), err => console.error('ERR ' + err)) + tap((val) => console.log(`VAL ${val}`), (err) => console.error(`ERR ${err}`)), ) .toPromise(); } private load(model:SupportedAttributeModels, id?:string|undefined|null):Observable { switch (model) { - case 'workPackage': - return this.loadWorkPackage(id); - case 'project': - return this.loadProject(id); - default: - return NEVER; + case 'workPackage': + return this.loadWorkPackage(id); + case 'project': + return this.loadProject(id); + default: + return NEVER; } } @@ -118,7 +118,7 @@ export class AttributeModelLoaderService { .id(id) .get() .pipe( - take(1) + take(1), ); } @@ -135,7 +135,7 @@ export class AttributeModelLoaderService { .id(id) .get() .pipe( - take(1) + take(1), ); } @@ -148,7 +148,7 @@ export class AttributeModelLoaderService { .get() .pipe( take(1), - map(collection => collection.elements[0] || null) + map((collection) => collection.elements[0] || null), ); } } diff --git a/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts b/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts index 58ff918825..44d69d6241 100644 --- a/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts +++ b/frontend/src/app/shared/components/fields/macros/attribute-value-macro.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -24,7 +24,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See docs/COPYRIGHT.rdoc for more details. -//++ Ng1FieldControlsWrapper, +// ++ Ng1FieldControlsWrapper, import { ChangeDetectionStrategy, @@ -33,21 +33,18 @@ import { ElementRef, HostBinding, Injector, - ViewChild -} from "@angular/core"; -import { HalResource } from "core-app/features/hal/resources/hal-resource"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { NEVER, Observable } from "rxjs"; -import { filter, map, take, tap } from "rxjs/operators"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { IFieldSchema } from "core-app/shared/components/fields/field.base"; -import { I18nService } from "core-app/core/i18n/i18n.service"; + ViewChild, +} from '@angular/core'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AttributeModelLoaderService, - SupportedAttributeModels -} from "core-app/shared/components/fields/macros/attribute-model-loader.service"; + SupportedAttributeModels, +} from 'core-app/shared/components/fields/macros/attribute-model-loader.service'; export const attributeValueMacro = 'macro.macro--attribute-value'; @@ -57,8 +54,8 @@ export const attributeValueMacro = 'macro.macro--attribute-value'; styleUrls: ['./attribute-macro.sass'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ - HalResourceEditingService - ] + HalResourceEditingService, + ], }) export class AttributeValueMacroComponent { @ViewChild('displayContainer') private displayContainer:ElementRef; @@ -70,22 +67,22 @@ export class AttributeValueMacroComponent { help: this.I18n.t('js.editor.macro.attribute_reference.macro_help_tooltip'), placeholder: this.I18n.t('js.placeholders.default'), not_found: this.I18n.t('js.editor.macro.attribute_reference.not_found'), - invalid_attribute: (attr:string) => - this.I18n.t('js.editor.macro.attribute_reference.invalid_attribute', { name: attr }), + invalid_attribute: (attr:string) => this.I18n.t('js.editor.macro.attribute_reference.invalid_attribute', { name: attr }), }; @HostBinding('title') hostTitle = this.text.help; resource:HalResource; + fieldName:string; constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly resourceLoader:AttributeModelLoaderService, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef) { + readonly injector:Injector, + readonly resourceLoader:AttributeModelLoaderService, + readonly schemaCache:SchemaCacheService, + readonly displayField:DisplayFieldService, + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef) { } @@ -104,7 +101,7 @@ export class AttributeValueMacroComponent { try { resource = await this.resourceLoader.require(model, id); } catch (e) { - console.error("Failed to render macro " + e); + console.error(`Failed to render macro ${e}`); return this.markError(this.text.not_found); } @@ -128,7 +125,7 @@ export class AttributeValueMacroComponent { } markError(message:string) { - this.error = this.I18n.t('js.editor.macro.error', { message: message }); + this.error = this.I18n.t('js.editor.macro.error', { message }); this.cdRef.detectChanges(); } } diff --git a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts index 271fa6864e..f1b9c97f56 100644 --- a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts +++ b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.component.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -24,20 +24,26 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See docs/COPYRIGHT.rdoc for more details. -//++ Ng1FieldControlsWrapper, +// ++ Ng1FieldControlsWrapper, -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostBinding, Injector } from "@angular/core"; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Observable } from "rxjs"; -import { tap } from "rxjs/operators"; -import { SchemaCacheService } from "core-app/core/schemas/schema-cache.service"; -import { HalResourceEditingService } from "core-app/shared/components/fields/edit/services/hal-resource-editing.service"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; -import { WorkPackageResource } from "core-app/features/hal/resources/work-package-resource"; -import { DateDisplayField } from "core-app/shared/components/fields/display/field-types/date-display-field.module"; -import { CombinedDateDisplayField } from "core-app/shared/components/fields/display/field-types/combined-date-display.field"; -import { PathHelperService } from "core-app/core/path-helper/path-helper.service"; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + HostBinding, + Injector, +} from '@angular/core'; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; +import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; +import { CombinedDateDisplayField } from 'core-app/shared/components/fields/display/field-types/combined-date-display.field'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; export const quickInfoMacroSelector = 'macro.macro--wp-quickinfo'; @@ -47,8 +53,8 @@ export const quickInfoMacroSelector = 'macro.macro--wp-quickinfo'; styleUrls: ['./work-package-quickinfo-macro.sass'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ - HalResourceEditingService - ] + HalResourceEditingService, + ], }) export class WorkPackageQuickinfoMacroComponent { // Whether the value could not be loaded @@ -56,25 +62,28 @@ export class WorkPackageQuickinfoMacroComponent { text = { not_found: this.I18n.t('js.editor.macro.attribute_reference.not_found'), - help: this.I18n.t('js.editor.macro.attribute_reference.macro_help_tooltip') + help: this.I18n.t('js.editor.macro.attribute_reference.macro_help_tooltip'), }; @HostBinding('title') hostTitle = this.text.help; /** Work package to be shown */ workPackage$:Observable; + dateDisplayField = CombinedDateDisplayField; + workPackageLink:string; + detailed = false; constructor(readonly elementRef:ElementRef, - readonly injector:Injector, - readonly apiV3Service:APIV3Service, - readonly schemaCache:SchemaCacheService, - readonly displayField:DisplayFieldService, - readonly pathHelper:PathHelperService, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef) { + readonly injector:Injector, + readonly apiV3Service:APIV3Service, + readonly schemaCache:SchemaCacheService, + readonly displayField:DisplayFieldService, + readonly pathHelper:PathHelperService, + readonly I18n:I18nService, + readonly cdRef:ChangeDetectorRef) { } @@ -90,13 +99,13 @@ export class WorkPackageQuickinfoMacroComponent { .id(id) .get() .pipe( - tap({ error: (e) => this.markError(this.text.not_found) }) + tap({ error: (e) => this.markError(this.text.not_found) }), ); } markError(message:string) { - console.error("Failed to render macro " + message); - this.error = this.I18n.t('js.editor.macro.error', { message: message }); + console.error(`Failed to render macro ${message}`); + this.error = this.I18n.t('js.editor.macro.error', { message }); this.cdRef.detectChanges(); } } diff --git a/frontend/src/app/shared/components/fields/openproject-fields.module.ts b/frontend/src/app/shared/components/fields/openproject-fields.module.ts index a1f8638d1b..1b311487c1 100644 --- a/frontend/src/app/shared/components/fields/openproject-fields.module.ts +++ b/frontend/src/app/shared/components/fields/openproject-fields.module.ts @@ -1,4 +1,4 @@ -//-- copyright +// -- copyright // OpenProject is an open source project management software. // Copyright (C) 2012-2021 the OpenProject GmbH // @@ -27,40 +27,40 @@ //++ import { APP_INITIALIZER, NgModule } from '@angular/core'; -import { CommonModule } from "@angular/common"; -import { OpenprojectAccessibilityModule } from "core-app/shared/directives/a11y/openproject-a11y.module"; -import { OpenprojectModalModule } from "core-app/shared/components/modal/modal.module"; +import { CommonModule } from '@angular/common'; +import { OpenprojectAccessibilityModule } from 'core-app/shared/directives/a11y/openproject-a11y.module'; +import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; -import { OpenprojectAttachmentsModule } from "core-app/shared/components/attachments/openproject-attachments.module"; -import { OPSharedModule } from "core-app/shared/shared.module"; -import { AttributeHelpTextModule } from "core-app/shared/components/attribute-help-texts/attribute-help-text.module"; -import { EditFieldService } from "core-app/shared/components/fields/edit/edit-field.service"; -import { DisplayFieldService } from "core-app/shared/components/fields/display/display-field.service"; -import { initializeCoreEditFields } from "core-app/shared/components/fields/edit/edit-field.initializer"; -import { initializeCoreDisplayFields } from "core-app/shared/components/fields/display/display-field.initializer"; -import { DurationEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/duration-edit-field.component"; -import { FloatEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/float-edit-field.component"; -import { MultiSelectEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/multi-select-edit-field.component"; -import { EditFormPortalComponent } from "core-app/shared/components/fields/edit/editing-portal/edit-form-portal.component"; -import { SelectAutocompleterRegisterService } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service"; -import { EditFormComponent } from "core-app/shared/components/fields/edit/edit-form/edit-form.component"; -import { WorkPackageEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/work-package-edit-field.component"; -import { EditableAttributeFieldComponent } from "core-app/shared/components/fields/edit/field/editable-attribute-field.component"; -import { ProjectStatusEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/project-status-edit-field.component"; -import { PlainFormattableEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component"; -import { TimeEntryWorkPackageEditFieldComponent } from "core-app/shared/components/fields/edit/field-types/te-work-package-edit-field.component"; -import { AttributeValueMacroComponent } from "core-app/shared/components/fields/macros/attribute-value-macro.component"; -import { AttributeLabelMacroComponent } from "core-app/shared/components/fields/macros/attribute-label-macro.component"; -import { WorkPackageQuickinfoMacroComponent } from "core-app/shared/components/fields/macros/work-package-quickinfo-macro.component"; -import { DisplayFieldComponent } from "core-app/shared/components/fields/display/display-field.component"; -import { OpenprojectAutocompleterModule } from "core-app/shared/components/autocompleter/openproject-autocompleter.module"; -import { BooleanEditFieldModule } from "core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module"; -import { IntegerEditFieldModule } from "core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module"; -import { TextEditFieldModule } from "core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module"; -import { DateEditFieldModule } from "core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module"; -import { SelectEditFieldModule } from "core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module"; -import { FormattableEditFieldModule } from "core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module"; -import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/field-controls/edit-field-controls.module"; +import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; +import { OPSharedModule } from 'core-app/shared/shared.module'; +import { AttributeHelpTextModule } from 'core-app/shared/components/attribute-help-texts/attribute-help-text.module'; +import { EditFieldService } from 'core-app/shared/components/fields/edit/edit-field.service'; +import { DisplayFieldService } from 'core-app/shared/components/fields/display/display-field.service'; +import { initializeCoreEditFields } from 'core-app/shared/components/fields/edit/edit-field.initializer'; +import { initializeCoreDisplayFields } from 'core-app/shared/components/fields/display/display-field.initializer'; +import { DurationEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/duration-edit-field.component'; +import { FloatEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/float-edit-field.component'; +import { MultiSelectEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/multi-select-edit-field.component'; +import { EditFormPortalComponent } from 'core-app/shared/components/fields/edit/editing-portal/edit-form-portal.component'; +import { SelectAutocompleterRegisterService } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-autocompleter-register.service'; +import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; +import { WorkPackageEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/work-package-edit-field.component'; +import { EditableAttributeFieldComponent } from 'core-app/shared/components/fields/edit/field/editable-attribute-field.component'; +import { ProjectStatusEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/project-status-edit-field.component'; +import { PlainFormattableEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component'; +import { TimeEntryWorkPackageEditFieldComponent } from 'core-app/shared/components/fields/edit/field-types/te-work-package-edit-field.component'; +import { AttributeValueMacroComponent } from 'core-app/shared/components/fields/macros/attribute-value-macro.component'; +import { AttributeLabelMacroComponent } from 'core-app/shared/components/fields/macros/attribute-label-macro.component'; +import { WorkPackageQuickinfoMacroComponent } from 'core-app/shared/components/fields/macros/work-package-quickinfo-macro.component'; +import { DisplayFieldComponent } from 'core-app/shared/components/fields/display/display-field.component'; +import { OpenprojectAutocompleterModule } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; +import { BooleanEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.module'; +import { IntegerEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.module'; +import { TextEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.module'; +import { DateEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/date-edit-field/date-edit-field.module'; +import { SelectEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/select-edit-field/select-edit-field.module'; +import { FormattableEditFieldModule } from 'core-app/shared/components/fields/edit/field-types/formattable-edit-field/formattable-edit-field.module'; +import { EditFieldControlsModule } from 'core-app/shared/components/fields/edit/field-controls/edit-field-controls.module'; @NgModule({ imports: [ @@ -91,13 +91,13 @@ import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/ provide: APP_INITIALIZER, useFactory: initializeCoreEditFields, deps: [EditFieldService, SelectAutocompleterRegisterService], - multi: true + multi: true, }, { provide: APP_INITIALIZER, useFactory: initializeCoreDisplayFields, deps: [DisplayFieldService], - multi: true + multi: true, }, ], declarations: [ @@ -116,8 +116,7 @@ import { EditFieldControlsModule } from "core-app/shared/components/fields/edit/ AttributeLabelMacroComponent, WorkPackageQuickinfoMacroComponent, - ] + ], }) export class OpenprojectFieldsModule { } - diff --git a/frontend/src/app/shared/components/forms/form-field/form-binding.directive.ts b/frontend/src/app/shared/components/forms/form-field/form-binding.directive.ts index 41aa1df973..2695bd21c0 100644 --- a/frontend/src/app/shared/components/forms/form-field/form-binding.directive.ts +++ b/frontend/src/app/shared/components/forms/form-field/form-binding.directive.ts @@ -1,18 +1,11 @@ +import { Directive, forwardRef, Input } from '@angular/core'; import { - Directive, - forwardRef, - Input, -} from '@angular/core'; -import { - NgControl, - FormControl, - FormGroup, - FormArray, + FormArray, FormControl, FormGroup, NgControl, } from '@angular/forms'; export const formControlBinding:any = { provide: NgControl, - useExisting: forwardRef(() => OpFormBindingDirective) + useExisting: forwardRef(() => OpFormBindingDirective), }; @Directive({ diff --git a/frontend/src/app/shared/components/forms/form-field/form-field.component.ts b/frontend/src/app/shared/components/forms/form-field/form-field.component.ts index bc1cb1e8dc..ab9209792d 100644 --- a/frontend/src/app/shared/components/forms/form-field/form-field.component.ts +++ b/frontend/src/app/shared/components/forms/form-field/form-field.component.ts @@ -1,15 +1,7 @@ import { - Component, - Input, - HostBinding, - ContentChild, - Optional, -} from "@angular/core"; -import { - NgControl, - AbstractControl, - FormGroupDirective, -} from "@angular/forms"; + Component, ContentChild, HostBinding, Input, Optional, +} from '@angular/core'; +import { AbstractControl, FormGroupDirective, NgControl } from '@angular/forms'; @Component({ selector: 'op-form-field', @@ -17,17 +9,25 @@ import { }) export class OpFormFieldComponent { @HostBinding('class.op-form-field') className = true; + @HostBinding('class.op-form-field_invalid') get errorClassName() { return this.showErrorMessage; } @Input() label = ''; + @Input() noWrapLabel = true; + @Input() required = false; + @Input() hidden = false; + @Input() showValidationErrorOn:'change' | 'blur' | 'submit' | 'never' = 'submit'; + @Input() control?:AbstractControl; + @Input() helpTextAttribute?:string; + @Input() helpTextAttributeScope?:string; @ContentChild(NgControl) ngControl:NgControl; @@ -57,9 +57,9 @@ export class OpFormFieldComponent { if (this.showValidationErrorOn === 'submit') { return this.formControl.invalid && this._formGroupDirective?.submitted; - } else if (this.showValidationErrorOn === 'blur') { + } if (this.showValidationErrorOn === 'blur') { return this.formControl.invalid && this.formControl.touched; - } else if (this.showValidationErrorOn === 'change') { + } if (this.showValidationErrorOn === 'change') { return this.formControl.invalid && this.formControl.dirty; } diff --git a/frontend/src/app/shared/components/grids/areas/grid-area.ts b/frontend/src/app/shared/components/grids/areas/grid-area.ts index b60bc1333a..6e0ca760ad 100644 --- a/frontend/src/app/shared/components/grids/areas/grid-area.ts +++ b/frontend/src/app/shared/components/grids/areas/grid-area.ts @@ -1,8 +1,12 @@ export class GridArea { private storedGuid:string; + public startRow:number; + public endRow:number; + public startColumn:number; + public endColumn:number; constructor(startRow:number, endRow:number, startColumn:number, endColumn:number) { @@ -42,7 +46,6 @@ export class GridArea { .toString(16) .substring(1); } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); + return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; } } - diff --git a/frontend/src/app/shared/components/grids/areas/grid-gap.ts b/frontend/src/app/shared/components/grids/areas/grid-gap.ts index de3e41ea53..4232078cb5 100644 --- a/frontend/src/app/shared/components/grids/areas/grid-gap.ts +++ b/frontend/src/app/shared/components/grids/areas/grid-gap.ts @@ -1,4 +1,4 @@ -import { GridArea } from "core-app/shared/components/grids/areas/grid-area"; +import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; export class GridGap extends GridArea { private type:'row'|'column'; @@ -12,33 +12,29 @@ export class GridGap extends GridArea { public get gridStartRow() { if (this.isRow) { return this.startRow * 2 - 1; - } else { - return this.startRow * 2; } + return this.startRow * 2; } public get gridEndRow() { if (this.isRow) { return this.endRow * 2 - 2; - } else { - return this.endRow * 2 - 1; } + return this.endRow * 2 - 1; } public get gridStartColumn() { if (this.isRow) { return this.startColumn * 2; - } else { - return this.startColumn * 2 - 1; } + return this.startColumn * 2 - 1; } public get gridEndColumn() { if (this.isRow) { return this.endColumn * 2 - 1; - } else { - return this.endColumn * 2 - 2; } + return this.endColumn * 2 - 2; } public get isRow() { diff --git a/frontend/src/app/shared/components/grids/areas/grid-widget-area.ts b/frontend/src/app/shared/components/grids/areas/grid-widget-area.ts index cb42c59b46..c46d9d8312 100644 --- a/frontend/src/app/shared/components/grids/areas/grid-widget-area.ts +++ b/frontend/src/app/shared/components/grids/areas/grid-widget-area.ts @@ -1,5 +1,5 @@ -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { GridArea } from "core-app/shared/components/grids/areas/grid-area"; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; export class GridWidgetArea extends GridArea { public widget:GridWidgetResource; @@ -35,39 +35,39 @@ export class GridWidgetArea extends GridArea { } public overlaps(otherArea:GridWidgetArea) { - return this.rowOverlaps(otherArea) && - this.columnOverlaps(otherArea); + return this.rowOverlaps(otherArea) + && this.columnOverlaps(otherArea); } public rowOverlaps(otherArea:GridWidgetArea) { - return this.startRow < otherArea.endRow && - this.endRow >= otherArea.endRow || - this.startRow <= otherArea.startRow && - this.endRow > otherArea.startRow || - this.startRow > otherArea.startRow && - this.endRow < otherArea.endRow; + return this.startRow < otherArea.endRow + && this.endRow >= otherArea.endRow + || this.startRow <= otherArea.startRow + && this.endRow > otherArea.startRow + || this.startRow > otherArea.startRow + && this.endRow < otherArea.endRow; } public columnOverlaps(otherArea:GridWidgetArea) { - return this.startColumn < otherArea.endColumn && - this.endColumn >= otherArea.endColumn || - this.startColumn <= otherArea.startColumn && - this.endColumn > otherArea.startColumn || - this.startColumn > otherArea.startColumn && - this.endColumn < otherArea.endColumn; + return this.startColumn < otherArea.endColumn + && this.endColumn >= otherArea.endColumn + || this.startColumn <= otherArea.startColumn + && this.endColumn > otherArea.startColumn + || this.startColumn > otherArea.startColumn + && this.endColumn < otherArea.endColumn; } public startColumnOverlaps(otherArea:GridWidgetArea) { - return this.startColumn < otherArea.startColumn && - this.endColumn > otherArea.startColumn && - this.rowOverlaps(otherArea); + return this.startColumn < otherArea.startColumn + && this.endColumn > otherArea.startColumn + && this.rowOverlaps(otherArea); } public get unchangedSize() { - return this.startColumn === this.widget.startColumn && - this.endColumn === this.widget.endColumn && - this.startRow === this.widget.startRow && - this.endRow === this.widget.endRow; + return this.startColumn === this.widget.startColumn + && this.endColumn === this.widget.endColumn + && this.startRow === this.widget.startRow + && this.endRow === this.widget.endRow; } public writeAreaChangeToWidget() { diff --git a/frontend/src/app/shared/components/grids/grid/add-widget.service.ts b/frontend/src/app/shared/components/grids/grid/add-widget.service.ts index 46189beb02..b6d84713de 100644 --- a/frontend/src/app/shared/components/grids/grid/add-widget.service.ts +++ b/frontend/src/app/shared/components/grids/grid/add-widget.service.ts @@ -1,44 +1,42 @@ -import { Injectable, Injector } from "@angular/core"; -import { OpModalService } from "core-app/shared/components/modal/modal.service"; -import { AddGridWidgetModal } from "core-app/shared/components/grids/widgets/add/add.modal"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { GridArea } from "core-app/shared/components/grids/areas/grid-area"; -import { HalResourceService } from "core-app/features/hal/services/hal-resource.service"; -import { GridWidgetArea } from "core-app/shared/components/grids/areas/grid-widget-area"; -import { GridAreaService } from "core-app/shared/components/grids/grid/area.service"; -import { GridDragAndDropService } from "core-app/shared/components/grids/grid/drag-and-drop.service"; -import { GridResizeService } from "core-app/shared/components/grids/grid/resize.service"; -import { GridMoveService } from "core-app/shared/components/grids/grid/move.service"; -import { GridGap } from "core-app/shared/components/grids/areas/grid-gap"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { Injectable, Injector } from '@angular/core'; +import { OpModalService } from 'core-app/shared/components/modal/modal.service'; +import { AddGridWidgetModalComponent } from 'core-app/shared/components/grids/widgets/add/add.modal'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; +import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; +import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; +import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; +import { GridDragAndDropService } from 'core-app/shared/components/grids/grid/drag-and-drop.service'; +import { GridResizeService } from 'core-app/shared/components/grids/grid/resize.service'; +import { GridMoveService } from 'core-app/shared/components/grids/grid/move.service'; +import { GridGap } from 'core-app/shared/components/grids/areas/grid-gap'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; @Injectable() export class GridAddWidgetService { - text = { add: this.i18n.t('js.grid.add_widget') }; constructor(readonly opModalService:OpModalService, - readonly injector:Injector, - readonly halResource:HalResourceService, - readonly layout:GridAreaService, - readonly drag:GridDragAndDropService, - readonly move:GridMoveService, - readonly resize:GridResizeService, - readonly i18n:I18nService) { + readonly injector:Injector, + readonly halResource:HalResourceService, + readonly layout:GridAreaService, + readonly drag:GridDragAndDropService, + readonly move:GridMoveService, + readonly resize:GridResizeService, + readonly i18n:I18nService) { } public isAddable(area:GridArea) { - return !this.drag.currentlyDragging && - !this.resize.currentlyResizing && - (this.layout.mousedOverArea === area || this.layout.isSingleCell || this.layout.inHelpMode) && - this.isAllowed; + return !this.drag.currentlyDragging + && !this.resize.currentlyResizing + && (this.layout.mousedOverArea === area || this.layout.isSingleCell || this.layout.inHelpMode) + && this.isAllowed; } public widget(area:GridArea) { this .select(area) .then((widgetResource) => { - if (this.layout.isGap(area)) { this.addLine(area as GridGap); } @@ -60,8 +58,8 @@ export class GridAddWidgetService { private select(area:GridArea) { return new Promise((resolve, reject) => { - const modal = this.opModalService.show(AddGridWidgetModal, this.injector, { schema: this.layout.schema }); - modal.closingEvent.subscribe((modal:AddGridWidgetModal) => { + const modal = this.opModalService.show(AddGridWidgetModalComponent, this.injector, { schema: this.layout.schema }); + modal.closingEvent.subscribe((modal:AddGridWidgetModalComponent) => { const registered = modal.chosenWidget; if (!registered) { @@ -76,10 +74,10 @@ export class GridAddWidgetService { endRow: area.endRow, startColumn: area.startColumn, endColumn: area.endColumn, - options: registered.properties || {} + options: registered.properties || {}, }; - const resource = this.halResource.createHalResource(source) as GridWidgetResource; + const resource:GridWidgetResource = this.halResource.createHalResource(source); resource.grid = this.layout.gridResource; diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index bd5a32394e..2cab8d4075 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -1,37 +1,44 @@ import { Injectable } from '@angular/core'; -import { GridWidgetArea } from "core-app/shared/components/grids/areas/grid-widget-area"; -import { GridArea } from "core-app/shared/components/grids/areas/grid-area"; -import { GridGap } from "core-app/shared/components/grids/areas/grid-gap"; -import { GridResource } from "core-app/features/hal/resources/grid-resource"; -import { GridWidgetResource } from "core-app/features/hal/resources/grid-widget-resource"; -import { SchemaResource } from "core-app/features/hal/resources/schema-resource"; -import { WidgetChangeset } from "core-app/shared/components/grids/widgets/widget-changeset"; -import { NotificationsService } from "core-app/shared/components/notifications/notifications.service"; -import { I18nService } from "core-app/core/i18n/i18n.service"; +import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; +import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; +import { GridGap } from 'core-app/shared/components/grids/areas/grid-gap'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; +import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; +import { WidgetChangeset } from 'core-app/shared/components/grids/widgets/widget-changeset'; +import { NotificationsService } from 'core-app/shared/components/notifications/notifications.service'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; import { BehaviorSubject } from 'rxjs'; -import { APIV3Service } from "core-app/core/apiv3/api-v3.service"; -import { Apiv3GridForm } from "core-app/core/apiv3/endpoints/grids/apiv3-grid-form"; -import { map } from "rxjs/operators"; +import { APIV3Service } from 'core-app/core/apiv3/api-v3.service'; +import { Apiv3GridForm } from 'core-app/core/apiv3/endpoints/grids/apiv3-grid-form'; @Injectable() export class GridAreaService { - private resource:GridResource; + public schema:SchemaResource; public numColumns = 0; + public numRows = 0; + public gridAreas:GridArea[]; + public gridGaps:GridArea[]; + public widgetAreas:GridWidgetArea[]; + public gridAreaIds:string[]; + public mousedOverArea:GridArea|null = null; + public $mousedOverArea = new BehaviorSubject(this.mousedOverArea); + public helpMode = false; - constructor (private apiV3Service:APIV3Service, - private notification:NotificationsService, - private i18n:I18nService) { } + constructor(private apiV3Service:APIV3Service, + private notification:NotificationsService, + private i18n:I18nService) { } public set gridResource(value:GridResource) { this.resource = value; @@ -57,11 +64,11 @@ export class GridAreaService { // array containing Numbers from this.numRows to 1 let unusedRows = _.range(this.numRows, 0, -1); - this.widgetAreas.forEach(widget => { - unusedRows = unusedRows.filter(item => item !== widget.startRow); + this.widgetAreas.forEach((widget) => { + unusedRows = unusedRows.filter((item) => item !== widget.startRow); }); - unusedRows.forEach(number => { + unusedRows.forEach((number) => { if (this.numRows > 1) { this.removeRow(number); } @@ -69,11 +76,11 @@ export class GridAreaService { let unusedColumns = _.range(this.numColumns, 0, -1); - this.widgetAreas.forEach(widget => { - unusedColumns = unusedColumns.filter(item => item !== widget.startColumn); + this.widgetAreas.forEach((widget) => { + unusedColumns = unusedColumns.filter((item) => item !== widget.startColumn); }); - unusedColumns.forEach(number => { + unusedColumns.forEach((number) => { if (this.numColumns > 1) { this.removeColumn(number); } @@ -95,7 +102,7 @@ export class GridAreaService { } public persist() { - this.resource.rowCount = this.numRows = (this.widgetAreas.map(area => area.endRow).sort((a, b) => a - b).pop() || 2) - 1; + this.resource.rowCount = this.numRows = (this.widgetAreas.map((area) => area.endRow).sort((a, b) => a - b).pop() || 2) - 1; this.resource.columnCount = this.numColumns; this.writeAreaChangesToWidgets(); @@ -110,7 +117,7 @@ export class GridAreaService { Object.assign(payloadWidget, changeset.changes); // Adding the id so that the url can be deduced - payload['id'] = this.resource.id; + payload.id = this.resource.id; this.saveGrid(payload); } @@ -149,7 +156,7 @@ export class GridAreaService { .grids .id(resource) .patch(resource, schema) - .subscribe(updatedGrid => { + .subscribe((updatedGrid) => { this.assignAreasWidget(updatedGrid); this.notification.addSuccess(this.i18n.t('js.notice_successful_update')); }); @@ -158,17 +165,17 @@ export class GridAreaService { private assignAreasWidget(newGrid:GridResource) { this.resource.widgets = newGrid.widgets; - const takenIds = this.widgetAreas.map(a => a.widget.id); - this.widgetAreas.forEach(area => { + const takenIds = this.widgetAreas.map((a) => a.widget.id); + this.widgetAreas.forEach((area) => { let newWidget:GridWidgetResource; // identify the right resource for the area. Typically that means fetching them by id. // But new areas have unpersisted resources at first. Unpersisted resources have no id. // In those cases, we find the one resource which is not claimed by any other area. if (area.widget.id) { - newWidget = newGrid.widgets.find(widget => widget.id === area.widget.id)!; + newWidget = newGrid.widgets.find((widget) => widget.id === area.widget.id)!; } else { - newWidget = newGrid.widgets.find(widget => !takenIds.includes(widget.id) && widget.identifier === area.widget.identifier && widget.startRow === area.widget.startRow && widget.startColumn === area.widget.startColumn)!; + newWidget = newGrid.widgets.find((widget) => !takenIds.includes(widget.id) && widget.identifier === area.widget.identifier && widget.startRow === area.widget.startRow && widget.startColumn === area.widget.startColumn)!; } area.widget = newWidget!; @@ -241,9 +248,7 @@ export class GridAreaService { } private buildGridWidgetAreas() { - return this.widgetResources.map((widget) => { - return new GridWidgetArea(widget); - }); + return this.widgetResources.map((widget) => new GridWidgetArea(widget)); } // persist all changes to the areas caused by dragging/resizing @@ -267,10 +272,10 @@ export class GridAreaService { const widget = this .rowWidgets(row) .sort((a, b) => a.startColumn - b.startColumn) - .find(widget => !(widget.startRow < excludeRow && widget.endRow > excludeRow) && - (widget.startColumn === column + 1 || - widget.endColumn === column + 1 || - widget.startColumn <= column && widget.endColumn > column)); + .find((widget) => !(widget.startRow < excludeRow && widget.endRow > excludeRow) + && (widget.startColumn === column + 1 + || widget.endColumn === column + 1 + || widget.startColumn <= column && widget.endColumn > column)); if (widget) { movedWidgets.push(widget); @@ -278,7 +283,7 @@ export class GridAreaService { } } - this.moveSubsequentRowWidgets(this.widgetAreas.filter(widget => !movedWidgets.includes(widget)), + this.moveSubsequentRowWidgets(this.widgetAreas.filter((widget) => !movedWidgets.includes(widget)), column); } @@ -295,10 +300,10 @@ export class GridAreaService { const widget = this .columnWidgets(column) .sort((a, b) => a.startRow - b.startRow) - .find(widget => !(widget.startColumn < excludeColumn && widget.endColumn > excludeColumn) && - (widget.startRow === row + 1 || - widget.endRow === row + 1 || - widget.startRow <= row && widget.endRow > row)); + .find((widget) => !(widget.startColumn < excludeColumn && widget.endColumn > excludeColumn) + && (widget.startRow === row + 1 + || widget.endRow === row + 1 + || widget.startRow <= row && widget.endRow > row)); if (widget) { movedWidgets.push(widget); @@ -306,26 +311,24 @@ export class GridAreaService { } } - this.moveSubsequentColumnWidgets(this.widgetAreas.filter(widget => !movedWidgets.includes(widget)), + this.moveSubsequentColumnWidgets(this.widgetAreas.filter((widget) => !movedWidgets.includes(widget)), row); } public removeColumn(column:number) { this.numColumns--; - //shrink widgets that span more than the removed column + // shrink widgets that span more than the removed column this.widgetAreas.forEach((widget) => { if (widget.startColumn <= column && widget.endColumn >= column + 1) { - //shrink widgets that span more than the removed column + // shrink widgets that span more than the removed column widget.endColumn--; } }); // move all widgets that are after the removed column // so that they appear to keep their place. - this.widgetAreas.filter((widget) => { - return widget.startColumn > column; - }).forEach((widget) => { + this.widgetAreas.filter((widget) => widget.startColumn > column).forEach((widget) => { widget.startColumn--; widget.endColumn--; }); @@ -334,28 +337,24 @@ export class GridAreaService { public removeRow(row:number) { this.numRows--; - //shrink widgets that span more than the removed row + // shrink widgets that span more than the removed row this.widgetAreas.forEach((widget) => { if (widget.startRow <= row && widget.endRow >= row + 1) { - //shrink widgets that span more than the removed row + // shrink widgets that span more than the removed row widget.endRow--; } }); // move all widgets that are after the removed row // so that they appear to keep their place. - this.widgetAreas.filter((widget) => { - return widget.startRow > row; - }).forEach((widget) => { + this.widgetAreas.filter((widget) => widget.startRow > row).forEach((widget) => { widget.startRow--; widget.endRow--; }); } public resetAreas(ignoredArea:GridWidgetArea|null = null) { - this.widgetAreas.filter((area) => { - return !ignoredArea || area.guid !== ignoredArea.guid; - }).forEach(area => area.reset()); + this.widgetAreas.filter((area) => !ignoredArea || area.guid !== ignoredArea.guid).forEach((area) => area.reset()); this.numRows = this.resource.rowCount; this.numColumns = this.resource.columnCount; @@ -368,7 +367,7 @@ export class GridAreaService { private buildGridAreaIds() { return this .gridAreas - .filter(area => !this.isGap(area)) + .filter((area) => !this.isGap(area)) .map((area) => area.guid); } @@ -379,11 +378,11 @@ export class GridAreaService { .id(this.resource) .form .post({}) - .subscribe(form => this.schema = form.schema); + .subscribe((form) => this.schema = form.schema); } public removeWidget(removedWidget:GridWidgetResource) { - let index = this.resource.widgets.findIndex((widget) => widget.id === removedWidget.id ); + let index = this.resource.widgets.findIndex((widget) => widget.id === removedWidget.id); this.resource.widgets.splice(index, 1); index = this.widgetAreas.findIndex((area) => area.widget.id === removedWidget.id); @@ -398,11 +397,11 @@ export class GridAreaService { } private rowWidgets(row:number) { - return this.widgetAreas.filter(widget => widget.startRow === row); + return this.widgetAreas.filter((widget) => widget.startRow === row); } private moveSubsequentRowWidgets(rowWidgets:GridWidgetArea[], column:number) { - rowWidgets.forEach(subsequentWidget => { + rowWidgets.forEach((subsequentWidget) => { if (subsequentWidget.startColumn > column) { subsequentWidget.startColumn++; subsequentWidget.endColumn++; @@ -411,11 +410,11 @@ export class GridAreaService { } private columnWidgets(column:number) { - return this.widgetAreas.filter(widget => widget.startColumn === column); + return this.widgetAreas.filter((widget) => widget.startColumn === column); } private moveSubsequentColumnWidgets(columnWidgets:GridWidgetArea[], row:number) { - columnWidgets.forEach(subsequentWidget => { + columnWidgets.forEach((subsequentWidget) => { if (subsequentWidget.startRow > row) { subsequentWidget.startRow++; subsequentWidget.endRow++; diff --git a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts index db03f02fda..7ef4d273c4 100644 --- a/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts +++ b/frontend/src/app/shared/components/grids/grid/drag-and-drop.service.ts @@ -1,20 +1,23 @@ import { Injectable, OnDestroy } from '@angular/core'; -import { GridWidgetArea } from "core-app/shared/components/grids/areas/grid-widget-area"; -import { GridArea } from "core-app/shared/components/grids/areas/grid-area"; -import { GridAreaService } from "core-app/shared/components/grids/grid/area.service"; -import { GridMoveService } from "core-app/shared/components/grids/grid/move.service"; +import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; +import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; +import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; +import { GridMoveService } from 'core-app/shared/components/grids/grid/move.service'; import { Subscription } from 'rxjs'; -import { filter, distinctUntilChanged, throttleTime } from 'rxjs/operators'; +import { distinctUntilChanged, filter, throttleTime } from 'rxjs/operators'; @Injectable() export class GridDragAndDropService implements OnDestroy { public draggedArea:GridWidgetArea|null; + public placeholderArea:GridWidgetArea|null; + public draggedHeight:number|null; + private mousedOverAreaObserver:Subscription; constructor(readonly layout:GridAreaService, - readonly move:GridMoveService) { + readonly move:GridMoveService) { // ngOnInit is not called on services this.setupMousedOverAreaSubscription(); } @@ -32,7 +35,7 @@ export class GridDragAndDropService implements OnDestroy { throttleTime(10), distinctUntilChanged(), filter((area) => this.currentlyDragging && !!area && !this.layout.isGap(area) && (this.placeholderArea!.startRow !== area.startRow || this.placeholderArea!.startColumn !== area.startColumn)), - ).subscribe(area => { + ).subscribe((area) => { this.updateArea(area!); this.layout.scrollPlaceholderIntoView(); @@ -99,9 +102,9 @@ export class GridDragAndDropService implements OnDestroy { return; } - this.placeholderArea!.copyDimensionsTo(this.draggedArea!); + this.placeholderArea!.copyDimensionsTo(this.draggedArea); - if (!this.draggedArea!.unchangedSize) { + if (!this.draggedArea.unchangedSize) { this.layout.writeAreaChangesToWidgets(); this.layout.cleanupUnusedAreas(); this.layout.rebuildAndPersist(); @@ -131,5 +134,4 @@ export class GridDragAndDropService implements OnDestroy { sink.endColumn = source.startColumn + sink.widget.width; } } - } diff --git a/frontend/src/app/shared/components/grids/grid/grid.component.html b/frontend/src/app/shared/components/grids/grid/grid.component.html index 6ffd5d7ec1..3ef1731f8c 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.html +++ b/frontend/src/app/shared/components/grids/grid/grid.component.html @@ -39,7 +39,7 @@ [customHandler]="true" (end)="resize.end(area)" (start)="resize.start(area)" - (move)="resize.moving($event)"> + (move)="resize.moving()">