diff --git a/package-lock.json b/package-lock.json index 4a3f7491..0a1e5d27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clearml-webapp", - "version": "1.9.0", + "version": "1.9.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "clearml-webapp", - "version": "1.9.0", + "version": "1.9.2", "dependencies": { "@angular/animations": "^14.2.12", "@angular/cdk": "^14.2.7", @@ -57,6 +57,7 @@ "process": "^0.11.10", "rxjs": "^7.5.7", "string-to-color": "^2.2.2", + "tinycolor2": "^1.5.2", "tslib": "^2.4.1", "url": "^0.11.0", "uuid": "^9.0.0", @@ -6806,16 +6807,6 @@ "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -9471,36 +9462,6 @@ "node": ">=8" } }, - "node_modules/findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==", - "dev": true, - "peer": true, - "dependencies": { - "glob": "~5.0.0" - }, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/findup-sync/node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "dev": true, - "peer": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -9540,15 +9501,6 @@ } } }, - "node_modules/font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", - "peer": true, - "engines": { - "node": ">=0.10.3" - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -10929,12 +10881,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jquery": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz", - "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==", - "peer": true - }, "node_modules/js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -12353,24 +12299,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", - "dev": true, - "peer": true, - "dependencies": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "node_modules/optimist/node_modules/minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", - "dev": true, - "peer": true - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -12805,17 +12733,6 @@ "node": ">=8" } }, - "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, "node_modules/postcss": { "version": "8.4.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", @@ -15094,6 +15011,11 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "node_modules/tinycolor2": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz", + "integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg==" + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -15174,38 +15096,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, - "node_modules/tslint": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz", - "integrity": "sha512-wkqXlDiU1qG31dMuxnCSNeNMdKmSaEMYgJ2RERgFkt1WvVEF/wYwUYR396DDDcJDDBYpq16a6XJodQh70IRtBQ==", - "dev": true, - "peer": true, - "dependencies": { - "colors": "^1.1.2", - "diff": "^2.2.1", - "findup-sync": "~0.3.0", - "glob": "^7.0.3", - "optimist": "~0.6.0", - "resolve": "^1.1.7", - "underscore.string": "^3.3.4" - }, - "bin": { - "tslint": "bin/tslint" - }, - "peerDependencies": { - "typescript": ">=1.7.3" - } - }, - "node_modules/tslint/node_modules/diff": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", - "integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -15298,20 +15188,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore.string": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", - "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "^1.1.1", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": "*" - } - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -15881,16 +15757,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -16367,8 +16233,7 @@ "version": "14.4.0", "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-14.4.0.tgz", "integrity": "sha512-AhAUFvSg0urtb6Lsowvuxwu6DMXUy0BPwrnfNOBGjRt9vG7F9kgXXAsm5DnIS0GNy/mLZ9mSfa86fv++1e0KUA==", - "dev": true, - "requires": {} + "dev": true }, "@angular-eslint/bundled-angular-compiler": { "version": "14.4.0", @@ -19243,15 +19108,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "dev": true, - "requires": {} + "dev": true }, "@csstools/selector-specificity": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", - "dev": true, - "requires": {} + "dev": true }, "@discoveryjs/json-ext": { "version": "0.5.7", @@ -19524,8 +19387,7 @@ "version": "14.2.10", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.10.tgz", "integrity": "sha512-sLHapZLVub6mEz5b19tf1VfIV1w3tYfg7FNPLeni79aldxu1FbP1v2WmiFAnMzrswqyK0bhTtxrl+Z/CLKqyoQ==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -20168,15 +20030,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -20637,8 +20497,7 @@ "bootstrap": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", - "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", - "requires": {} + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==" }, "bowser": { "version": "2.11.0", @@ -20951,13 +20810,6 @@ "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "peer": true - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -21297,8 +21149,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true, - "requires": {} + "dev": true }, "css-select": { "version": "4.2.1", @@ -22579,8 +22430,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", - "dev": true, - "requires": {} + "dev": true }, "eslint-scope": { "version": "5.1.1", @@ -22951,32 +22801,6 @@ "path-exists": "^4.0.0" } }, - "findup-sync": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", - "integrity": "sha512-z8Nrwhi6wzxNMIbxlrTzuUW6KWuKkogZ/7OdDVq+0+kxn77KUH1nipx8iU6suqkHqc4y6n7a9A8IpmxY/pTjWg==", - "dev": true, - "peer": true, - "requires": { - "glob": "~5.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "dev": true, - "peer": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -22999,12 +22823,6 @@ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, - "font-awesome": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", - "integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==", - "peer": true - }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -23464,8 +23282,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -24009,12 +23826,6 @@ } } }, - "jquery": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz", - "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==", - "peer": true - }, "js-sdsl": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", @@ -25074,26 +24885,6 @@ "is-wsl": "^2.2.0" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==", - "dev": true, - "peer": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==", - "dev": true, - "peer": true - } - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -25434,12 +25225,6 @@ "find-up": "^4.0.0" } }, - "popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "peer": true - }, "postcss": { "version": "8.4.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", @@ -25573,15 +25358,13 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-image-set-function": { "version": "4.0.7", @@ -25607,8 +25390,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-lab-function": { "version": "4.2.1", @@ -25635,22 +25417,19 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true, - "requires": {} + "dev": true }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true, - "requires": {} + "dev": true }, "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==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -25710,8 +25489,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-place": { "version": "7.0.5", @@ -25792,8 +25570,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-selector-not": { "version": "6.0.1", @@ -26346,8 +26123,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -26966,8 +26742,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -27023,6 +26798,11 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "tinycolor2": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.5.2.tgz", + "integrity": "sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg==" + }, "tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -27087,31 +26867,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, - "tslint": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz", - "integrity": "sha512-wkqXlDiU1qG31dMuxnCSNeNMdKmSaEMYgJ2RERgFkt1WvVEF/wYwUYR396DDDcJDDBYpq16a6XJodQh70IRtBQ==", - "dev": true, - "peer": true, - "requires": { - "colors": "^1.1.2", - "diff": "^2.2.1", - "findup-sync": "~0.3.0", - "glob": "^7.0.3", - "optimist": "~0.6.0", - "resolve": "^1.1.7", - "underscore.string": "^3.3.4" - }, - "dependencies": { - "diff": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", - "integrity": "sha512-9wfm3RLzMp/PyTFWuw9liEzdlxsdGixCW0ZTU1XDmtlAkvpVXTPGF8KnfSs0hm3BPbg19OrUPPsRkHXoREpP1g==", - "dev": true, - "peer": true - } - } - }, "tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -27178,17 +26933,6 @@ "which-boxed-primitive": "^1.0.2" } }, - "underscore.string": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", - "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", - "dev": true, - "peer": true, - "requires": { - "sprintf-js": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -27394,8 +27138,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "json-schema-traverse": { "version": "0.4.1", @@ -27605,13 +27348,6 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", - "dev": true, - "peer": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -27659,8 +27395,7 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "requires": {} + "dev": true }, "xhr2": { "version": "0.2.1", diff --git a/package.json b/package.json index ce97a530..0a26fbac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clearml-webapp", - "version": "1.9.0", + "version": "1.9.2", "license": "", "scripts": { "ng": "ng", @@ -68,6 +68,7 @@ "process": "^0.11.10", "rxjs": "^7.5.7", "string-to-color": "^2.2.2", + "tinycolor2": "^1.5.2", "tslib": "^2.4.1", "url": "^0.11.0", "uuid": "^9.0.0", diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bb9a043a..1a0a7fc4 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -12,13 +12,20 @@ import {messagesReducer} from '@common/core/reducers/messages-reducer'; import {projectsReducer, RootProjects} from '@common/core/reducers/projects.reducer'; import {routerReducer} from '@common/core/reducers/router-reducer'; import {SmSyncStateSelectorService} from '@common/core/services/sync-state-selector.service'; -import {EXPERIMENTS_COMPARE_METRICS_CHARTS_} from '@common/experiments-compare/actions/experiments-compare-charts.actions'; +import { + EXPERIMENTS_COMPARE_METRICS_CHARTS_ +} from '@common/experiments-compare/actions/experiments-compare-charts.actions'; import {compareSyncedKeys} from '@common/experiments-compare/experiments-compare.module'; import {EXPERIMENTS_OUTPUT_PREFIX} from '@common/experiments/actions/common-experiment-output.actions'; import {EXPERIMENTS_INFO_PREFIX} from '@common/experiments/actions/common-experiments-info.actions'; import {EXPERIMENTS_PREFIX} from '@common/experiments/actions/common-experiments-view.actions'; import {EXPERIMENTS_STORE_KEY} from '@common/experiments/shared/common-experiments.const'; -import {MODELS_PREFIX_INFO, MODELS_PREFIX_MENU, MODELS_PREFIX_VIEW, MODELS_STORE_KEY} from '@common/models/models.consts'; +import { + MODELS_PREFIX_INFO, + MODELS_PREFIX_MENU, + MODELS_PREFIX_VIEW, + MODELS_STORE_KEY +} from '@common/models/models.consts'; import {modelSyncedKeys} from '@common/models/models.module'; import {PROJECTS_PREFIX} from '@common/projects/common-projects.consts'; import {CHOOSE_COLOR_PREFIX} from '@common/shared/ui-components/directives/choose-color/choose-color.actions'; @@ -42,6 +49,7 @@ import {usersReducer} from './reducers/users.reducer'; import {viewReducer} from './reducers/view.reducer'; import {UsageStatsService} from './services/usage-stats.service'; import {extCoreModules} from '~/build-specifics'; +import {ReportCodeEmbedService} from '../shared/services/report-code-embed.service'; export const reducers = { auth: authReducer, @@ -83,16 +91,16 @@ export const localStorageReducer = (reducer: ActionReducer): ActionReducer< if (action.type === '@ngrx/store/init') { const savedState = JSON.parse(localStorage.getItem(key)); nextState = merge(nextState, savedState); - } + } if (state === nextState) { return nextState; - } + } if (actionsPrefix && !actionsPrefix.some(ap => action.type.startsWith(ap))) { return nextState; - } + } localStorage.setItem(key, JSON.stringify(pick(syncedKeys, nextState))); return nextState; - }; + }; const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer[] => [ (reducer: ActionReducer) => @@ -141,6 +149,7 @@ const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer SmSyncStateSelectorService, UsageStatsService, AdminService, + ReportCodeEmbedService, { provide: USER_PROVIDED_META_REDUCERS, deps: [UserPreferences], diff --git a/src/app/core/effects/projects.effects.ts b/src/app/core/effects/projects.effects.ts index bc06acb1..40e0da1e 100644 --- a/src/app/core/effects/projects.effects.ts +++ b/src/app/core/effects/projects.effects.ts @@ -45,6 +45,7 @@ export class ProjectsEffects { return [ actions.setSelectedProject({project: ALL_PROJECTS_OBJECT}), actions.getProjectUsers(action), + actions.getCompanyTags(), deactivateLoader(action.type)]; } else { this.fetchingExampleExperiment = action.example && action.projectId; diff --git a/src/app/shared/services/report-code-embed.service.ts b/src/app/shared/services/report-code-embed.service.ts new file mode 100644 index 00000000..fa1a557a --- /dev/null +++ b/src/app/shared/services/report-code-embed.service.ts @@ -0,0 +1,2 @@ +export {ReportCodeEmbedBaseService as ReportCodeEmbedService} from '../../webapp-common/shared/services/report-code-embed-base.service'; + diff --git a/src/app/webapp-common/assets/fonts/trains-icons.scss b/src/app/webapp-common/assets/fonts/trains-icons.scss index f34807d1..df19d6c5 100644 --- a/src/app/webapp-common/assets/fonts/trains-icons.scss +++ b/src/app/webapp-common/assets/fonts/trains-icons.scss @@ -3,7 +3,7 @@ @font-face { font-family: '#{$icomoon-font-family}'; - src: url('./#{$icomoon-font-family}.ttf?f4pbms') format('truetype'); + src: url('./#{$icomoon-font-family}.ttf?s4ibjb') format('truetype'); font-weight: normal; font-style: normal; font-display: block; @@ -24,19 +24,19 @@ -moz-osx-font-smoothing: grayscale; } -.al-ico-md-copy { +.al-ico-markdown { &:before { - content: $al-ico-md-copy; + content: $al-ico-markdown; } } .al-ico-hor-expand { &:before { - content: $al-ico-hor-expand; + content: $al-ico-hor-expand; } } .al-ico-hor-minimize { &:before { - content: $al-ico-hor-minimize; + content: $al-ico-hor-minimize; } } .al-ico-pdf { diff --git a/src/app/webapp-common/assets/fonts/trains.ttf b/src/app/webapp-common/assets/fonts/trains.ttf index 4f82c651..64c3e761 100644 Binary files a/src/app/webapp-common/assets/fonts/trains.ttf and b/src/app/webapp-common/assets/fonts/trains.ttf differ diff --git a/src/app/webapp-common/assets/fonts/variables.scss b/src/app/webapp-common/assets/fonts/variables.scss index 1ffb0487..70277dcc 100644 --- a/src/app/webapp-common/assets/fonts/variables.scss +++ b/src/app/webapp-common/assets/fonts/variables.scss @@ -1,7 +1,7 @@ $icomoon-font-family: "trains" !default; $icomoon-font-path: "fonts" !default; -$al-ico-md-copy: "\e9ec"; +$al-ico-markdown: "\e9ec"; $al-ico-hor-expand: "\e9ea"; $al-ico-hor-minimize: "\e9eb"; $al-ico-pdf: "\e9e9"; diff --git a/src/app/webapp-common/assets/icons/training_loss_graph.png b/src/app/webapp-common/assets/icons/training_loss_graph.png new file mode 100644 index 00000000..1ec459ca Binary files /dev/null and b/src/app/webapp-common/assets/icons/training_loss_graph.png differ diff --git a/src/app/webapp-common/assets/markdown-cheatsheet.html b/src/app/webapp-common/assets/markdown-cheatsheet.html new file mode 100644 index 00000000..cf04e1cf --- /dev/null +++ b/src/app/webapp-common/assets/markdown-cheatsheet.html @@ -0,0 +1,439 @@ + + + + + diff --git a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.actions.ts b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.actions.ts index 2d393c3f..f4dd175a 100644 --- a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.actions.ts +++ b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.actions.ts @@ -9,6 +9,7 @@ export const getPlot = createAction('[App] getPlot', props<{ metrics: string[]; variants: string[]; company: string; + otherSearchParams?: URLSearchParams; }>()); export const getScalar = createAction('[App] getScalar', props<{ @@ -17,6 +18,7 @@ export const getScalar = createAction('[App] getScalar', props<{ metrics: string[]; variants: string[]; company: string; + otherSearchParams?: URLSearchParams; }>()); export const getSample = createAction('[App] getSample', props<{ tasks: string[]; @@ -24,6 +26,7 @@ export const getSample = createAction('[App] getSample', props<{ metrics: string[]; variants: string[]; company: string; + otherSearchParams?: URLSearchParams; }>()); export const setPlotData = createAction('[App] setPlot', props<{ data: ReportsApiMultiplotsResponse }>()); @@ -32,3 +35,4 @@ export const setSampleData = createAction('[App] setSample', props<{ data: Debug export const reportsPlotlyReady = createAction('[App] plotly ready'); export const setSignIsNeeded = createAction('[App] set sign is needed'); +export const setNoPermissions = createAction('[App] set no permissions'); diff --git a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.html b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.html index 8a0e01a1..b97e35c6 100644 --- a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.html +++ b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.html @@ -1,38 +1,39 @@ - - - - - + + + + + + - - - + + + + -
@@ -44,7 +45,14 @@
- -
Missing S3 credentials. Please verify credentials in WEB APP CLOUD ACCESS in clearml app.
+
Missing S3 credentials. Please verify credentials in WEB + APP CLOUD ACCESS in clearml app. +
+
+ + +
Missing permissions to view this item. +
diff --git a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.ts b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.ts index 7481e77d..bbac1a98 100644 --- a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.ts +++ b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.component.ts @@ -1,11 +1,19 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit, ViewChild} from '@angular/core'; import {Store} from '@ngrx/store'; import {MatDialog} from '@angular/material/dialog'; import {Observable} from 'rxjs'; import {filter, map, switchMap, take} from 'rxjs/operators'; import {Environment} from '../environments/base'; import {getPlot, getSample, getScalar, reportsPlotlyReady} from './app.actions'; -import {ReportsApiMultiplotsResponse, selectPlotData, selectReportsPlotlyReady, selectSampleData, selectSignIsNeeded, State} from './app.reducer'; +import { + ReportsApiMultiplotsResponse, + selectNoPermissions, + selectPlotData, + selectReportsPlotlyReady, + selectSampleData, + selectSignIsNeeded, + State +} from './app.reducer'; import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base'; import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer'; import {getSignedUrl, setS3Credentials} from '@common/core/actions/common-auth.actions'; @@ -18,6 +26,7 @@ import {cloneDeep} from 'lodash/fp'; import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent'; import {SingleGraphComponent} from '@common/shared/single-graph/single-graph.component'; import {setCurrentDebugImage} from '@common/shared/debug-sample/debug-sample.actions'; +import {isFileserverUrl} from '~/shared/utils/url'; @Component({ @@ -34,13 +43,21 @@ export class AppComponent implements OnInit { private environment: Environment; public activated: boolean = false; private searchParams: URLSearchParams; + private otherSearchParams: URLSearchParams; public type: string; public singleGraphHeight; public hideMaximize: 'show' | 'hide' | 'disabled' = 'show'; - public signIsNeeded: Observable; - + public signIsNeeded$: Observable; + public noPermissions$: Observable; + public isDarkTheme: boolean; + public externalTool: boolean = false; @ViewChild(SingleGraphComponent) 'singleGraph': SingleGraphComponent; + @HostListener('window:resize') + onResize() { + this.singleGraph?.redrawPlot(); + } + constructor( private store: Store, private configService: ConfigurationService, @@ -49,26 +66,39 @@ export class AppComponent implements OnInit { this.configService.globalEnvironmentObservable.subscribe(env => { this.environment = env; }); - this.signIsNeeded = store.select(selectSignIsNeeded); + this.signIsNeeded$ = store.select(selectSignIsNeeded); + this.noPermissions$ = store.select(selectNoPermissions); this.searchParams = new URLSearchParams(window.location.search); this.type = this.searchParams.get('type'); this.singleGraphHeight = window.innerHeight; + this.otherSearchParams = this.getOtherSearchParams(); + this.isDarkTheme = !this.searchParams.get('light'); try { - const lala = JSON.parse(localStorage.getItem('_saved_state_')); - this.store.dispatch(setS3Credentials(lala.auth.s3BucketCredentials)); + const data = JSON.parse(localStorage.getItem('_saved_state_')); + data.auth && this.store.dispatch(setS3Credentials(data.auth.s3BucketCredentials)); } catch (e) { console.log(e); } } + private getOtherSearchParams() { + const paramsToRemove = ['light', 'type','tasks', 'metrics', 'variants', 'iterations', 'company']; + const otherSearchParams = new URLSearchParams(window.location.search); + paramsToRemove.forEach( key => { + otherSearchParams.delete(key); + }); + return otherSearchParams; + } + ngOnInit(): void { try { if (!(window.top as any).holdIframe) { this.activate(); } } catch (e) { + this.externalTool = true; this.hideMaximize = 'hide'; console.log('no-access-to-parent-window'); this.activate(); @@ -164,14 +194,18 @@ export class AppComponent implements OnInit { this.store.select(selectSampleData) .pipe(filter(sample => !!sample)) .subscribe(sample => { - this.store.dispatch(getSignedUrl({url: sample.url})); - this.store.select(selectSignedUrl(sample.url)) + const url = new URL(sample.url); + if (isFileserverUrl(sample.url) && this.searchParams.get('company')) { + url.searchParams.append('tenant', this.searchParams.get('company')); + } + this.store.dispatch(getSignedUrl({url: url.toString()})); + this.store.select(selectSignedUrl(url.toString())) .pipe( filter(signed => !!signed?.signed), map(({signed: signedUrl}) => signedUrl), take(1) - ).subscribe(() => { - this.frame = sample; + ).subscribe((signedUrl) => { + this.frame = {...sample, url: signedUrl}; this.activated = true; this.cdr.detectChanges(); }); @@ -186,18 +220,18 @@ export class AppComponent implements OnInit { metrics: this.searchParams.getAll('metrics'), variants: this.searchParams.getAll('variants'), iterations: this.searchParams.getAll('iterations').map(iteration => parseInt(iteration, 10)), - company: this.searchParams.get('company') || '', + company: this.searchParams.get('company') || '', }; switch (this.type) { case 'plot': - this.store.dispatch(getPlot(queryParams)); + this.store.dispatch(getPlot({...queryParams, otherSearchParams: this.otherSearchParams})); break; case 'scalar': - this.store.dispatch(getScalar(queryParams)); + this.store.dispatch(getScalar({...queryParams, otherSearchParams: this.otherSearchParams})); break; case 'sample': - this.store.dispatch(getSample(queryParams)); + this.store.dispatch(getSample({...queryParams, otherSearchParams: this.otherSearchParams})); } this.activated = true; }; diff --git a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.effects.ts b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.effects.ts index e2ddea17..fde34301 100644 --- a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.effects.ts +++ b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.effects.ts @@ -1,19 +1,27 @@ import {Injectable} from '@angular/core'; import {Actions, createEffect, ofType} from '@ngrx/effects'; -import {getPlot, getSample, getScalar, setPlotData, setSampleData, setSignIsNeeded} from './app.actions'; +import { + getPlot, + getSample, + getScalar, + setNoPermissions, + setPlotData, + setSampleData, + setSignIsNeeded +} from './app.actions'; import {EMPTY, mergeMap, of, switchMap} from 'rxjs'; import {Store} from '@ngrx/store'; -import {filter} from 'rxjs/operators'; +import {catchError, filter} from 'rxjs/operators'; import {ReportsApiMultiplotsResponse, State} from './app.reducer'; import {ApiReportsService} from '~/business-logic/api-services/reports.service'; import {BaseAdminService} from '@common/settings/admin/base-admin.service'; import {ReportsGetTaskDataResponse} from '~/business-logic/model/reports/reportsGetTaskDataResponse'; -import {setCurrentDebugImage} from '@common/shared/debug-sample/debug-sample.actions'; import {getSignedUrl, setSignedUrl} from '@common/core/actions/common-auth.actions'; import {SignResponse} from '@common/settings/admin/base-admin-utils'; import {HttpClient, HttpHeaders} from '@angular/common/http'; import {HTTP} from '~/app.constants'; import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer'; +import {requestFailed} from '@common/core/actions/http.actions'; @Injectable() @@ -37,7 +45,7 @@ export class AppEffects { getPlot = createEffect(() => this.actions$.pipe( ofType(getPlot), - switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`, + switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`, { id: action.tasks, plots: { @@ -47,12 +55,13 @@ export class AppEffects { }, {headers: this.getHeaders(action.company)} )), - mergeMap((res) => [setPlotData({data: res.data.plots as unknown as ReportsApiMultiplotsResponse})]) + mergeMap((res) => [setPlotData({data: res.data.plots as unknown as ReportsApiMultiplotsResponse})]), + catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])]) )); getScalar = createEffect(() => this.actions$.pipe( ofType(getScalar), - mergeMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`, + mergeMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`, { id: action.tasks, // eslint-disable-next-line @typescript-eslint/naming-convention @@ -64,14 +73,14 @@ export class AppEffects { ).pipe( mergeMap(res => [ setPlotData({data: res.data.scalar_metrics_iter_histogram as ReportsApiMultiplotsResponse})] - ) + ), catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])]) ) ) )); getSample = createEffect(() => this.actions$.pipe( ofType(getSample), - switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data`, + switchMap(action => this.httpClient.post<{ data: ReportsGetTaskDataResponse }>(`${this.basePath}/reports.get_task_data?${action.otherSearchParams.toString()}`, { id: action.tasks, // eslint-disable-next-line @typescript-eslint/naming-convention @@ -84,7 +93,8 @@ export class AppEffects { ).pipe( mergeMap(res => [ setSampleData({data: res.data.debug_images?.[0]?.iterations?.[0]?.events[0] as DebugSample}) - ]) + ]), + catchError(error => [requestFailed(error), ...(error.status === 403 ? [setNoPermissions()] : [])]) ) )) ); diff --git a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.reducer.ts b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.reducer.ts index ee98110e..f117e5fb 100644 --- a/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.reducer.ts +++ b/src/app/webapp-common/clearml-applications/report-widgets/src/app/app.reducer.ts @@ -1,5 +1,5 @@ import {createReducer, createSelector, on} from '@ngrx/store'; -import {reportsPlotlyReady, setPlotData, setSampleData, setSignIsNeeded} from './app.actions'; +import {reportsPlotlyReady, setNoPermissions, setPlotData, setSampleData, setSignIsNeeded} from './app.actions'; import {DebugSample} from '@common/shared/debug-sample/debug-sample.reducer'; import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent'; @@ -25,6 +25,7 @@ export interface State { scaleFactor: number; plotlyReady: boolean; signIsNeeded: boolean; + noPermissions: boolean; } export const initialState: State = { @@ -32,7 +33,8 @@ export const initialState: State = { sampleData: null, scaleFactor: 100, plotlyReady: false, - signIsNeeded: false + signIsNeeded: false, + noPermissions: false }; export const appReducer = createReducer( @@ -40,10 +42,8 @@ export const appReducer = createReducer( on(reportsPlotlyReady, (state) => ({...state, plotlyReady: true})), on(setPlotData, (state, action) => ({...state, plotData: action.data as ReportsApiMultiplotsResponse})), on(setSampleData, (state, action) => ({...state, sampleData: action.data})), - on(setSignIsNeeded, (state) => { - debugger - return ({...state, signIsNeeded: true}) - }), + on(setSignIsNeeded, (state) => ({...state, signIsNeeded: true})), + on(setNoPermissions, (state) => ({...state, noPermissions: true})), ); export const selectFeature = state => state.appReducer as State; @@ -53,3 +53,4 @@ export const selectReportsPlotlyReady = createSelector(selectFeature, state => s export const selectPlotData = createSelector(selectFeature, state => state.plotData); export const selectSampleData = createSelector(selectFeature, state => state.sampleData); export const selectSignIsNeeded = createSelector(selectFeature, state => state.signIsNeeded); +export const selectNoPermissions = createSelector(selectFeature, state => state.noPermissions); diff --git a/src/app/webapp-common/common-styles.scss b/src/app/webapp-common/common-styles.scss index e5cd7fd3..84f84690 100644 --- a/src/app/webapp-common/common-styles.scss +++ b/src/app/webapp-common/common-styles.scss @@ -18,7 +18,7 @@ //@import "../webapp-common/shared/ui-components/styles/material-theme.scss"; $custom-typography: mat.define-typography-config( - $font-family: $font-family-base + $font-family: $font-family-base ); @include mat.all-component-typographies($custom-typography); @@ -587,10 +587,10 @@ as-split { } $type-colors: ( - string: #ff8400, - number: $neon-yellow, - boolean: #b938a4, - date: #05668D, + string: #ff8400, + number: $neon-yellow, + boolean: #b938a4, + date: #05668D, ); @@ -780,3 +780,44 @@ button.btn.button-outline-dark { fill: rgb(77, 102, 255) !important; } } + + +@media print { + * { + color: black !important; + } + + html, body, .content, .info-content { + overflow: visible !important; + } + + sm-side-nav, + .actions button, .actions .add-button, + .content.report .mat-drawer, .report .hover-button { + display: none !important; + } + + .md-layout #print-element { + padding: 0 !important + } + .content.report .mat-drawer-content { + margin-left: unset !important + } + .content.report .mat-drawer-content, + .content.report .mat-drawer-container { + overflow: visible !important; + } + + .md-editor-container, + .preview-panel { + border: none !important; + } + + .header-container, + .header, + .content { + max-width: unset !important; + margin: 0 !important; + padding: 0 !important; + } +} diff --git a/src/app/webapp-common/core/actions/layout.actions.ts b/src/app/webapp-common/core/actions/layout.actions.ts index 3b1110f6..16012531 100644 --- a/src/app/webapp-common/core/actions/layout.actions.ts +++ b/src/app/webapp-common/core/actions/layout.actions.ts @@ -7,12 +7,12 @@ import {MessageSeverityEnum} from '@common/constants'; export const setAutoRefresh = createAction( VIEW_PREFIX + '[set auto refresh]', - props<{autoRefresh: boolean}>() + props<{ autoRefresh: boolean }>() ); export const setCompareAutoRefresh = createAction( VIEW_PREFIX + '[set compare auto refresh]', - props<{autoRefresh: boolean}>() + props<{ autoRefresh: boolean }>() ); export const setServerError = createAction( @@ -25,16 +25,17 @@ export const setServerError = createAction( }) ); + export const setNotificationDialog = createAction( VIEW_PREFIX + '[set notification dialog]', - props<{notification: {message: string; title: string}}>() + props<{ notification: { message: string; title: string } }>() ); export const resetLoader = createAction(VIEW_PREFIX + '[reset loader]'); export const setBackdrop = createAction( VIEW_PREFIX + '[set backdrop]', - props<{active: boolean}>() + props<{ active: boolean }>() ); export const activeLoader = createAction( @@ -42,6 +43,11 @@ export const activeLoader = createAction( (endpoint: string) => ({endpoint, noPreferences: true}) ); +export const showEmbedReportMenu = createAction( + VIEW_PREFIX + '[show embed report menu]', + props<{ show: boolean; position: { x: number; y: number } }>() +); + export const deactivateLoader = createAction( VIEW_PREFIX + '[deactivate loader]', (endpoint: string) => ({endpoint, noPreferences: true}) @@ -50,12 +56,12 @@ export const deactivateLoader = createAction( export const visibilityChanged = createAction( VIEW_PREFIX + '[visibility changed]', - props<{visible: boolean}>() + props<{ visible: boolean }>() ); export const saveAceCaretPosition = createAction( VIEW_PREFIX + '[save ace caret position]', - props<{id: string; position: Ace.Point}>() + props<{ id: string; position: Ace.Point }>() ); export const resetAceCaretsPositions = createAction(VIEW_PREFIX + '[reset ace carets positions]'); @@ -63,7 +69,7 @@ export const resetAceCaretsPositions = createAction(VIEW_PREFIX + '[reset ace ca export const addMessage = createAction( VIEW_PREFIX + '[add message]', - (severity: MessageSeverityEnum, msg: string, userActions?: {actions: any[]; name: string}[], suppressNextMessages?: boolean) => + (severity: MessageSeverityEnum, msg: string, userActions?: { actions: any[]; name: string }[], suppressNextMessages?: boolean) => ({severity, msg, userActions, suppressNextMessages}) ); @@ -71,29 +77,26 @@ export const removeMessage = createAction(VIEW_PREFIX + '[remove message]'); export const setServerUpdatesAvailable = createAction( VIEW_PREFIX + '[set server updates available]', - props<{availableUpdates}>() + props<{ availableUpdates }>() ); export const setScaleFactor = createAction( VIEW_PREFIX + '[set scale]', - props<{scale: number}>() + props<{ scale: number }>() ); export const firstLogin = createAction( VIEW_PREFIX + '[set first Login]', - props<{first: boolean}>() + props<{ first: boolean }>() ); export const neverShowPopupAgain = createAction(VIEW_PREFIX + 'NEVER_SHOW_POPUP_AGAIN', props<{ popupId: string; reset?: boolean }>()); -export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARGUMENTS', props<{ redactedArguments: { key: string} [] }>()); -export const setHideRedactedArguments = createAction(VIEW_PREFIX + 'SET_SHOW_REDACTED_ARGUMENTS', props<{hide: boolean }>()); +export const setRedactedArguments = createAction(VIEW_PREFIX + 'SET_REDACTED_ARGUMENTS', props<{ redactedArguments: { key: string } [] }>()); +export const setHideRedactedArguments = createAction(VIEW_PREFIX + 'SET_SHOW_REDACTED_ARGUMENTS', props<{ hide: boolean }>()); export const plotlyReady = createAction(VIEW_PREFIX + '[plotly ready]'); export const aceReady = createAction(VIEW_PREFIX + '[ace ready]'); export const openAppsAwarenessDialog = createAction(VIEW_PREFIX + '[apps awareness dialog]', - props<{appsYouTubeIntroVideoId}>() + props<{ appsYouTubeIntroVideoId }>() ); -export const toggleUserFocus = createAction( - VIEW_PREFIX + '[toggle user focus in header', - props<{show: boolean}>() -); + diff --git a/src/app/webapp-common/core/effects/projects.effects.ts b/src/app/webapp-common/core/effects/projects.effects.ts index 4ed5bded..336e50cd 100644 --- a/src/app/webapp-common/core/effects/projects.effects.ts +++ b/src/app/webapp-common/core/effects/projects.effects.ts @@ -53,12 +53,6 @@ export class ProjectsEffects { map(action => activeLoader(action.type)) )); - setProject = createEffect(() => this.actions$.pipe( - ofType(actions.setSelectedProject), - switchMap(action => [actions.getCompanyTags()] - .concat(!!action.project?.id ? [actions.getTags(action.project.id)] : [])) - )); - getProjects$ = createEffect(() => this.actions$.pipe( ofType(actions.getAllSystemProjects), withLatestFrom( diff --git a/src/app/webapp-common/core/meta-reducers/user-pref-reducer.ts b/src/app/webapp-common/core/meta-reducers/user-pref-reducer.ts index dea655c4..56b9d073 100644 --- a/src/app/webapp-common/core/meta-reducers/user-pref-reducer.ts +++ b/src/app/webapp-common/core/meta-reducers/user-pref-reducer.ts @@ -36,7 +36,7 @@ export const createUserPrefReducer = ( } // filter unchanged state. - if (action.noPreferences || state === nextState) { + if (action.noPreferences || state === nextState || !(nextState?.[key]?.preferencesReady)) { return nextState; } diff --git a/src/app/webapp-common/core/reducers/users-reducer.ts b/src/app/webapp-common/core/reducers/users-reducer.ts index d2109a6c..c23cc985 100644 --- a/src/app/webapp-common/core/reducers/users-reducer.ts +++ b/src/app/webapp-common/core/reducers/users-reducer.ts @@ -7,8 +7,12 @@ import { setCurrentUserName } from '../actions/users.actions'; import {GetCurrentUserResponseUserObject} from '~/business-logic/model/users/getCurrentUserResponseUserObject'; -import {GetCurrentUserResponseUserObjectCompany} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany'; -import {OrganizationGetUserCompaniesResponseCompanies} from '~/business-logic/model/organization/organizationGetUserCompaniesResponseCompanies'; +import { + GetCurrentUserResponseUserObjectCompany +} from '~/business-logic/model/users/getCurrentUserResponseUserObjectCompany'; +import { + OrganizationGetUserCompaniesResponseCompanies +} from '~/business-logic/model/organization/organizationGetUserCompaniesResponseCompanies'; export interface UsersState { currentUser: GetCurrentUserResponseUserObject; @@ -34,13 +38,13 @@ export const initUsers: UsersState = { export const users = state => state.users as UsersState; -export const selectCurrentUser = createSelector(users, state => state.currentUser); +export const selectCurrentUser = createSelector(users, state => state.currentUser); export const selectActiveWorkspace = createSelector(users, state => state.activeWorkspace); export const selectUserWorkspaces = createSelector(users, state => state.userWorkspaces); export const selectSelectedWorkspaceTab = createSelector(users, state => state.selectedWorkspaceTab); export const selectWorkspaces = createSelector(users, state => state.workspaces); export const selectShowOnlyUserWork = createSelector(users, state => state.showOnlyUserWork); -export const selectServerVersions = createSelector(users, state => state.serverVersions); +export const selectServerVersions = createSelector(users, state => state.serverVersions); export const selectGettingStarted = createSelector(users, state => state.gettingStarted); export const usersReducerFunctions = [ @@ -54,6 +58,8 @@ export const usersReducerFunctions = [ ...state, currentUser: null })), - on(setFilterByUser, (state, action) => ({...state, showOnlyUserWork: action.showOnlyUserWork})), + on(setFilterByUser, (state, action) => { + return ({...state, showOnlyUserWork: action.showOnlyUserWork}); + }), on(setApiVersion, (state, action) => ({...state, serverVersions: action.serverVersions})) ] as ReducerTypes[]; diff --git a/src/app/webapp-common/core/reducers/view.reducer.ts b/src/app/webapp-common/core/reducers/view.reducer.ts index 42315d05..36fc2bf4 100644 --- a/src/app/webapp-common/core/reducers/view.reducer.ts +++ b/src/app/webapp-common/core/reducers/view.reducer.ts @@ -23,6 +23,7 @@ export interface ViewState { showUserFocus: boolean; redactedArguments: { key: string }[]; hideRedactedArguments: boolean; + showEmbedReportMenu: { show: boolean; position: { x: number; y: number } }; } export const initViewState: ViewState = { @@ -48,6 +49,7 @@ export const initViewState: ViewState = { {key: 'AWS_SECRET_ACCESS_KEY'}, {key: 'AZURE_STORAGE_KEY'}], hideRedactedArguments: false, + showEmbedReportMenu: {show: null, position: null} }; export const views = state => state.views as ViewState; @@ -72,7 +74,7 @@ export const selectAceCaretPosition = createSelector(views, state => state.aceCa export const selectNeverShowPopups = createSelector(views, (state): string[] => state.neverShowPopupAgain); export const selectRedactedArguments = createSelector(views, (state): { key: string }[] => state.redactedArguments); export const selectHideRedactedArguments = createSelector(views, (state): { key: string }[] => state.hideRedactedArguments ? state.redactedArguments : null); -export const selectShowUserFocus = createSelector(views, state => state.showUserFocus); +export const selectShowEmbedReportMenu = createSelector(views, state => state.showEmbedReportMenu); export const viewReducers = [ @@ -114,11 +116,14 @@ export const viewReducers = [ on(layoutActions.setBackdrop, (state, action) => ({...state, backdropActive: action.active})), on(layoutActions.setAutoRefresh, (state, action) => ({...state, autoRefresh: action.autoRefresh})), on(layoutActions.setCompareAutoRefresh, (state, action) => ({...state, compareAutoRefresh: action.autoRefresh})), + on(layoutActions.showEmbedReportMenu, (state, action) => ({ + ...state, + showEmbedReportMenu: {show: action.show, position: action.position} + })), on(layoutActions.neverShowPopupAgain, (state, action) => ({ ...state, neverShowPopupAgain: action.reset ? state.neverShowPopupAgain.filter(popups => popups !== action.popupId) : Array.from(new Set([...state.neverShowPopupAgain, action.popupId])) })), - on(layoutActions.toggleUserFocus, (state, action) => ({...state, showUserFocus: action.show})) ] as ReducerTypes[]; export const viewReducer = createReducer( diff --git a/src/app/webapp-common/datasets/simple-datasets/simple-datasets.component.html b/src/app/webapp-common/datasets/simple-datasets/simple-datasets.component.html index fe80abcc..37fd8715 100644 --- a/src/app/webapp-common/datasets/simple-datasets/simple-datasets.component.html +++ b/src/app/webapp-common/datasets/simple-datasets/simple-datasets.component.html @@ -27,7 +27,7 @@ >
-
+
diff --git a/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.html b/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.html index 7eeb7262..0fa4026d 100644 --- a/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.html +++ b/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.html @@ -11,7 +11,7 @@ [theme]="isDarkTheme? themeEnum.Dark: themeEnum.Light" (imageError)="imageUrlError({frame, experimentId})" (imageClicked)="imageClicked.emit({frame})" - (createEmbedCode)="createEmbedCode.emit({metrics: [frame.metric], variants: [frame.variant]});"> + (createEmbedCode)="createEmbedCode.emit({metrics: [frame.metric], variants: [frame.variant], domRect:$event});"> diff --git a/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.ts b/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.ts index f804563e..27c8e283 100644 --- a/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.ts +++ b/src/app/webapp-common/debug-images/debug-images-view/debug-images-view.component.ts @@ -22,7 +22,7 @@ export class DebugImagesViewComponent { @Input() isDatasetVersionPreview = false; @Output() imageClicked = new EventEmitter(); @Output() refreshClicked = new EventEmitter(); - @Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]}>(); + @Output() createEmbedCode = new EventEmitter<{metrics?: string[]; variants?: string[]; domRect: DOMRect}>(); @Output() urlError = new EventEmitter(); public imageUrlError(data: { frame: Event; experimentId: string }) { diff --git a/src/app/webapp-common/debug-images/debug-images.component.html b/src/app/webapp-common/debug-images/debug-images.component.html index d23f6535..9bd50956 100644 --- a/src/app/webapp-common/debug-images/debug-images.component.html +++ b/src/app/webapp-common/debug-images/debug-images.component.html @@ -1,7 +1,7 @@
; public modelInfo$: Observable; - public ExperimentInfo$: Observable; + public experimentInfo$: Observable; public activeSection: any; public selectedId$: Observable; private experimentKey$: Observable; @@ -43,9 +43,10 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy { this.backdropActive$ = this.store.select(selectBackdropActive); this.editable$ = this.store.select(selectIsExperimentEditable); this.modelInfo$ = this.store.select(selectExperimentModelInfoData); - this.ExperimentInfo$ = this.store.select(selectExperimentInfoData); + this.experimentInfo$ = this.store.select(selectExperimentInfoData); this.routerConfig$ = this.store.select(selectRouterConfig); - this.selectedId$ = this.store.select(selectRouterParams).pipe(map(params => params?.artifactId || params?.modelId)); + this.selectedId$ = this.store.select(selectRouterParams) + .pipe(map(params => decodeURIComponent(params?.artifactId || params?.modelId))); this.experimentKey$ = this.store.select(selectRouterParams).pipe(map(params => params?.experimentId)); this.sub.add(this.store.select(selectRouterConfig) @@ -74,7 +75,7 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy { this.selectedId$, this.modelInfo$, this.experimentKey$, - this.ExperimentInfo$, + this.experimentInfo$, this.store.select(selectCurrentArtifactExperimentId) ]) .pipe( @@ -110,11 +111,11 @@ export class ExperimentInfoArtifactsComponent implements OnDestroy { private resetSelection(modelInfo): void { let target: string; if (modelInfo.input?.length > 0) { - target = `../artifacts/input-model/${modelInfo.input[0]?.id}`; + target = `../artifacts/input-model/${encodeURIComponent(modelInfo.input[0]?.id)}`; } else if (modelInfo.output?.length > 0) { - target = `../artifacts/output-model/${modelInfo.output[0]?.id}/`; + target = `../artifacts/output-model/${encodeURIComponent(modelInfo.output[0]?.id)}/`; } else if (modelInfo.artifacts.length > 0) { - target = `../artifacts/other/${modelInfo.artifacts[0]?.key}/${modelInfo.artifacts[0]?.mode}`; + target = `../artifacts/other/${encodeURIComponent(modelInfo.artifacts[0]?.key)}/${encodeURIComponent(modelInfo.artifacts[0]?.mode)}`; } else { // no items target = '../artifacts/input-model/input-model'; diff --git a/src/app/webapp-common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component.ts b/src/app/webapp-common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component.ts index cc0fe02a..76514236 100644 --- a/src/app/webapp-common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component.ts +++ b/src/app/webapp-common/experiments/containers/experiment-info-artifact-item/experiment-info-artifact-item.component.ts @@ -27,9 +27,7 @@ export class ExperimentInfoArtifactItemComponent implements OnInit, OnDestroy { this.modelInfo$ = this.store.select(selectExperimentModelInfoData); this.artifactKey$ = this.store.select(selectRouterParams) .pipe( - map(params => { - return {key: get('artifactId', params), mode: get('mode', params)}; - }), + map(params => ({key: decodeURIComponent(params?.artifactId), mode: get('mode', params)})), filter(artifactId => !!artifactId), distinctUntilChanged() ); diff --git a/src/app/webapp-common/experiments/containers/experiment-output-log/experiment-output-log.component.ts b/src/app/webapp-common/experiments/containers/experiment-output-log/experiment-output-log.component.ts index 71004cb0..a467e4b8 100644 --- a/src/app/webapp-common/experiments/containers/experiment-output-log/experiment-output-log.component.ts +++ b/src/app/webapp-common/experiments/containers/experiment-output-log/experiment-output-log.component.ts @@ -109,10 +109,10 @@ export class ExperimentOutputLogComponent implements OnInit, AfterViewInit, OnDe })); this.subs.add(this.refresh.tick - .pipe(filter(() => !this.loading && !!this.currExperiment?.id && (!this.logRef || this.logRef.atEnd))) - .subscribe((autoRefresh) => this.store.dispatch(getExperimentLog({ + .pipe(filter(autoRefresh => autoRefresh !== null && !this.loading && !!this.currExperiment?.id && (!this.logRef || this.logRef.atEnd))) + .subscribe(autoRefresh => this.store.dispatch(getExperimentLog({ id: this.currExperiment.id, - direction: autoRefresh ? 'prev' : 'next', + direction: autoRefresh ? 'next' : 'prev', from: last(this.logRef?.orgLogs)?.timestamp, autoRefresh: true }))) diff --git a/src/app/webapp-common/experiments/containers/experiment-output-plots/experiment-output-plots.component.ts b/src/app/webapp-common/experiments/containers/experiment-output-plots/experiment-output-plots.component.ts index d768d875..0d50f67b 100644 --- a/src/app/webapp-common/experiments/containers/experiment-output-plots/experiment-output-plots.component.ts +++ b/src/app/webapp-common/experiments/containers/experiment-output-plots/experiment-output-plots.component.ts @@ -35,7 +35,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base'; import {MetricsPlotEvent} from '~/business-logic/model/events/metricsPlotEvent'; import {addMessage} from '@common/core/actions/layout.actions'; import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experiment-graphs.component'; -import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service'; +import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service'; @Component({ selector: 'sm-experiment-output-plots', @@ -49,9 +49,6 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan public plotsList: Array; public selectedGraph: string = null; - private plotsSubscription: Subscription; - private settingsSubscription: Subscription; - private routerParamsSubscription: Subscription; private experimentId: string; private routerParams$: Observable; public listOfHidden: Observable>; @@ -61,9 +58,10 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan public graphs: { [key: string]: ExtFrame[] }; public refreshDisabled: boolean; public selectIsExperimentPendingRunning: Observable; - private selectedExperimentSubscription: Subscription; public splitSize$: Observable; public dark: boolean; + private subs = new Subscription(); + private experimentCompany: string | undefined; constructor( @@ -73,8 +71,9 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan private changeDetection: ChangeDetectorRef, private reportEmbed: ReportCodeEmbedService ) { - this.searchTerm$ = this.store.pipe(select(selectExperimentMetricsSearchTerm)); - this.splitSize$ = this.store.pipe(select(selectSplitSize)); + this.searchTerm$ = this.store.select(selectExperimentMetricsSearchTerm); + this.splitSize$ = this.store.select(selectSplitSize); + this.subs.add(this.store.select(selectSelectedExperiment).subscribe(exp => this.experimentCompany = exp?.company?.id ?? null)); this.experimentSettings$ = this.store.pipe( select(selectSelectedExperimentSettings), @@ -105,7 +104,7 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan ngOnInit() { this.minimized = this.activeRoute.snapshot.routeConfig.data?.minimized; this.listOfHidden = this.store.select(selectSelectedSettingsHiddenPlot); - this.plotsSubscription = this.store.select(selectExperimentInfoPlots) + this.subs.add(this.store.select(selectExperimentInfoPlots) .pipe( distinctUntilChanged(), filter(metrics => !!metrics), @@ -119,15 +118,15 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan this.graphs = graphs; parsingError && this.store.dispatch(addMessage('warn', `Couldn't read all plots. Please make sure all plots are properly formatted (NaN & Inf aren't supported).`, [], true)); this.changeDetection.detectChanges(); - }); + })); - this.settingsSubscription = this.experimentSettings$ + this.subs.add(this.experimentSettings$ .subscribe((selectedPlot) => { this.selectedGraph = selectedPlot; this.graphsComponent?.scrollToGraph(selectedPlot); - }); + })); - this.routerParamsSubscription = this.routerParams$ + this.subs.add(this.routerParams$ .subscribe(params => { if (!this.experimentId || this.experimentId !== params.experimentId) { this.graphs = undefined; @@ -136,9 +135,9 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan this.store.dispatch(setExperimentMetricsSearchTerm({searchTerm: ''})); } this.experimentId = params.experimentId; - }); + })); - this.selectedExperimentSubscription = this.store.select(selectSelectedExperiment) + this.subs.add(this.store.select(selectSelectedExperiment) .pipe( filter(experiment => !!experiment && !this.isDatasetVersionPreview), distinctUntilChanged() @@ -146,16 +145,13 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan .subscribe(experiment => { this.experimentId = experiment.id; this.refresh(); - }); + })); } ngOnDestroy() { this.resetMetrics(); - this.plotsSubscription.unsubscribe(); - this.settingsSubscription.unsubscribe(); - this.routerParamsSubscription.unsubscribe(); - this.selectedExperimentSubscription.unsubscribe(); + this.subs.unsubscribe(); this.resetMetrics(); } @@ -188,7 +184,7 @@ export class ExperimentOutputPlotsComponent implements OnInit, OnDestroy, OnChan this.store.dispatch(resetExperimentMetrics()); } - createEmbedCode(event: { metrics?: string[]; variants?: string[] }) { + createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect }) { this.reportEmbed.createCode({ type: 'plot', tasks: [this.experimentId], diff --git a/src/app/webapp-common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component.ts b/src/app/webapp-common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component.ts index 9568d18f..c04b03ea 100644 --- a/src/app/webapp-common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component.ts +++ b/src/app/webapp-common/experiments/containers/experiment-output-scalars/experiment-output-scalars.component.ts @@ -33,7 +33,7 @@ import {ExtFrame} from '@common/shared/single-graph/plotly-graph-base'; import {ExperimentGraphsComponent} from '@common/shared/experiment-graphs/experiment-graphs.component'; import {isEqual} from 'lodash/fp'; import { EventsGetTaskSingleValueMetricsResponseValues } from '~/business-logic/model/events/eventsGetTaskSingleValueMetricsResponseValues'; -import {ReportCodeEmbedService} from '@common/shared/services/report-code-embed.service'; +import { ReportCodeEmbedService } from '~/shared/services/report-code-embed.service'; export const prepareScalarList = (metricsScalar: GroupedList): GroupedList => Object.keys(metricsScalar || []).reduce((acc, curr) => { @@ -155,6 +155,8 @@ export class ExperimentOutputScalarsComponent implements OnInit, OnDestroy { this.subs.add(this.store.select(selectShowSettings) .subscribe((show) => this.showSettingsBar = show) ); + + } ngOnInit() { @@ -265,7 +267,7 @@ export class ExperimentOutputScalarsComponent implements OnInit, OnDestroy { }, {}); } - createEmbedCode(event: { metrics?: string[]; variants?: string[] }) { + createEmbedCode(event: { metrics?: string[]; variants?: string[]; domRect: DOMRect}) { this.reportEmbed.createCode({ type: 'scalar', tasks: [this.experimentId], diff --git a/src/app/webapp-common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component.html b/src/app/webapp-common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component.html index 5fe166c5..1f795400 100644 --- a/src/app/webapp-common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component.html +++ b/src/app/webapp-common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component.html @@ -15,7 +15,7 @@ *ngFor="let model of inputModels" queryParamsHandling="preserve" class="artifact-container pointer" - [routerLink]="'input-model/' + model.id" + [routerLink]="'input-model/' + (model.id | safeUrlParameter)" [class.selected]="model.id === selectedArtifactKey && activeSection === ACTIVE_SECTIONS['input-model']" > @@ -32,7 +32,7 @@ *ngFor="let model of outputModels" queryParamsHandling="preserve" class="artifact-container pointer" - [routerLink]="'output-model/' + model.id" + [routerLink]="'output-model/' + (model.id | safeUrlParameter)" [class.selected]="model.id === selectedArtifactKey && activeSection === ACTIVE_SECTIONS['output-model']" > @@ -47,7 +47,7 @@
{{artifact.key}} @@ -60,7 +60,7 @@
diff --git a/src/app/webapp-common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component.html b/src/app/webapp-common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component.html index f0b3aa4f..02f64204 100644 --- a/src/app/webapp-common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component.html +++ b/src/app/webapp-common/experiments/dumb/experiment-hyper-params-navbar/experiment-hyper-params-navbar.component.html @@ -17,7 +17,7 @@ - HYPER PARAMETERS + HYPERPARAMETERS
(); @Output() minimizeClicked = new EventEmitter(); - @Output() closeInfoClicked = new EventEmitter(); - @Output() maximizedClicked = new EventEmitter(); + @Output() closeInfoClicked = new EventEmitter(); + @Output() maximizedClicked = new EventEmitter(); @ViewChild('tagMenu') tagMenu: MenuComponent; @ViewChild('tagsMenuContent') tagMenuContent: TagsMenuComponent; @@ -65,6 +65,7 @@ export class ExperimentInfoHeaderComponent implements OnDestroy { ngOnDestroy(): void { this.tagMenu = null; this.tagMenuContent = null; + this.store.dispatch(setExperiment(null)); } private _experiment: any; diff --git a/src/app/webapp-common/experiments/effects/common-experiment-output.effects.ts b/src/app/webapp-common/experiments/effects/common-experiment-output.effects.ts index 3c970dd9..cdd43128 100644 --- a/src/app/webapp-common/experiments/effects/common-experiment-output.effects.ts +++ b/src/app/webapp-common/experiments/effects/common-experiment-output.effects.ts @@ -33,7 +33,7 @@ export class CommonExperimentOutputEffects { activeLoader = createEffect(() => this.actions$.pipe( ofType(outputActions.getExperimentLog, outputActions.experimentScalarRequested, outputActions.experimentPlotsRequested), - filter(action => !action?.['from']), + filter(action => !action?.['autoRefresh']), map(action => activeLoader(action.type)) )); diff --git a/src/app/webapp-common/experiments/effects/common-experiments-menu.effects.ts b/src/app/webapp-common/experiments/effects/common-experiments-menu.effects.ts index 7d1cfd9a..475da178 100644 --- a/src/app/webapp-common/experiments/effects/common-experiments-menu.effects.ts +++ b/src/app/webapp-common/experiments/effects/common-experiments-menu.effects.ts @@ -114,7 +114,7 @@ export class CommonExperimentsMenuEffects { if (res.queue_watched === false && !neverShowAgainPopups.includes('orphanedQueue')) { this.dialog.open(WelcomeMessageComponent, { data: { - queue: res.queue, + queue: action.queue, step: 2 } }).afterClosed().subscribe(doNotShowAgain => { diff --git a/src/app/webapp-common/experiments/experiments.component.html b/src/app/webapp-common/experiments/experiments.component.html index 802a7d16..2febadcf 100644 --- a/src/app/webapp-common/experiments/experiments.component.html +++ b/src/app/webapp-common/experiments/experiments.component.html @@ -123,6 +123,7 @@ [projectTags]="projectTags$ | async" [companyTags]="companyTags$ | async" [activateFromMenuButton]="false" + [useCurrentEntity]="singleRowContext" [minimizedView]="true" [tableMode]="!minimizedView" [backdrop]="menuBackdrop" diff --git a/src/app/webapp-common/experiments/shared/components/clone-dialog/clone-dialog.component.ts b/src/app/webapp-common/experiments/shared/components/clone-dialog/clone-dialog.component.ts index c9e7abd8..2a337f34 100644 --- a/src/app/webapp-common/experiments/shared/components/clone-dialog/clone-dialog.component.ts +++ b/src/app/webapp-common/experiments/shared/components/clone-dialog/clone-dialog.component.ts @@ -84,9 +84,9 @@ export class CloneDialogComponent implements OnInit, OnDestroy { const projectList = projects?.map(project => ({value: project.id, label: project.name})); if (!isEqual(projectList, this.projects)) { this.projects = projectList; - const defaultProject = this.projects.find(project => project.value === this.defaultProjectId) as Project; + const defaultProjectIndex = this.projects.findIndex(project => project.value === this.defaultProjectId); setTimeout(() => { - this.formData.project = (defaultProject && defaultProject.company?.id) ? defaultProject : projects[0] ? this.projects[0] : null; + this.formData.project = (defaultProjectIndex > -1 && projects[defaultProjectIndex].company?.id) ? this.projects[defaultProjectIndex] : projects[0] ? this.projects[0] : null; this.filterText = this.formData.project?.label; }, 0); } diff --git a/src/app/webapp-common/experiments/shared/components/experiment-menu/experiment-menu.component.ts b/src/app/webapp-common/experiments/shared/components/experiment-menu/experiment-menu.component.ts index 2140274e..2fe239a3 100644 --- a/src/app/webapp-common/experiments/shared/components/experiment-menu/experiment-menu.component.ts +++ b/src/app/webapp-common/experiments/shared/components/experiment-menu/experiment-menu.component.ts @@ -71,6 +71,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements @Input() companyTags: string[]; @Input() numSelected = 0; @Input() activateFromMenuButton = true; + @Input() useCurrentEntity = false; @Input() set experiment(experiment: ISelectedExperiment) { this._experiment = experiment; @@ -202,14 +203,14 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements } public resetPopup() { - const selectedExperiments = this.selectedExperiments ? selectionDisabledReset(this.selectedExperiments).selectedFiltered : [this._experiment]; + const selectedExperiments = (!(this.activateFromMenuButton || this.useCurrentEntity) && this.selectedExperiments) ? selectionDisabledReset(this.selectedExperiments).selectedFiltered : [this._experiment]; const devWarning: boolean = selectedExperiments.some(exp => isDevelopment(exp)); const confirmDialogRef = this.dialog.open(CommonDeleteDialogComponent, { data: { entity: this._experiment, - numSelected: this.numSelected, + numSelected: selectedExperiments?.length ?? this.numSelected, entityType: EntityTypeEnum.experiment, - useCurrentEntity: this.activateFromMenuButton, + useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity, resetMode: true, devWarning }, @@ -359,7 +360,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements entity: this._experiment, numSelected: this.numSelected, entityType: entityType || EntityTypeEnum.experiment, - useCurrentEntity: this.activateFromMenuButton, + useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity, includeChildren }, width: '600px', diff --git a/src/app/webapp-common/experiments/shared/components/select-queue/select-queue.component.ts b/src/app/webapp-common/experiments/shared/components/select-queue/select-queue.component.ts index dc194831..5664be3d 100644 --- a/src/app/webapp-common/experiments/shared/components/select-queue/select-queue.component.ts +++ b/src/app/webapp-common/experiments/shared/components/select-queue/select-queue.component.ts @@ -47,25 +47,16 @@ export class SelectQueueComponent implements OnInit, OnDestroy { reference?: string; } ) { + this.store.dispatch(new GetQueuesForEnqueue()); this.userAllowedToCreateQueue$ = userAllowedToCreateQueue$(store); if (data && data.taskIds?.length > 0) { this.store.dispatch(new GetTaskForEnqueue(data.taskIds)); this.reference = data.taskIds.length < 2 ? data.reference : `${data.taskIds.length} experiments `; } - this.queuesSub = this.queues$.subscribe(queues => { - if (queues) { - this.queues = queues; - this.queuesNames = queues.map(q => q.name); - this.defaultQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0]; - this.queueControl.reset(this.defaultQueue, {emitEvent: false}); - this.cdr.detectChanges(); - } - }); } ngOnInit() { - this.store.dispatch(new GetQueuesForEnqueue()); this.filteredOptions$ = combineLatest([ this.queueControl.valueChanges.pipe(startWith('')), this.queues$ @@ -82,6 +73,16 @@ export class SelectQueueComponent implements OnInit, OnDestroy { return queues.filter(q => q.name.toLowerCase().includes(name)); }), ); + + this.queuesSub = this.queues$.subscribe(queues => { + if (queues) { + this.queues = queues; + this.queuesNames = queues.map(q => q.name); + this.defaultQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0]; + this.queueControl.reset(this.defaultQueue, {emitEvent: false}); + this.cdr.detectChanges(); + } + }); } closeDialog(confirmed) { diff --git a/src/app/webapp-common/layout/header/header.component.html b/src/app/webapp-common/layout/header/header.component.html index 9e2f05f6..f29c2e99 100644 --- a/src/app/webapp-common/layout/header/header.component.html +++ b/src/app/webapp-common/layout/header/header.component.html @@ -1,12 +1,12 @@
-
+
@@ -39,7 +39,7 @@ diff --git a/src/app/webapp-common/layout/header/header.component.ts b/src/app/webapp-common/layout/header/header.component.ts index 799072e6..7365a73e 100644 --- a/src/app/webapp-common/layout/header/header.component.ts +++ b/src/app/webapp-common/layout/header/header.component.ts @@ -1,6 +1,6 @@ import {ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit} from '@angular/core'; import {Store} from '@ngrx/store'; -import {selectActiveWorkspace, selectCurrentUser} from '../../core/reducers/users-reducer'; +import {selectActiveWorkspace, selectCurrentUser, selectShowOnlyUserWork} from '../../core/reducers/users-reducer'; import {Observable, Subscription} from 'rxjs'; import {logout} from '../../core/actions/users.actions'; import {addMessage, openAppsAwarenessDialog} from '../../core/actions/layout.actions'; @@ -16,7 +16,6 @@ import {ActivatedRoute, NavigationEnd, Router} from '@angular/router'; import {LoginService} from '~/shared/services/login.service'; import {selectUserSettingsNotificationPath} from '~/core/reducers/view.reducer'; import {selectInvitesPending} from '~/core/reducers/users.reducer'; -import {selectShowUserFocus} from '@common/core/reducers/view.reducer'; import {MESSAGES_SEVERITY} from '@common/constants'; @Component({ @@ -37,7 +36,6 @@ export class HeaderComponent implements OnInit, OnDestroy { public url: Observable; public invitesPending$: Observable; public userNotificationPath: string; - public showUserFocus$: Observable; private sub = new Subscription(); constructor( @@ -53,7 +51,6 @@ export class HeaderComponent implements OnInit, OnDestroy { this.user = this.store.select(selectCurrentUser); this.sub.add(this.store.select(selectUserSettingsNotificationPath).subscribe(path => this.userNotificationPath = path)); this.invitesPending$ = this.store.select(selectInvitesPending); - this.showUserFocus$ = this.store.select(selectShowUserFocus); this.sub.add(this.store.select(selectActiveWorkspace) .pipe( filter(workspace => !!workspace), diff --git a/src/app/webapp-common/layout/s3-access-resolver/s3-access-resolver.component.ts b/src/app/webapp-common/layout/s3-access-resolver/s3-access-resolver.component.ts index 588d1e69..31bc6bc0 100644 --- a/src/app/webapp-common/layout/s3-access-resolver/s3-access-resolver.component.ts +++ b/src/app/webapp-common/layout/s3-access-resolver/s3-access-resolver.component.ts @@ -36,7 +36,7 @@ export class S3AccessResolverComponent { this.isAzure = data.isAzure; if (data.credentialsError) { this.header = this.header = `${data.credentialsError}, please check credentials for bucket ${this.bucket}.`; - } else { + } else { this.header = `Please provide credentials for bucket ${this.bucket}.`; } } diff --git a/src/app/webapp-common/login/login/login.component.ts b/src/app/webapp-common/login/login/login.component.ts index d0c88acd..bc7a861f 100644 --- a/src/app/webapp-common/login/login/login.component.ts +++ b/src/app/webapp-common/login/login/login.component.ts @@ -158,7 +158,10 @@ export class LoginComponent implements OnInit, OnDestroy { if (this.showGitHub) { fetch('https://api.github.com/repos/allegroai/clearml', {method: 'GET'}) .then(response => response.json() - .then(json => this.stars = json['stargazers_count']) + .then(json => { + this.stars = json['stargazers_count']; + this.cdr.detectChanges(); + }) ); } } diff --git a/src/app/webapp-common/models/containers/model-menu/model-menu.component.html b/src/app/webapp-common/models/containers/model-menu/model-menu.component.html index 2c830f64..39200856 100644 --- a/src/app/webapp-common/models/containers/model-menu/model-menu.component.html +++ b/src/app/webapp-common/models/containers/model-menu/model-menu.component.html @@ -1,5 +1,5 @@ - - +
(); constructor( @@ -167,7 +168,7 @@ export class ModelMenuComponent extends BaseContextMenuComponent { entity: this._model, numSelected: this.numSelected, entityType: EntityTypeEnum.model, - useCurrentEntity: this.showButton + useCurrentEntity: this.activateFromMenuButton || this.useCurrentEntity }, width: '600px', disableClose: true diff --git a/src/app/webapp-common/models/shared/models-table/models-table.component.html b/src/app/webapp-common/models/shared/models-table/models-table.component.html index ea51ec82..c2431762 100644 --- a/src/app/webapp-common/models/shared/models-table/models-table.component.html +++ b/src/app/webapp-common/models/shared/models-table/models-table.component.html @@ -10,7 +10,8 @@ [tagsFilterByProject]="tagsFilterByProject$ | async" [projectTags]="projectTags$ | async" [companyTags]="companyTags$ | async" - [showButton]="false" + [activateFromMenuButton]="false" + [useCurrentEntity]="singleRowContext" [backdrop]="menuBackdrop" (tagSelected)="addTag($event)" (menuOpened)="setContextMenuStatus(true)" diff --git a/src/app/webapp-common/pipelines-controller/controllers.component.html b/src/app/webapp-common/pipelines-controller/controllers.component.html index 49b80ab0..c16f01e6 100644 --- a/src/app/webapp-common/pipelines-controller/controllers.component.html +++ b/src/app/webapp-common/pipelines-controller/controllers.component.html @@ -118,6 +118,7 @@ [projectTags]="projectTags$ | async" [companyTags]="companyTags$ | async" [activateFromMenuButton]="false" + [useCurrentEntity]="singleRowContext" [minimizedView]="true" [tableMode]="!minimizedView" [backdrop]="menuBackdrop" diff --git a/src/app/webapp-common/pipelines-controller/pipeline-details/pipeline-info.component.html b/src/app/webapp-common/pipelines-controller/pipeline-details/pipeline-info.component.html index f6f0cb95..8a43caae 100644 --- a/src/app/webapp-common/pipelines-controller/pipeline-details/pipeline-info.component.html +++ b/src/app/webapp-common/pipelines-controller/pipeline-details/pipeline-info.component.html @@ -8,7 +8,7 @@ class="al-icon lg" [class]="'al-ico-type-' + (entity?.type ? (entity.type.toString()).replace('_', '-') : 'training')" > -
{{step.name}}
+
{{step.name}}
{{step.data.status}}
diff --git a/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.html b/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.html index eaf5ffec..f34f6bc6 100644 --- a/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.html +++ b/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.html @@ -26,7 +26,7 @@ > -
+
diff --git a/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.ts b/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.ts index 7be2995d..0563a9c3 100644 --- a/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.ts +++ b/src/app/webapp-common/pipelines/pipelines-page/pipelines-page.component.ts @@ -45,7 +45,7 @@ def step(size: int): name='ingest', project='data processing', version='0.1', - pipeline_execution_queue="default" + pipeline_execution_queue='default' ) def pipeline_logic(do_stuff: bool): if do_stuff: diff --git a/src/app/webapp-common/projects/common-projects.reducer.ts b/src/app/webapp-common/projects/common-projects.reducer.ts index 402df2aa..789b7702 100644 --- a/src/app/webapp-common/projects/common-projects.reducer.ts +++ b/src/app/webapp-common/projects/common-projects.reducer.ts @@ -62,27 +62,31 @@ export const commonProjectsReducers = [ on(setCurrentScrollId, (state, action) => ({...state, scrollId: action.scrollId})), on(setNoMoreProjects, (state, action) => ({...state, noMoreProjects: action.payload})), on(updateProjectSuccess, (state, action) => ({...state, projects: state.projects?.map(ex => ex.id === action.id ? {...ex, ...action.changes} : ex)})), - on(resetProjects, state => ({...state, scrollId: null, noMoreProjects: false, projects: commonProjectsInitState.projects})), + on(resetProjects, state => ({...state, + scrollId: null, + noMoreProjects: commonProjectsInitState.noMoreProjects, + projects: commonProjectsInitState.projects + })), on(setProjectsOrderBy, (state, action) => ({ ...state, orderBy: action.orderBy, sortOrder: getCorrectSortingOrder(state.sortOrder, state.orderBy, action.orderBy), scrollId: null, - noMoreProjects: false, + noMoreProjects: commonProjectsInitState.noMoreProjects, projects: commonProjectsInitState.projects })), on(setProjectsSearchQuery, (state, action) => ({ ...state, searchQuery: (action as ReturnType), scrollId: null, - noMoreProjects: true, + noMoreProjects: commonProjectsInitState.noMoreProjects, projects: commonProjectsInitState.projects })), on(resetProjectsSearchQuery, state => ({ ...state, searchQuery: commonProjectsInitState.searchQuery, scrollId: null, - noMoreProjects: true, + noMoreProjects: commonProjectsInitState.noMoreProjects, projects: commonProjectsInitState.projects })), on(checkProjectForDeletion, (state, action) => ({ diff --git a/src/app/webapp-common/projects/dumb/projects-list/projects-list.component.html b/src/app/webapp-common/projects/dumb/projects-list/projects-list.component.html index 579602d6..0fb53e7f 100644 --- a/src/app/webapp-common/projects/dumb/projects-list/projects-list.component.html +++ b/src/app/webapp-common/projects/dumb/projects-list/projects-list.component.html @@ -15,7 +15,7 @@ -
+
diff --git a/src/app/webapp-common/reports/report-card/report-card.component.html b/src/app/webapp-common/reports/report-card/report-card.component.html index d9022573..dcb282c2 100644 --- a/src/app/webapp-common/reports/report-card/report-card.component.html +++ b/src/app/webapp-common/reports/report-card/report-card.component.html @@ -48,9 +48,7 @@
-
- {{report?.comment}} -
+
{{report?.comment}}
-
-
@@ -56,11 +58,12 @@
- +
@@ -86,7 +90,7 @@ (saveInfo)="save($event)" (editModeChanged)="editModeChanged();" > -
+
THERE'S NOTHING HERE YET…