diff --git a/.gitignore b/.gitignore
index af98ecfb6..f37d1f47b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,6 @@ src/environments/environment.researchfi.ts
src/environments/environment.researchfi.prod.ts
src/assets/config/config.json
src/assets/config/auth_config.json
+/test-results/
+/playwright-report/
+/playwright/.cache/
diff --git a/angular.json b/angular.json
index 10a7014ca..8795a6481 100644
--- a/angular.json
+++ b/angular.json
@@ -92,11 +92,23 @@
"en": {
"localize": [
"en"
+ ],
+ "fileReplacements": [
+ {
+ "replace": "src/environments/locale.ts",
+ "with": "src/environments/locale.en.ts"
+ }
]
},
"sv": {
"localize": [
"sv"
+ ],
+ "fileReplacements": [
+ {
+ "replace": "src/environments/locale.ts",
+ "with": "src/environments/locale.sv.ts"
+ }
]
},
"es5": {
diff --git a/package-lock.json b/package-lock.json
index 5d841ebe9..90afe3d6a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,6 +55,7 @@
"mississippi": "^4.0.0",
"ngx-bootstrap": "^9.0.0",
"ngx-countup": "^13.0.0",
+ "ngx-pipes": "^3.2.2",
"nodemailer": "^6.7.8",
"popper.js": "^1.16.0",
"referrer-policy": "^1.2.0",
@@ -66,6 +67,7 @@
"through2": "^4.0.2",
"timers": "^0.1.1",
"tslib": "^2.1.0",
+ "valibot": "^0.17.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
@@ -83,6 +85,7 @@
"@babel/preset-env": "^7.16.11",
"@fortawesome/fontawesome-free": "^6.0.0",
"@ngx-i18nsupport/ngx-i18nsupport": "^1.1.6",
+ "@playwright/test": "^1.39.0",
"@types/d3": "^7.1.0",
"@types/express": "^4.17.11",
"@types/jasmine": "~3.6.6",
@@ -3610,10 +3613,9 @@
}
},
"node_modules/@jridgewell/source-map": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
- "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
- "dev": true,
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -3623,7 +3625,6 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -3934,6 +3935,21 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
+ "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
+ "dev": true,
+ "dependencies": {
+ "playwright": "1.39.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -4281,7 +4297,6 @@
"version": "8.4.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
"integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
- "dev": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -4291,7 +4306,6 @@
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
- "dev": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
@@ -4300,8 +4314,7 @@
"node_modules/@types/estree": {
"version": "0.0.51",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
- "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
- "dev": true
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
},
"node_modules/@types/express": {
"version": "4.17.14",
@@ -4359,8 +4372,7 @@
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
- "dev": true
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
},
"node_modules/@types/lodash": {
"version": "4.14.188",
@@ -4386,8 +4398,7 @@
"node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
- "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
- "dev": true
+ "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -4739,7 +4750,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
- "dev": true,
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
@@ -4748,26 +4758,22 @@
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
- "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
- "dev": true
+ "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
- "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
- "dev": true
+ "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
- "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
- "dev": true
+ "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
- "dev": true,
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -4777,14 +4783,12 @@
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
- "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
- "dev": true
+ "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -4796,7 +4800,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
- "dev": true,
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
@@ -4805,7 +4808,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
- "dev": true,
"dependencies": {
"@xtuc/long": "4.2.2"
}
@@ -4813,14 +4815,12 @@
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
- "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
- "dev": true
+ "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -4836,7 +4836,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
@@ -4849,7 +4848,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -4861,7 +4859,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -4875,7 +4872,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
- "dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.1",
"@xtuc/long": "4.2.2"
@@ -4920,14 +4916,12 @@
"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==",
- "dev": true
+ "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==",
- "dev": true
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
"node_modules/@yarnpkg/lockfile": {
"version": "1.1.0",
@@ -4959,9 +4953,9 @@
}
},
"node_modules/acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"bin": {
"acorn": "bin/acorn"
},
@@ -4990,10 +4984,9 @@
}
},
"node_modules/acorn-import-assertions": {
- "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,
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
+ "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"peerDependencies": {
"acorn": "^8"
}
@@ -5101,7 +5094,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -5156,7 +5148,6 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
- "dev": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
@@ -5965,7 +5956,6 @@
"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==",
- "dev": true,
"engines": {
"node": ">=6.0"
}
@@ -6095,8 +6085,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"node_modules/commondir": {
"version": "1.0.1",
@@ -7701,10 +7690,9 @@
}
},
"node_modules/enhanced-resolve": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
- "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
- "dev": true,
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
+ "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -7813,8 +7801,7 @@
"node_modules/es-module-lexer": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
- "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
- "dev": true
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
},
"node_modules/es6-promise": {
"version": "4.2.8",
@@ -8316,7 +8303,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@@ -8329,7 +8315,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true,
"engines": {
"node": ">=4.0"
}
@@ -8667,7 +8652,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -8715,7 +8699,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "dev": true,
"engines": {
"node": ">=0.8.x"
}
@@ -8885,8 +8868,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-glob": {
"version": "3.2.12",
@@ -8919,8 +8901,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
@@ -9401,8 +9382,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"node_modules/globals": {
"version": "11.12.0",
@@ -9446,8 +9426,7 @@
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
@@ -10763,7 +10742,6 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
- "dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -10777,7 +10755,6 @@
"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"
}
@@ -10786,7 +10763,6 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -10917,8 +10893,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema": {
"version": "0.4.0",
@@ -10929,8 +10904,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -11409,7 +11383,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
- "dev": true,
"engines": {
"node": ">=6.11.5"
}
@@ -11844,8 +11817,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/merge2": {
"version": "1.4.1",
@@ -12174,9 +12146,15 @@
"dev": true
},
"node_modules/nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -12248,8 +12226,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"node_modules/ngx-bootstrap": {
"version": "9.0.0",
@@ -12279,6 +12256,223 @@
"@angular/core": ">=13.0.0"
}
},
+ "node_modules/ngx-pipes": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/ngx-pipes/-/ngx-pipes-3.2.2.tgz",
+ "integrity": "sha512-nhNIUJe/bud5ir4RD2YDA6MdrJUh3YASIcNrMFX9WljTxuvAb99SZ88PaKSFGy9SXjHg8MxjxHfCoOLlTkxxlQ==",
+ "dependencies": {
+ "postcss": "^8.4.19",
+ "tslib": "^2.3.0",
+ "webpack": "^5.75.0"
+ },
+ "peerDependencies": {
+ "@angular/core": ">=14"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@types/estree": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz",
+ "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/ast": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
+ "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+ "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
+ "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
+ "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/ieee754": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/leb128": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/utf8": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
+ "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/helper-wasm-section": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-opt": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6",
+ "@webassemblyjs/wast-printer": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
+ "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
+ "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
+ "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
+ "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/es-module-lexer": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz",
+ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q=="
+ },
+ "node_modules/ngx-pipes/node_modules/schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/ngx-pipes/node_modules/webpack": {
+ "version": "5.89.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
+ "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^1.0.0",
+ "@webassemblyjs/ast": "^1.11.5",
+ "@webassemblyjs/wasm-edit": "^1.11.5",
+ "@webassemblyjs/wasm-parser": "^1.11.5",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.9.0",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.15.0",
+ "es-module-lexer": "^1.2.1",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.2.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.3.7",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ },
+ "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/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@@ -13467,6 +13661,36 @@
"node": ">=8"
}
},
+ "node_modules/playwright": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
+ "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.39.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
+ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
+ "dev": true,
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
@@ -13478,9 +13702,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.18",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
- "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
@@ -13489,10 +13713,14 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "nanoid": "^3.3.4",
+ "nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
@@ -14729,7 +14957,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
"dependencies": {
"safe-buffer": "^5.1.0"
}
@@ -15583,10 +15810,9 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
- "dev": true,
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
"dependencies": {
"randombytes": "^2.1.0"
}
@@ -15853,7 +16079,6 @@
"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"
}
@@ -15903,7 +16128,6 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -16238,7 +16462,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -16278,16 +16501,15 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
- "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
- "dev": true,
+ "version": "5.3.9",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
+ "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.14",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.0",
- "terser": "^5.14.1"
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.16.8"
},
"engines": {
"node": ">= 10.13.0"
@@ -16315,7 +16537,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
- "dev": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@@ -16329,6 +16550,23 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/terser-webpack-plugin/node_modules/terser": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
+ "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/test-exclude": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
@@ -16794,7 +17032,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -16830,6 +17067,11 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/valibot": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.17.0.tgz",
+ "integrity": "sha512-ykoYfBc4HOH+osdWaFBmeL7yX35bFfKg1WdwHYZhNVuvGIRH5f2gtYT6cnU85XOsOE7R8la5RnVY6yAScWU9qg=="
+ },
"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",
@@ -16913,7 +17155,6 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
- "dev": true,
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -16965,7 +17206,6 @@
"version": "5.74.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
- "dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
@@ -17274,7 +17514,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
- "dev": true,
"engines": {
"node": ">=10.13.0"
}
@@ -17304,7 +17543,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
- "dev": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@@ -20087,10 +20325,9 @@
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
},
"@jridgewell/source-map": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
- "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
- "dev": true,
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
+ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
"requires": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@@ -20100,7 +20337,6 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@@ -20340,6 +20576,15 @@
"which": "^2.0.2"
}
},
+ "@playwright/test": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz",
+ "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==",
+ "dev": true,
+ "requires": {
+ "playwright": "1.39.0"
+ }
+ },
"@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -20675,7 +20920,6 @@
"version": "8.4.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
"integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==",
- "dev": true,
"requires": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -20685,7 +20929,6 @@
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz",
"integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==",
- "dev": true,
"requires": {
"@types/eslint": "*",
"@types/estree": "*"
@@ -20694,8 +20937,7 @@
"@types/estree": {
"version": "0.0.51",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
- "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
- "dev": true
+ "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
},
"@types/express": {
"version": "4.17.14",
@@ -20753,8 +20995,7 @@
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
- "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
- "dev": true
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
},
"@types/lodash": {
"version": "4.14.188",
@@ -20780,8 +21021,7 @@
"@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
- "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==",
- "dev": true
+ "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"@types/parse-json": {
"version": "4.0.0",
@@ -21023,7 +21263,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
"integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
- "dev": true,
"requires": {
"@webassemblyjs/helper-numbers": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1"
@@ -21032,26 +21271,22 @@
"@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
- "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
- "dev": true
+ "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
},
"@webassemblyjs/helper-api-error": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
- "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
- "dev": true
+ "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
},
"@webassemblyjs/helper-buffer": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
- "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
- "dev": true
+ "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
},
"@webassemblyjs/helper-numbers": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
"integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
- "dev": true,
"requires": {
"@webassemblyjs/floating-point-hex-parser": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -21061,14 +21296,12 @@
"@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
- "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
- "dev": true
+ "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
},
"@webassemblyjs/helper-wasm-section": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
"integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -21080,7 +21313,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
"integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
- "dev": true,
"requires": {
"@xtuc/ieee754": "^1.2.0"
}
@@ -21089,7 +21321,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
"integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
- "dev": true,
"requires": {
"@xtuc/long": "4.2.2"
}
@@ -21097,14 +21328,12 @@
"@webassemblyjs/utf8": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
- "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
- "dev": true
+ "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
},
"@webassemblyjs/wasm-edit": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
"integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -21120,7 +21349,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
"integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-wasm-bytecode": "1.11.1",
@@ -21133,7 +21361,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
"integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-buffer": "1.11.1",
@@ -21145,7 +21372,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
"integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/helper-api-error": "1.11.1",
@@ -21159,7 +21385,6 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
"integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
- "dev": true,
"requires": {
"@webassemblyjs/ast": "1.11.1",
"@xtuc/long": "4.2.2"
@@ -21191,14 +21416,12 @@
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
- "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
- "dev": true
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
},
"@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==",
- "dev": true
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
"@yarnpkg/lockfile": {
"version": "1.1.0",
@@ -21227,9 +21450,9 @@
}
},
"acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA=="
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw=="
},
"acorn-globals": {
"version": "6.0.0",
@@ -21248,10 +21471,9 @@
}
},
"acorn-import-assertions": {
- "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,
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
+ "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"requires": {}
},
"acorn-jsx": {
@@ -21335,7 +21557,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
- "dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -21376,7 +21597,6 @@
"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": {}
},
"angular-auth-oidc-client": {
@@ -21961,8 +22181,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
},
"clean-stack": {
"version": "2.2.0",
@@ -22059,8 +22278,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"commondir": {
"version": "1.0.1",
@@ -23390,10 +23608,9 @@
"dev": true
},
"enhanced-resolve": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
- "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
- "dev": true,
+ "version": "5.15.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
+ "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"requires": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -23483,8 +23700,7 @@
"es-module-lexer": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
- "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
- "dev": true
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
},
"es6-promise": {
"version": "4.2.8",
@@ -23935,7 +24151,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@@ -23944,8 +24159,7 @@
"estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
}
}
},
@@ -24001,7 +24215,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
- "dev": true,
"requires": {
"estraverse": "^5.2.0"
}
@@ -24036,8 +24249,7 @@
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "dev": true
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
"execa": {
"version": "5.1.1",
@@ -24168,8 +24380,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
"version": "3.2.12",
@@ -24198,8 +24409,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fast-levenshtein": {
"version": "2.0.6",
@@ -24565,8 +24775,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"globals": {
"version": "11.12.0",
@@ -24598,8 +24807,7 @@
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"grapheme-splitter": {
"version": "1.0.4",
@@ -25573,7 +25781,6 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
- "dev": true,
"requires": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -25583,14 +25790,12 @@
"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
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"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"
}
@@ -25686,8 +25891,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"json-schema": {
"version": "0.4.0",
@@ -25698,8 +25902,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -26082,8 +26285,7 @@
"loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
- "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
- "dev": true
+ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="
},
"loader-utils": {
"version": "3.2.1",
@@ -26418,8 +26620,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"merge2": {
"version": "1.4.1",
@@ -26663,9 +26864,9 @@
"dev": true
},
"nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
+ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA=="
},
"natural-compare": {
"version": "1.4.0",
@@ -26721,8 +26922,7 @@
"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==",
- "dev": true
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"ngx-bootstrap": {
"version": "9.0.0",
@@ -26741,6 +26941,200 @@
"tslib": "^2.0.0"
}
},
+ "ngx-pipes": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/ngx-pipes/-/ngx-pipes-3.2.2.tgz",
+ "integrity": "sha512-nhNIUJe/bud5ir4RD2YDA6MdrJUh3YASIcNrMFX9WljTxuvAb99SZ88PaKSFGy9SXjHg8MxjxHfCoOLlTkxxlQ==",
+ "requires": {
+ "postcss": "^8.4.19",
+ "tslib": "^2.3.0",
+ "webpack": "^5.75.0"
+ },
+ "dependencies": {
+ "@types/estree": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz",
+ "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ=="
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
+ "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
+ "requires": {
+ "@webassemblyjs/helper-numbers": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+ "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
+ "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
+ },
+ "@webassemblyjs/helper-numbers": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+ "requires": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
+ "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
+ "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/helper-wasm-section": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-opt": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6",
+ "@webassemblyjs/wast-printer": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
+ "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
+ "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-buffer": "1.11.6",
+ "@webassemblyjs/wasm-gen": "1.11.6",
+ "@webassemblyjs/wasm-parser": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
+ "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@webassemblyjs/helper-api-error": "1.11.6",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+ "@webassemblyjs/ieee754": "1.11.6",
+ "@webassemblyjs/leb128": "1.11.6",
+ "@webassemblyjs/utf8": "1.11.6"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
+ "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
+ "requires": {
+ "@webassemblyjs/ast": "1.11.6",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "es-module-lexer": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz",
+ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q=="
+ },
+ "schema-utils": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "webpack": {
+ "version": "5.89.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
+ "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
+ "requires": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^1.0.0",
+ "@webassemblyjs/ast": "^1.11.5",
+ "@webassemblyjs/wasm-edit": "^1.11.5",
+ "@webassemblyjs/wasm-parser": "^1.11.5",
+ "acorn": "^8.7.1",
+ "acorn-import-assertions": "^1.9.0",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.15.0",
+ "es-module-lexer": "^1.2.1",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-even-better-errors": "^2.3.1",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.2.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.3.7",
+ "watchpack": "^2.4.0",
+ "webpack-sources": "^3.2.3"
+ }
+ }
+ }
+ },
"nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
@@ -27657,17 +28051,33 @@
"find-up": "^4.0.0"
}
},
+ "playwright": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz",
+ "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==",
+ "dev": true,
+ "requires": {
+ "fsevents": "2.3.2",
+ "playwright-core": "1.39.0"
+ }
+ },
+ "playwright-core": {
+ "version": "1.39.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz",
+ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==",
+ "dev": true
+ },
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
},
"postcss": {
- "version": "8.4.18",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
- "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"requires": {
- "nanoid": "^3.3.4",
+ "nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
@@ -28498,7 +28908,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
"requires": {
"safe-buffer": "^5.1.0"
}
@@ -29155,10 +29564,9 @@
}
},
"serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
- "dev": true,
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
"requires": {
"randombytes": "^2.1.0"
}
@@ -29376,8 +29784,7 @@
"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
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-js": {
"version": "1.0.2",
@@ -29410,7 +29817,6 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@@ -29660,8 +30066,7 @@
"tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
- "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
- "dev": true
+ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
},
"tar": {
"version": "6.1.12",
@@ -29689,28 +30094,37 @@
}
},
"terser-webpack-plugin": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
- "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
- "dev": true,
+ "version": "5.3.9",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
+ "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
"requires": {
- "@jridgewell/trace-mapping": "^0.3.14",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
- "serialize-javascript": "^6.0.0",
- "terser": "^5.14.1"
+ "serialize-javascript": "^6.0.1",
+ "terser": "^5.16.8"
},
"dependencies": {
"schema-utils": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
- "dev": true,
"requires": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
}
+ },
+ "terser": {
+ "version": "5.22.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
+ "integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
+ "requires": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ }
}
}
},
@@ -30041,7 +30455,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
- "dev": true,
"requires": {
"punycode": "^2.1.0"
}
@@ -30071,6 +30484,11 @@
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
},
+ "valibot": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.17.0.tgz",
+ "integrity": "sha512-ykoYfBc4HOH+osdWaFBmeL7yX35bFfKg1WdwHYZhNVuvGIRH5f2gtYT6cnU85XOsOE7R8la5RnVY6yAScWU9qg=="
+ },
"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",
@@ -30140,7 +30558,6 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
- "dev": true,
"requires": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@@ -30183,7 +30600,6 @@
"version": "5.74.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
"integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
- "dev": true,
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
@@ -30215,7 +30631,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
"integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
- "dev": true,
"requires": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@@ -30403,8 +30818,7 @@
"webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
- "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
- "dev": true
+ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="
},
"webpack-subresource-integrity": {
"version": "5.1.0",
diff --git a/package.json b/package.json
index a4c982900..7f44a6962 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
"mississippi": "^4.0.0",
"ngx-bootstrap": "^9.0.0",
"ngx-countup": "^13.0.0",
+ "ngx-pipes": "^3.2.2",
"nodemailer": "^6.7.8",
"popper.js": "^1.16.0",
"referrer-policy": "^1.2.0",
@@ -81,6 +82,7 @@
"through2": "^4.0.2",
"timers": "^0.1.1",
"tslib": "^2.1.0",
+ "valibot": "^0.17.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
@@ -98,6 +100,7 @@
"@babel/preset-env": "^7.16.11",
"@fortawesome/fontawesome-free": "^6.0.0",
"@ngx-i18nsupport/ngx-i18nsupport": "^1.1.6",
+ "@playwright/test": "^1.39.0",
"@types/d3": "^7.1.0",
"@types/express": "^4.17.11",
"@types/jasmine": "~3.6.6",
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 000000000..301801ee1
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,77 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ // baseURL: 'http://127.0.0.1:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+
+ {
+ name: 'firefox',
+ use: { ...devices['Desktop Firefox'] },
+ },
+
+ {
+ name: 'webkit',
+ use: { ...devices['Desktop Safari'] },
+ },
+
+ /* Test against mobile viewports. */
+ // {
+ // name: 'Mobile Chrome',
+ // use: { ...devices['Pixel 5'] },
+ // },
+ // {
+ // name: 'Mobile Safari',
+ // use: { ...devices['iPhone 12'] },
+ // },
+
+ /* Test against branded browsers. */
+ // {
+ // name: 'Microsoft Edge',
+ // use: { ...devices['Desktop Edge'], channel: 'msedge' },
+ // },
+ // {
+ // name: 'Google Chrome',
+ // use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+ // },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ // webServer: {
+ // command: 'npm run start',
+ // url: 'http://127.0.0.1:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});
diff --git a/src/app/portal/components/collapsible/collapsible.component.html b/src/app/portal/components/collapsible/collapsible.component.html
new file mode 100644
index 000000000..449cf2a1a
--- /dev/null
+++ b/src/app/portal/components/collapsible/collapsible.component.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ +
+
+
+
+ {{label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/portal/components/collapsible/collapsible.component.scss b/src/app/portal/components/collapsible/collapsible.component.scss
new file mode 100644
index 000000000..372b2fdd0
--- /dev/null
+++ b/src/app/portal/components/collapsible/collapsible.component.scss
@@ -0,0 +1,39 @@
+.collapsible-container {
+ background-color: rgb(247, 247, 251);
+}
+
+.collapsible-label {
+ font-size: 1.1rem;
+ font-weight: bolder;
+
+ padding: 11px 16px;
+ border-radius: 4px;
+
+ user-select: none;
+ cursor: pointer;
+
+ display: flex;
+ justify-content: space-between;
+}
+
+.icon-collapsed {
+ transition: transform 0.25s linear;
+ transform: rotate(0deg);
+}
+
+.icon-expanded {
+ transition: transform 0.25s ease-out;
+ transform: rotate(180deg);
+}
+
+.collapsible-content {
+ transition: max-height 0.4s ease-out;
+ overflow: hidden;
+
+ max-height: 1000px;
+}
+
+.collapsed {
+ // transition: 0.2s linear;
+ max-height: 0;
+}
diff --git a/src/app/portal/components/collapsible/collapsible.component.ts b/src/app/portal/components/collapsible/collapsible.component.ts
new file mode 100644
index 000000000..be78034f8
--- /dev/null
+++ b/src/app/portal/components/collapsible/collapsible.component.ts
@@ -0,0 +1,39 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { NgClass, NgIf } from '@angular/common';
+import { MatIconModule } from '@angular/material/icon';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { TooltipModule } from 'ngx-bootstrap/tooltip';
+
+@Component({
+ selector: 'app-collapsible',
+ templateUrl: './collapsible.component.html',
+ styleUrls: ['./collapsible.component.scss'],
+ imports: [
+ NgIf,
+ MatIconModule,
+ NgClass,
+ TooltipModule
+ ],
+ standalone: true,
+ animations: [
+ trigger('expandCollapse', [
+ state('collapsed', style({ height: '0px', overflow: 'hidden' })),
+ state('expanded', style({ height: '*', overflow: 'hidden' })),
+ transition('expanded <=> collapsed', animate('0.1s ease-out'))
+ ])
+ ]
+})
+export class CollapsibleComponent {
+ @Input() label = '';
+ @Input() decoration = "none";
+
+ @Input() isOpen = false;
+ @Output() isOpenChange = new EventEmitter();
+
+ @Input() hasTooltip = false;
+
+ toggle() {
+ this.isOpen = !this.isOpen;
+ this.isOpenChange.emit(this.isOpen);
+ }
+}
diff --git a/src/app/portal/components/filter-limit-button/filter-limit-button.component.html b/src/app/portal/components/filter-limit-button/filter-limit-button.component.html
new file mode 100644
index 000000000..33c7cbcf2
--- /dev/null
+++ b/src/app/portal/components/filter-limit-button/filter-limit-button.component.html
@@ -0,0 +1,16 @@
+
+
+
+
+ 0">
+ = max' (click)='valueChange.emit(clamp(value + step))'>
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/portal/components/filter-limit-button/filter-limit-button.component.scss b/src/app/portal/components/filter-limit-button/filter-limit-button.component.scss
new file mode 100644
index 000000000..b13f833f6
--- /dev/null
+++ b/src/app/portal/components/filter-limit-button/filter-limit-button.component.scss
@@ -0,0 +1,7 @@
+.limit-button {
+ height: 3rem;
+}
+
+.limit-button:enabled {
+ color: #4546b9;
+}
diff --git a/src/app/portal/components/filter-limit-button/filter-limit-button.component.spec.ts b/src/app/portal/components/filter-limit-button/filter-limit-button.component.spec.ts
new file mode 100644
index 000000000..be14282fd
--- /dev/null
+++ b/src/app/portal/components/filter-limit-button/filter-limit-button.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FilterLimitButtonComponent } from './filter-limit-button.component';
+
+describe('FilterLimitButtonComponent', () => {
+ let component: FilterLimitButtonComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ FilterLimitButtonComponent ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(FilterLimitButtonComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/portal/components/filter-limit-button/filter-limit-button.component.ts b/src/app/portal/components/filter-limit-button/filter-limit-button.component.ts
new file mode 100644
index 000000000..3d0e481ee
--- /dev/null
+++ b/src/app/portal/components/filter-limit-button/filter-limit-button.component.ts
@@ -0,0 +1,23 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatButtonModule } from '@angular/material/button';
+
+@Component({
+ selector: 'app-filter-limit-button',
+ standalone: true,
+ imports: [CommonModule, MatButtonModule],
+ templateUrl: './filter-limit-button.component.html',
+ styleUrls: ['./filter-limit-button.component.scss']
+})
+export class FilterLimitButtonComponent {
+ @Input() value = 0;
+ @Output() valueChange = new EventEmitter();
+
+ @Input() step = 0;
+ @Input() min = 0;
+ @Input() max = 0;
+
+ clamp(value: number) {
+ return Math.min(Math.max(value, this.min), this.max);
+ }
+}
diff --git a/src/app/portal/components/filter-option/filter-option.component.html b/src/app/portal/components/filter-option/filter-option.component.html
new file mode 100644
index 000000000..5ba7644a1
--- /dev/null
+++ b/src/app/portal/components/filter-option/filter-option.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ {{label}}
+
+
+
+ {{count}}
+
+
+
diff --git a/src/app/portal/components/filter-option/filter-option.component.scss b/src/app/portal/components/filter-option/filter-option.component.scss
new file mode 100644
index 000000000..0fcbfe167
--- /dev/null
+++ b/src/app/portal/components/filter-option/filter-option.component.scss
@@ -0,0 +1,20 @@
+.label-text {
+ text-overflow: ellipsis;
+ flex-shrink: 1;
+}
+
+.filter-option-layout {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ // padding: 16px 12px;
+ padding: 8px;
+
+ cursor: pointer;
+ user-select: none;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.04);
+ }
+}
diff --git a/src/app/portal/components/filter-option/filter-option.component.ts b/src/app/portal/components/filter-option/filter-option.component.ts
new file mode 100644
index 000000000..b9d37ef15
--- /dev/null
+++ b/src/app/portal/components/filter-option/filter-option.component.ts
@@ -0,0 +1,28 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { MatRippleModule } from '@angular/material/core';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+
+@Component({
+ selector: 'app-filter-option',
+ templateUrl: './filter-option.component.html',
+ styleUrls: ['./filter-option.component.scss'],
+ imports: [
+ FormsModule,
+ MatRippleModule,
+ MatCheckboxModule
+ ],
+ standalone: true
+})
+export class FilterOptionComponent {
+ @Input() label = "";
+ @Input() count = 0;
+
+ @Input() value: boolean = false;
+ @Output() valueChange = new EventEmitter();
+
+ toggleValue() {
+ this.value = !this.value;
+ this.valueChange.emit(this.value);
+ }
+}
diff --git a/src/app/portal/components/organization-filter/organization-filter.component.html b/src/app/portal/components/organization-filter/organization-filter.component.html
new file mode 100644
index 000000000..58bb1c93e
--- /dev/null
+++ b/src/app/portal/components/organization-filter/organization-filter.component.html
@@ -0,0 +1,35 @@
+
+
+
+
diff --git a/src/app/portal/components/organization-filter/organization-filter.component.scss b/src/app/portal/components/organization-filter/organization-filter.component.scss
new file mode 100644
index 000000000..dd50f90b7
--- /dev/null
+++ b/src/app/portal/components/organization-filter/organization-filter.component.scss
@@ -0,0 +1,53 @@
+.example-accordion {
+ display: block;
+ max-width: 500px;
+}
+
+.example-accordion-item {
+ display: block;
+ border: solid 1px #ccc;
+}
+
+.example-accordion-item + .example-accordion-item {
+ border-top: none;
+}
+
+.example-accordion-item-header {
+ display: flex;
+ align-content: center;
+ justify-content: space-between;
+}
+
+.example-accordion-item-description {
+ font-size: 0.85em;
+ color: #999;
+}
+
+/*.example-accordion-item-header,
+.example-accordion-item-body {
+ padding: 16px;
+}*/
+
+.example-accordion-item-header:hover {
+ cursor: pointer;
+ background-color: #eee;
+}
+
+.example-accordion-item:first-child {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+}
+
+.example-accordion-item:last-child {
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+/**/
+
+.filter-heading {
+ font-size: 17.6px;
+ font-weight: 700;
+
+ padding: 11px 16px;
+}
diff --git a/src/app/portal/components/organization-filter/organization-filter.component.ts b/src/app/portal/components/organization-filter/organization-filter.component.ts
new file mode 100644
index 000000000..fa735d5d8
--- /dev/null
+++ b/src/app/portal/components/organization-filter/organization-filter.component.ts
@@ -0,0 +1,56 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
+import { FilterOptionComponent } from '@portal/components/filter-option/filter-option.component';
+import { NgArrayPipesModule } from 'ngx-pipes';
+import { FormsModule } from '@angular/forms';
+import { LimitPipe } from '@portal/pipes/limit.pipe';
+import { CdkAccordionModule } from '@angular/cdk/accordion';
+import { CollapsibleComponent } from '@portal/components/collapsible/collapsible.component';
+
+@Component({
+ selector: 'app-organization-filter',
+ templateUrl: './organization-filter.component.html',
+ styleUrls: ['./organization-filter.component.scss'],
+ imports: [
+ NgIf,
+ AsyncPipe,
+ FilterOptionComponent,
+ LimitPipe,
+ NgArrayPipesModule,
+ NgForOf,
+ FormsModule,
+ CdkAccordionModule,
+ CollapsibleComponent
+ ],
+ standalone: true
+})
+export class OrganizationFilterComponent {
+ @Input() filterData: unknown;
+ @Output() selected = new EventEmitter();
+
+ selectedSector = -1;
+
+ expanded = false;
+ sectorFilter: Record = {};
+
+ sectorLimits: Record = {
+ 1: 10,
+ 2: 10,
+ 3: 10,
+ 4: 10,
+ 6: 10,
+ };
+
+ /* TODO localization solution */ // TODO ?
+ public sectorNames = {
+ 1: $localize`:@@organizationSector1:Yliopisto`,
+ 2: $localize`:@@organizationSector2:Ammattikorkeakoulu`,
+ 3: $localize`:@@organizationSector3:Tutkimuslaitos`,
+ 4: $localize`:@@organizationSector4:Yliopistollisen sairaalan erityisvastuualue`,
+ 6: $localize`:@@organizationSector5:Muu`
+ }
+
+ toggleExpanded() {
+ this.expanded = !this.expanded;
+ }
+}
diff --git a/src/app/portal/components/results/publications2/publications2.component.html b/src/app/portal/components/results/publications2/publications2.component.html
new file mode 100644
index 000000000..4eeab11ed
--- /dev/null
+++ b/src/app/portal/components/results/publications2/publications2.component.html
@@ -0,0 +1,951 @@
+
+
+
+
+
+
+
Rajaa hakua
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Näytä enemmän
+
+
+
+ Näytä vähemmän
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tilastokeskuksen tieteenalaluokitus. Taiteenalat OKM:n luokituksen mukaisesti. Julkaisulla voi olla 1-6 tieteen- tai taiteenalaa.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OKM:n julkaisutiedonkeruun mukainen julkaisutyyppi A–G.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Artikkeli:
+ Sisältää alkuperäis- ja katsausartikkelit, kirjan tai lehden johdannot ja esipuheet, lyhyet tutkimusselostukset, pääkirjoitukset, keskustelupuheenvuorot ja kommentit.
+
+
+
+ Erillisteos:
+ Sisältää monografiat/kirjat, tutkimus- ja kehitystyöhön perustuva kehittämis- tai tutkimusraportti, selvitykset, ns. white paperit sekä working papers ja discussion papers -tyyppiset julkaisut.
+
+
+
+
+ Toimitustyö:
+ Sisältää useista eri kirjoittajien artikkeleista koostuvan tieteellisen kirjan tai lehden erikoisnumeron toimitustyöt.
+
+
+
+ Abstrakti:
+ Sisältää konferenssiesitelmien abstraktit sekä laajennetut abstraktit.
+
+
+
+ Posteri:
+ Sisältää konferenssiesitelmien posterit.
+
+
+
+ Blogikirjoitus:
+ Sisältää blogimuotoiset julkaisut, joiden julkaisemisesta on päättänyt riippumaton toimituskunta tai joiden julkaisualustalla on ISSN-tunnus.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Julkaisukanavan kohdeyleisö
+
+
+
+ Tieteellinen julkaisu:
+ Julkaisut, jotka on tarkoitettu edistämään tiedettä sekä tuottamaan uutta tietoa.
+
+
+
+ Ammatillinen julkaisu:
+ Julkaisut, jotka levittävät tutkimukseen ja kehitystyöhön perustuvaa tietoa ammattiyhteisön käyttöön.
+
+
+
+ Yleistajuinen julkaisu:
+ Julkaisut, jotka levittävät tutkimus- ja kehitystyöhön perustuvaa tietoa suurelle yleisölle ja joiden sisällön ymmärtäminen ei edellytä erityistä perehtyneisyyttä alaan.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lehti:
+ sisältää tieteelliset aikakauslehdet ja ammattilehdet.
+
+
+
+ Kokoomateos:
+ Sisältää tieteelliset kokoomateokset, tieteelliset vuosikirjat ja vastaavat, ammatilliset käsi- tai opaskirjat, ammatilliset tietojärjestelmät tai kokoomateokset, oppikirja-aineistot sekä lyhyet ensyklopediatekstit.
+
+
+
+ Konferenssi:
+ Sisältää konferenssin painetut tai julkisesti saatavilla olevat julkaisut, ns. proceedings-julkaisut.
+
+
+
+ Verkkoalusta:
+ Sisältää muilla sähköisillä alustoilla julkaistut julkaisut.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Alkuperäisartikkeli:
+
+ on pääosin aiemmin julkaisemattomasta materiaalista koostuva tieteellinen artikkeli.
+
+
+
+ Katsausartikkeli:
+
+ perustuu aikaisempiin samasta aihepiiristä tehtyihin julkaisuihin.
+
+
+
+ Data-artikkeli:
+
+ sisältää ns. data journals -julkaisuissa ilmestyneet, tutkimusaineistoja kuvailevat artikkelit.
+
+
+
+ Muu artikkeli:
+
+ sisältää muihin luokkiin kuulumattomat artikkelit.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tieteellisten julkaisujen vertaisarvioinnilla tarkoitetaan menettelyä, jossa tutkimustuloksia julkaiseva lehti, konferenssi tai kirjakustantaja pyytää tieteenalan asiantuntijoita suorittamaan ennakkoarvion julkaistavaksi tarjottujen käsikirjoitusten tieteellisestä julkaisukelpoisuudesta.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kotimaisen julkaisun kustantaja on suomalainen tai se on ensisijaisesti julkaistu Suomessa. Kansainvälisen julkaisun kustantaja ei ole suomalainen tai se on ensisijaisesti julkaistu muualla kuin Suomessa.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kieli, jolla julkaisu on kirjoitettu.
+
+
+
+
+
+
+
+
+
+
+ Näytä enemmän
+
+
+
+ Näytä vähemmän
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Julkaisufoorumin (www.julkaisufoorumi.fi) mukainen julkaisukanavan (kirjakustantaja, konferenssi tai julkaisusarja) tasoluokitus: 1 = perustaso, 2 = johtava taso, 3 = korkein taso. Tasolla 0 ovat kanavat, jotka eivät joltain osin täytä tason 1 vaatimuksia tai ovat uusia. Julkaisufoorumitaso määräytyy julkaisun julkaisuvuoden mukaan.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Avoimesti saatavilla:
+ Julkaisu on avoimesti saatavilla kustantajan palvelussa.
+
+
+
+ Ei avoin:
+ Julkaisu ei ole avoimesti saatavilla kustantajan palvelussa.
+
+
+
+ Ei tietoa:
+ Tietoa ei ole raportoitu.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Kokonaan avoin julkaisukanava:
+ Julkaisukanavan kaikki julkaisut ovat pysyvästi avoimesti saatavilla.
+
+
+
+ Osittain avoin julkaisukanava:
+ Julkaisukanavassa vain osa julkaisuista on pysyvästi avoimesti saatavilla.
+
+
+
+ Viivästetysti avoin julkaisukanava:
+ Julkaisukanavan tieteelliset artikkelit avataan kustantajan määrittelemän viiveajan jälkeen.
+
+
+
+ Ei vastausta:
+ Julkaisukanava ei ole kokonaan, osittain tai viivästetysti avoin.
+
+
+
+ Ei tietoa:
+ Tietoa ei ole raportoitu.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Rinnakkaistallennettu:
+ Julkaisu on tallennettu organisaatio- tai tieteenalakohtaiseen julkaisuarkistoon joko välittömästi tai kustantajan määrittämän kohtuullisen embargoajan jälkeen.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Testaa uudistetun haun betaversiota. Uudistettua hakua on toistaiseksi mahdollista testata ainoastaan julkaisutietojen hakemisessa.
+
+
+
+ Kirjoita hakukenttään esimerkiksi julkaisun tai tekijän nimi, tai julkaisuun liittyvä asiasana.
+
+
+
+ Haku käyttää sanojen välissä oletuksena OR-hakuoperaattioria. Halutessasi voit käyttää myös AND- ja NOT- hakuoperaattoireita. Esimerkiksi haku Innovation AND gamification NOT games etsii julkaisuja, joissa esiintyvät yhtä aikaa sanat "innovation" ja "gamification", mutta joissa ei ole mukana sanaa "games". Huomioithan, että AND, NOT ja OR -hakuoperaattorit tulee kirjoittaa hakukenttään isoilla kirjaimilla.
+
+
+
+ Sulkemalla haussa esiintyvät sanat lainausmerkeillä fraasiksi voit etsiä osumia jotka ovat täsmälleen samassa muodossa. Esimerkiksi fraasihaku "Innovations in gamifications" etsii julkaisuja, joissa esiintyy juuri tämä lause. Voit myös yhdistää useampia fraasihakuja AND-, OR- tai NOT-hakuoperaattoreilla.
+
+
+
+ Voit hakea kohdistetusti joitakin metatietoja suoraan hakukentässä käyttämällä niille erikseen määriteltyjä komentoja. Haussa on käytettävä fraasihakua. Toistaiseksi haku on mahdollista kohdistaa seuraaviin metatietoihin:
+
+
+
+
+ DOI, kirjoita hakukenttään doi:"haettava tunnus", esimerkiksi doi:"100100100"
+
+
+
+ ISSN, kirjoita hakukenttään issn:"haettava tunnnus", esimerkiksi issn:"1234-1234"
+
+
+
+ ISBN, kirjoita hakukenttään isbn:"haettava tunnus", esimerkiksi isbn:"123-0-456-12345-1"
+
+
+
+ JUFO-tunnus, kirjoita hakukenttään jufoCode:"haettava tunnus", esimerkiksi jufoCode:"12345"
+
+
+
+
+ Huomioithan, että kohdistetussa haussa metatietokentät tulee kirjoittaa pienillä kirjaimilla.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Olet haun uudistetussa betaversiossa. Voit palata takaisin koko sivustolle.
+
+
+
+
+
+
+
+
+
+
+
+
Julkaisut - {{total$ | async}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0'>
+
+
Rajaukset ({{searchTermsCount$ | async}})
+
+
+
+
+
+
+
+
+
+
{{organizationFilter.name}}
+
+
+
+
+
+
+
+
{{fieldsOfScienceFilter.name}}
+
+
+
+
+
+
+
+
{{publicationTypeCodeFilter.name}}
+
+
+
+
+
+
+
+
{{languageCodeFilter.name}}
+
+
+
+
+
+
+
+
{{publicationFormatFilter.name}}
+
+
+
+
+
+
+
+
{{publicationAudienceFilter.name}}
+
+
+
+
+
+
+
+
{{peerReviewedFilter.name}}
+
+
+
+
+
+
+
+
{{parentPublicationTypeFilter.name}}
+
+
+
+
+
+
+
+
{{internationalPublicationFilter.name}}
+
+
+
+
+
+
+
+
{{articleTypeCodeFilter.name}}
+
+
+
+
+
+
+
+
{{jufoClassCodeFilter}}
+
+
+
+
+
+
+
+
+
+
{{openAccessFilter.name}}
+
+
+
+
+
+
+
+
{{publisherOpenAccessFilter.name}}
+
+
+
+
+
+
+
+
{{selfArchivedCodeFilter.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Julkaisun nimi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Julkaisukanava
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Julkaisuvuosi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/portal/components/results/publications2/publications2.component.scss b/src/app/portal/components/results/publications2/publications2.component.scss
new file mode 100644
index 000000000..c83dc3cbb
--- /dev/null
+++ b/src/app/portal/components/results/publications2/publications2.component.scss
@@ -0,0 +1,131 @@
+.search-container {
+ display: grid;
+
+ grid-template-rows:
+ auto
+ minmax(0, auto)
+ minmax(0, auto)
+ minmax(0, auto)
+ minmax(0, auto);
+
+ grid-template-columns: 1fr minmax(0, 350px) minmax(0, 1100px) 1fr;
+
+ grid-template-areas:
+ "search search search search"
+ ". info info ."
+ ". filters results .";
+}
+
+@media (max-width: 1200px) {
+ .search-container {
+ grid-template-columns: 350px minmax(0, 1fr);
+
+ grid-template-areas:
+ "search search"
+ "info info"
+ "filters results"
+ }
+}
+
+@media (max-width: 990px) {
+ .search-container {
+ grid-template-columns: minmax(0, 1fr);
+
+ grid-template-areas:
+ "search"
+ "info"
+ "filters-toggle"
+ "results"
+ }
+}
+
+.search-bar {
+ grid-area: search;
+}
+
+.search-tabs {
+ grid-area: tabs;
+}
+
+.search-info {
+ grid-area: info;
+}
+
+.search-filters {
+ grid-area: filters;
+}
+
+.search-filters-toggle {
+ grid-area: filters-toggle;
+}
+
+.search-pagination {
+ display: flex;
+ justify-content: center;
+}
+
+.search-combination {
+ grid-area: results;
+}
+
+/* Ensure the table takes the full width */
+cdk-table {
+ // width: 100%;
+}
+
+/* Add some spacing and border to the cells */
+cdk-cell {
+ padding: 1rem 0.5rem;
+ display: table-cell;
+ text-align: start;
+}
+
+cdk-header-cell {
+ padding: 0.25rem 0.5rem 1rem 0.5rem;
+ display: table-cell;
+ text-align: start;
+
+ position: sticky;
+ top: 0;
+ background: white;
+ z-index: 1;
+}
+
+/* Define the header row and data row to behave like traditional table rows */
+cdk-header-row, cdk-row {
+ display: table-row;
+}
+
+
+
+/* Define columns to ensure they take equal widths - adjust as required */
+/*[cdkColumnDef="publicationName"] {
+ width: 35%;
+ background-color: red;
+}
+
+[cdkColumnDef="authorsText"] {
+ width: 25%;
+}
+
+[cdkColumnDef="publisherName"] {
+ width: 20%;
+}
+
+[cdkColumnDef="publicationYear"] {
+ width: 20%;
+}*/
+
+/*em {
+ background-color: #FFFF00;
+ font-style: normal;
+ padding: 0.1em 0;
+}*/
+
+.filters-summary {
+ background-color: #e8e8f5;
+ padding: 16px;
+ border: 1px solid #4546b9;
+ border-radius: 4px;
+ margin: 0 0 1.5rem 1.5rem;
+}
diff --git a/src/app/portal/components/results/publications2/publications2.component.ts b/src/app/portal/components/results/publications2/publications2.component.ts
new file mode 100644
index 000000000..69d842b9a
--- /dev/null
+++ b/src/app/portal/components/results/publications2/publications2.component.ts
@@ -0,0 +1,714 @@
+import { Component, inject, OnDestroy, TemplateRef, ViewChild } from '@angular/core';
+import { CdkTableModule, DataSource } from '@angular/cdk/table';
+import { BehaviorSubject, combineLatest, interval, Observable } from 'rxjs';
+import { ActivatedRoute, Router, RouterLink, RouterModule } from '@angular/router';
+import { FormsModule } from '@angular/forms';
+import { AsyncPipe, JsonPipe, NgForOf, NgIf, NgStyle, NgTemplateOutlet } from '@angular/common';
+import {
+ getArticleTypeCodeAdditions,
+ getFieldsOfScienceAdditions,
+ getJufoClassCodeAdditions,
+ getLanguageCodeAdditions,
+ getOpenAccessAdditions,
+ getOrganizationAdditions,
+ getParentPublicationTypeAdditions,
+ getPeerReviewedAdditions,
+ getPublicationAudienceAdditions,
+ getPublicationFormatAdditions,
+ getPublicationTypeCodeAdditions,
+ getPublisherInternationalityAdditions,
+ getPublisherOpenAccessCodeAdditions,
+ getSelfArchivedCodeAdditions,
+ getYearAdditions,
+ HighlightedPublication,
+ Publication2Service, SearchParams
+} from '@portal/services/publication2.service';
+import { filter, map, take, tap } from 'rxjs/operators';
+import { SharedModule } from '@shared/shared.module';
+import { SearchBar2Component } from '@portal/search-bar2/search-bar2.component';
+import { NgArrayPipesModule, NgMathPipesModule } from 'ngx-pipes';
+import { OrganizationFilterComponent } from '@portal/components/organization-filter/organization-filter.component';
+import { FilterOptionComponent } from '@portal/components/filter-option/filter-option.component';
+import { LimitPipe } from '@portal/pipes/limit.pipe';
+import { CollapsibleComponent } from '@portal/components/collapsible/collapsible.component';
+import { MatButtonModule } from '@angular/material/button';
+import { FilterLimitButtonComponent } from '@portal/components/filter-limit-button/filter-limit-button.component';
+import { FirstDigitPipe } from '@shared/pipes/first-digit.pipe';
+import { FirstLetterPipe } from '@shared/pipes/first-letter.pipe';
+import { BreakpointObserver, LayoutModule } from '@angular/cdk/layout';
+import { ColumnSorterComponent } from '@shared/components/column-sorter/column-sorter.component';
+import { faChartBar, faSlidersH } from '@fortawesome/free-solid-svg-icons';
+import { Dialog, DialogRef } from '@angular/cdk/dialog';
+import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
+
+@Component({
+ selector: 'app-publications2',
+ templateUrl: './publications2.component.html',
+ styleUrls: ['./publications2.component.scss'],
+ imports: [CdkTableModule, FormsModule, AsyncPipe, JsonPipe, NgForOf, NgIf, LimitPipe, NgArrayPipesModule,
+ SharedModule, //TODO do not depend on shared module
+ FormsModule,
+ RouterModule,
+ SearchBar2Component, OrganizationFilterComponent, FilterOptionComponent, CollapsibleComponent, MatButtonModule, NgStyle, FilterLimitButtonComponent, FirstDigitPipe, FirstLetterPipe, RouterLink,
+ LayoutModule, ColumnSorterComponent, NgTemplateOutlet, FontAwesomeModule, NgMathPipesModule
+ ],
+ standalone: true
+})
+export class Publications2Component implements OnDestroy {
+ route = inject(ActivatedRoute);
+ router = inject(Router);
+ publications2Service = inject(Publication2Service);
+ breakpointObserver = inject(BreakpointObserver);
+ dialog = inject(Dialog);
+
+ keywords = "";
+ page = 1;
+ size = 10;
+
+ sort = "";
+
+ dialogRef?: DialogRef;
+ @ViewChild('searchDialog') dialogTemplate: TemplateRef;
+
+ // Dialog for insturctions / tutorial
+ tutorialDialogRef?: DialogRef;
+ @ViewChild('tutorialDialog') tutorialDialogTemplate: TemplateRef;
+
+ openTutorialDialog() {
+ this.tutorialDialogRef = this.dialog.open(this.tutorialDialogTemplate, {
+ panelClass: 'large-responsive-panel',
+ });
+
+ this.tutorialDialogRef.closed.subscribe(() => {
+ console.log('The tutorial dialog was closed');
+ });
+ }
+
+ closeTutorialDialog() {
+ this.tutorialDialogRef?.close();
+ }
+
+ labelText = {
+ yearOfPublication: $localize`:@@yearOfPublication:Julkaisuvuosi`,
+ organization: $localize`:@@organization:Organisaatio`,
+ fieldOfScience: $localize`:@@fieldOfScience:Tieteenala`,
+ publicationType: $localize`:@@publicationType:Julkaisutyyppi`,
+ publicationFormat: $localize`:@@publicationFormat:Julkaisumuoto`,
+ publicationAudience: $localize`:@@publicationAudience:Julkaisun yleisö`,
+ parentPublicationType: $localize`:@@parentPublicationType:Emojulkaisun tyyppi`,
+ articleType: $localize`:@@articleType:Artikkelin tyyppi`,
+ peerReviewed: $localize`:@@peerReviewed:Vertaisarvioitu`,
+ publisherInternationality: $localize`:@@publisherInternationality:Kustantajan kansainvälisyys`,
+ language: $localize`:@@language:Kieli`,
+ jufoLevel: $localize`:@@jufoLevel:Julkaisufoorumitaso`,
+ openAccess: $localize`:@@openAccess:Avoin saatavuus`,
+ publisherOpenAccess: $localize`:@@publisherOpenAccess:Julkaisukanavan avoin saatavuus`,
+ selfArchivedCode: $localize`:@@selfArchivedCode:Rinnakkaistallenne`,
+ }
+
+ publicationTypeLabels = [
+ {id: "A", text: $localize`:@@publicationClassA:Vertaisarvioidut tieteelliset artikkelit`},
+ {id: "B", text: $localize`:@@publicationClassB:Vertaisarvioimattomat tieteelliset kirjoitukset`},
+ {id: "C", text: $localize`:@@publicationClassC:Tieteelliset kirjat`},
+ {id: "D", text: $localize`:@@publicationClassD:Ammattiyhteisölle suunnatut julkaisut`},
+ {id: "E", text: $localize`:@@publicationClassE:Suurelle yleisölle suunnatut julkaisut`},
+ {id: "F", text: $localize`:@@publicationClassF:Julkinen taiteellinen ja taideteollinen toiminta`},
+ {id: "G", text: $localize`:@@publicationClassG:Opinnäytteet`},
+ {id: "I", text: $localize`:@@publicationClassI:Audiovisuaaliset julkaisut ja tieto- ja viestintätekniset sovellukset`},
+ ];
+
+ faSlidersH = faSlidersH;
+ faChartBar = faChartBar;
+
+ openDialog() {
+ this.dialogRef = this.dialog.open(this.dialogTemplate, {
+ panelClass: 'fullscreen-panel',
+ });
+
+ this.dialogRef.closed.subscribe(() => {
+ console.log('The dialog2 was closed');
+ });
+ }
+
+ closeDialog() {
+ this.dialogRef?.close();
+ }
+
+ clearFilters() {
+ this.router.navigate([], {
+ relativeTo: this.route,
+ queryParams: {
+ q: this.keywords,
+ page: this.page.toString(),
+ size: this.size.toString(),
+ }
+ });
+ }
+
+ displayedColumns: string[] = ['icon', 'publicationName', 'authorsText', 'publisherName', 'publicationYear'];
+
+ highlights$ = this.publications2Service.getSearch(); // TODO: /*: Observable*/
+ dataSource = new PublicationDataSource(this.highlights$);
+
+ searchParams$ = this.route.queryParams.pipe( map(splitFields) );
+ aggregations$ = this.publications2Service.getAggregations();
+
+ yearAdditions$ = this.aggregations$.pipe(
+ map(aggs =>
+ getYearAdditions(aggs).map(
+ (bucket: any) => ({ year: bucket.key.toString(), count: bucket.doc_count })
+ ) ?? []
+ ),
+ map(aggs => aggs.sort((a, b) => b.year - a.year))
+ );
+
+ enabledYearFilters$ = this.searchParams$.pipe(map(params => params.year ?? []));
+
+ yearFilters$ = combineLatest([this.yearAdditions$, this.enabledYearFilters$]).pipe(
+ map(([yearAdditions, enabledFilters]) => yearAdditions.map(yearAddition => ({
+ year: yearAddition.year,
+ count: yearAddition.count,
+ enabled: enabledFilters.includes(yearAddition.year.toString())
+ })))
+ );
+
+ organizationNames$ = this.publications2Service.getOrganizationNames();
+
+ organizationAdditions$ = this.aggregations$.pipe(
+ map(aggs => getOrganizationAdditions(aggs).map((bucket: any) => ({ id: bucket.key, count: bucket.doc_count})) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledOrganizationFilters$ = this.searchParams$.pipe(map(params => params.organization ?? []));
+
+ enabledOrganizationFiltersWithNames$ = combineLatest([this.enabledOrganizationFilters$, this.organizationNames$]).pipe(
+ map(([enabledFilters, organizationNames]) => enabledFilters.map(filter => ({ id: filter, name: organizationNames[filter].name })))
+ );
+
+ organizationFilters$ = combineLatest([this.organizationAdditions$, this.organizationNames$, this.enabledOrganizationFilters$]).pipe(
+ map(([organizationAdditions, organizationNames, enabledFilters]) => organizationAdditions.map(organizationAddition => ({
+ id: organizationAddition.id,
+ count: organizationAddition.count,
+ name: organizationNames[organizationAddition.id].name,
+ sectorId: organizationNames[organizationAddition.id].sectorId,
+ enabled: enabledFilters.includes(organizationAddition.id)
+ })))
+ );
+
+ languageCodeAdditions$ = this.aggregations$.pipe(
+ map(aggs => getLanguageCodeAdditions(aggs).map((bucket: any) => ({ languageCode: bucket.key, count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ languageCodeNames$ = this.publications2Service.getLanguageCodeNames();
+
+ enabledLanguageCodeFilters$ = this.searchParams$.pipe(map(params => params.language ?? []));
+
+ enabledLanguageCodeFiltersWithNames$ = combineLatest([this.enabledLanguageCodeFilters$, this.languageCodeNames$]).pipe(
+ map(([enabledFilters, languageCodeNames]) => enabledFilters.map(filter => ({ id: filter, name: languageCodeNames[filter] })))
+ );
+
+ languageCodeFilters$ = combineLatest([this.languageCodeAdditions$, this.languageCodeNames$, this.enabledLanguageCodeFilters$]).pipe(
+ map(([languageCodeAdditions, languageCodeNames, enabledFilters]) => languageCodeAdditions.map(languageCodeAddition => ({
+ id: languageCodeAddition.languageCode,
+ count: languageCodeAddition.count,
+ name: languageCodeNames[languageCodeAddition.languageCode],
+ enabled: enabledFilters.includes(languageCodeAddition.languageCode)
+ })))
+ );
+
+ publicationFormatNames$ = this.publications2Service.getPublicationFormatNames();
+
+ publicationFormatAdditions$ = this.aggregations$.pipe(
+ map(aggs => getPublicationFormatAdditions(aggs).map((bucket: any) => ({ id: bucket.key, count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.filter(publicationFormatAddition => publicationFormatAddition.id !== "-1")),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledPublicationFormatFilters$ = this.searchParams$.pipe(map(params => params.format ?? []));
+
+ enabledPublicationFormatFiltersWithNames$ = combineLatest([this.enabledPublicationFormatFilters$, this.publicationFormatNames$]).pipe(
+ map(([enabledFilters, publicationFormatNames]) => enabledFilters.map(filter => ({ id: filter, name: publicationFormatNames[filter] })))
+ );
+
+ publicationFormatFilters$ = combineLatest([this.publicationFormatAdditions$, this.publicationFormatNames$, this.enabledPublicationFormatFilters$]).pipe(
+ map(([publicationFormatAdditions, publicationFormatNames, enabledFilters]) => publicationFormatAdditions.map(publicationFormatAddition => ({
+ id: publicationFormatAddition.id,
+ count: publicationFormatAddition.count,
+ name: publicationFormatNames[publicationFormatAddition.id],
+ enabled: enabledFilters.includes(publicationFormatAddition.id)
+ })))
+ );
+
+ publicationAudienceNames$ = this.publications2Service.getPublicationAudienceNames();
+
+ publicationAudienceAdditions$ = this.aggregations$.pipe(
+ map(aggs => getPublicationAudienceAdditions(aggs).map((bucket: any) => ({ id: bucket.key, count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.filter(publicationAudienceAddition => publicationAudienceAddition.id !== "-1")),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledPublicationAudienceFilters$ = this.searchParams$.pipe(map(params => params.audience ?? []));
+
+ enabledPublicationAudienceFiltersWithNames$ = combineLatest([this.enabledPublicationAudienceFilters$, this.publicationAudienceNames$]).pipe(
+ map(([enabledFilters, publicationAudienceNames]) => enabledFilters.map(filter => ({ id: filter, name: publicationAudienceNames[filter] })))
+ );
+
+ publicationAudienceFilters$ = combineLatest([this.publicationAudienceAdditions$, this.publicationAudienceNames$, this.enabledPublicationAudienceFilters$]).pipe(
+ map(([publicationAudienceAdditions, publicationAudienceNames, enabledFilters]) => publicationAudienceAdditions.map(publicationAudienceAddition => ({
+ id: publicationAudienceAddition.id,
+ count: publicationAudienceAddition.count,
+ name: publicationAudienceNames[publicationAudienceAddition.id],
+ enabled: enabledFilters.includes(publicationAudienceAddition.id)
+ })))
+ );
+
+ peerReviewedAdditions$ = this.aggregations$.pipe(
+ map(aggs => getPeerReviewedAdditions(aggs).map((bucket: any) => ({ id: bucket.key, count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.filter(peerReviewedAddition => ![' ', '-1'].includes(peerReviewedAddition.id))),
+ map(aggs => aggs.sort((a, b) => b.count - a.count)),
+ );
+
+ peerReviewedNames$ = this.publications2Service.getPeerReviewedNames();
+
+ enabledPeerReviewedFilters$ = this.searchParams$.pipe(map(params => params.peerReviewed ?? []));
+
+ enabledPeerReviewedFiltersWithNames$ = combineLatest([this.enabledPeerReviewedFilters$, this.peerReviewedNames$]).pipe(
+ map(([enabledFilters, peerReviewedNames]) => enabledFilters.map(filter => ({ id: filter, name: peerReviewedNames[filter] })))
+ );
+
+ peerReviewedFilters$ = combineLatest([this.peerReviewedAdditions$, this.peerReviewedNames$, this.enabledPeerReviewedFilters$]).pipe(
+ map(([peerReviewedAdditions, peerReviewedNames, enabledFilters]) => peerReviewedAdditions.map(peerReviewedAddition => ({
+ id: peerReviewedAddition.id,
+ count: peerReviewedAddition.count,
+ name: peerReviewedNames[peerReviewedAddition.id],
+ enabled: enabledFilters.includes(peerReviewedAddition.id)
+ })))
+ );
+
+ parentPublicationTypeAdditions$ = this.aggregations$.pipe(
+ map(aggs => getParentPublicationTypeAdditions(aggs).map((bucket: any) => ({ id: bucket.key, count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.filter(parentPublicationTypeAddition => parentPublicationTypeAddition.id !== "-1")),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ publisherInternationalityAdditions$ = this.aggregations$.pipe(
+ map(aggs => getPublisherInternationalityAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.filter(internationalPublicationAddition => internationalPublicationAddition.id !== "9")),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ publisherInternationalityNames$ = this.publications2Service.getInternationalPublicationNames();
+
+ enabledPublisherInternationalityFilters$ = this.searchParams$.pipe(map(params => params.international ?? []));
+
+ enabledPublisherInternationalityFiltersWithNames$ = combineLatest([this.enabledPublisherInternationalityFilters$, this.publisherInternationalityNames$]).pipe(
+ map(([enabledFilters, publisherInternationalityNames]) => enabledFilters.map(filter => ({ id: filter, name: publisherInternationalityNames[filter] })))
+ );
+
+ publisherInternationalityFilters$ = combineLatest([this.publisherInternationalityAdditions$, this.publisherInternationalityNames$, this.enabledPublisherInternationalityFilters$]).pipe(
+ map(([internationalPublicationAdditions, internationalPublicationNames, enabledFilters]) => internationalPublicationAdditions.map(internationalPublicationAddition => ({
+ id: internationalPublicationAddition.id,
+ count: internationalPublicationAddition.count,
+ name: internationalPublicationNames[internationalPublicationAddition.id],
+ enabled: enabledFilters.includes(internationalPublicationAddition.id)
+ })))
+ );
+
+ articleTypeCodeAdditions$ = this.aggregations$.pipe(
+ map(aggs => getArticleTypeCodeAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ articleTypeCodeNames$ = this.publications2Service.getArticleTypeCodeNames();
+
+ enabledArticleTypeCodeFilters$ = this.searchParams$.pipe(map(params => params.articleType ?? []));
+
+ enabledArticleTypeCodeFiltersWithNames$ = combineLatest([this.enabledArticleTypeCodeFilters$, this.articleTypeCodeNames$]).pipe(
+ map(([enabledFilters, articleTypeCodeNames]) => enabledFilters.map(filter => ({ id: filter, name: articleTypeCodeNames[filter] })))
+ );
+
+ articleTypeCodeFilters$ = combineLatest([this.articleTypeCodeAdditions$, this.articleTypeCodeNames$, this.enabledArticleTypeCodeFilters$]).pipe(
+ map(([articleTypeCodeAdditions, articleTypeCodeNames, enabledFilters]) => articleTypeCodeAdditions.map(articleTypeCodeAddition => ({
+ id: articleTypeCodeAddition.id,
+ count: articleTypeCodeAddition.count,
+ name: articleTypeCodeNames[articleTypeCodeAddition.id],
+ enabled: enabledFilters.includes(articleTypeCodeAddition.id)
+ })))
+ );
+
+ jufoClassCodeAdditions$ = this.aggregations$.pipe(
+ map(aggs => getJufoClassCodeAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledJufoClassCodeFilters$ = this.searchParams$.pipe(map(params => params.jufo ?? []));
+
+ jufoClassCodeFilters$ = combineLatest([this.jufoClassCodeAdditions$, this.enabledJufoClassCodeFilters$]).pipe(
+ map(([jufoClassCodeAdditions, enabledFilters]) => jufoClassCodeAdditions.map(jufoClassCodeAddition => ({
+ id: jufoClassCodeAddition.id,
+ count: jufoClassCodeAddition.count,
+ name: jufoClassCodeAddition.id,
+ enabled: enabledFilters.includes(jufoClassCodeAddition.id)
+ })))
+ );
+
+ fieldsOfScienceNames$ = this.publications2Service.getFieldsOfScienceNames();
+
+ fieldsOfScienceAdditions$ = this.aggregations$.pipe(
+ map(aggs => getFieldsOfScienceAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledFieldsOfScienceFilters$ = this.searchParams$.pipe(map(params => params.fieldsOfScience ?? []));
+
+ enabledFieldsOfScienceFiltersWithNames$ = combineLatest([this.enabledFieldsOfScienceFilters$, this.fieldsOfScienceNames$]).pipe(
+ map(([enabledFilters, fieldsOfScienceNames]) => enabledFilters.map(filter => ({ id: filter, name: fieldsOfScienceNames[filter] })))
+ );
+
+ fieldsOfScienceFilters$ = combineLatest([this.fieldsOfScienceAdditions$, this.fieldsOfScienceNames$, this.enabledFieldsOfScienceFilters$]).pipe(
+ map(([fieldsOfScienceAdditions, fieldsOfScienceNames, enabledFilters]) => fieldsOfScienceAdditions.map(fieldsOfScienceAddition => ({
+ id: fieldsOfScienceAddition.id,
+ count: fieldsOfScienceAddition.count,
+ name: fieldsOfScienceNames[fieldsOfScienceAddition.id],
+ enabled: enabledFilters.includes(fieldsOfScienceAddition.id)
+ })))
+ );
+
+ publicationTypeCodeNames$ = this.publications2Service.getPublicationTypeCodeNames();
+
+ publicationTypeCodeAdditions$ = this.aggregations$.pipe(
+ map(aggs => getPublicationTypeCodeAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ enabledPublicationTypeCodeFilters$ = this.searchParams$.pipe(map(params => params.publicationTypeCode ?? []));
+
+ enabledPublicationTypeCodeFiltersWithNames$ = combineLatest([this.enabledPublicationTypeCodeFilters$, this.publicationTypeCodeNames$]).pipe(
+ map(([enabledFilters, publicationTypeCodeNames]) => enabledFilters.map(filter => ({ id: filter, name: publicationTypeCodeNames[filter] })))
+ );
+
+ publicationTypeCodeFilters$ = combineLatest([this.publicationTypeCodeAdditions$, this.publicationTypeCodeNames$, this.enabledPublicationTypeCodeFilters$]).pipe(
+ map(([publicationTypeCodeAdditions, publicationTypeCodeNames, enabledFilters]) => publicationTypeCodeAdditions.map(publicationTypeCodeAddition => ({
+ id: publicationTypeCodeAddition.id,
+ count: publicationTypeCodeAddition.count,
+ name: publicationTypeCodeNames[publicationTypeCodeAddition.id],
+ enabled: enabledFilters.includes(publicationTypeCodeAddition.id)
+ })))
+ );
+
+ parentPublicationTypeNames$ = this.publications2Service.getParentPublicationTypeNames();
+
+ enabledParentPublicationTypeFilters$ = this.searchParams$.pipe(map(params => params.parentPublicationType ?? []));
+
+ enabledParentPublicationTypeFiltersWithNames$ = combineLatest([this.enabledParentPublicationTypeFilters$, this.parentPublicationTypeNames$]).pipe(
+ map(([enabledFilters, parentPublicationTypeNames]) => enabledFilters.map(filter => ({ id: filter, name: parentPublicationTypeNames[filter] })))
+ );
+
+ parentPublicationTypeFilters$ = combineLatest([this.parentPublicationTypeAdditions$, this.parentPublicationTypeNames$, this.enabledParentPublicationTypeFilters$]).pipe(
+ map(([parentPublicationTypeAdditions, parentPublicationTypeNames, enabledFilters]) => parentPublicationTypeAdditions.map(parentPublicationTypeAddition => ({
+ id: parentPublicationTypeAddition.id,
+ count: parentPublicationTypeAddition.count,
+ name: parentPublicationTypeNames[parentPublicationTypeAddition.id],
+ enabled: enabledFilters.includes(parentPublicationTypeAddition.id)
+ })))
+ );
+
+ additionsFromOpenAccess$ = this.aggregations$.pipe(
+ map(aggs => getOpenAccessAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ openAccessNames$ = this.publications2Service.getOpenAccessNames();
+
+ enabledOpenAccessFilters$ = this.searchParams$.pipe(map(params => params.openAccess ?? []));
+
+ enabledOpenAccessFiltersWithNames$ = combineLatest([this.enabledOpenAccessFilters$, this.openAccessNames$]).pipe(
+ map(([enabledFilters, openAccessNames]) => enabledFilters.map(filter => ({ id: filter, name: openAccessNames[filter] })))
+ );
+
+ openAccessFilters$ = combineLatest([this.additionsFromOpenAccess$, this.openAccessNames$, this.enabledOpenAccessFilters$]).pipe(
+ map(([additionsFromOpenAccess, openAccessNames, enabledFilters]) => additionsFromOpenAccess.map(additionFromOpenAccess => ({
+ id: additionFromOpenAccess.id,
+ count: additionFromOpenAccess.count,
+ name: openAccessNames[additionFromOpenAccess.id],
+ enabled: enabledFilters.includes(additionFromOpenAccess.id)
+ })))
+ );
+
+ additionsFromPublisherOpenAccess$ = this.aggregations$.pipe(
+ map(aggs => getPublisherOpenAccessCodeAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ publisherOpenAccessNames$ = this.publications2Service.getPublisherOpenAccessNames();
+
+ enabledPublisherOpenAccessFilters$ = this.searchParams$.pipe(map(params => params.publisherOpenAccessCode ?? []));
+
+ enabledPublisherOpenAccessFiltersWithNames$ = combineLatest([this.enabledPublisherOpenAccessFilters$, this.publisherOpenAccessNames$]).pipe(
+ map(([enabledFilters, publisherOpenAccessNames]) => enabledFilters.map(filter => ({ id: filter, name: publisherOpenAccessNames[filter] })))
+ );
+
+ publisherOpenAccessFilters$ = combineLatest([this.additionsFromPublisherOpenAccess$, this.publisherOpenAccessNames$, this.enabledPublisherOpenAccessFilters$]).pipe(
+ map(([additionsFromPublisherOpenAccess, publisherOpenAccessNames, enabledFilters]) => additionsFromPublisherOpenAccess.map(additionFromPublisherOpenAccess => ({
+ id: additionFromPublisherOpenAccess.id,
+ count: additionFromPublisherOpenAccess.count,
+ name: publisherOpenAccessNames[additionFromPublisherOpenAccess.id],
+ enabled: enabledFilters.includes(additionFromPublisherOpenAccess.id)
+ })))
+ );
+
+ additionsFromSelfArchivedCode$ = this.aggregations$.pipe(
+ map(aggs => getSelfArchivedCodeAdditions(aggs).map((bucket: any) => ({ id: bucket.key.toString(), count: bucket.doc_count })) ?? []),
+ map(aggs => aggs.sort((a, b) => b.count - a.count))
+ );
+
+ selfArchivedCodeNames$ = this.publications2Service.getSelfArchivedCodeNames();
+
+ enabledSelfArchivedCodeFilters$ = this.searchParams$.pipe(map(params => params.selfArchivedCode ?? []));
+
+ enabledSelfArchivedCodeFiltersWithNames$ = combineLatest([this.enabledSelfArchivedCodeFilters$, this.selfArchivedCodeNames$]).pipe(
+ map(([enabledFilters, selfArchivedCodeNames]) => enabledFilters.map(filter => ({ id: filter, name: selfArchivedCodeNames[filter] })))
+ );
+
+ selfArchivedCodeFilters$ = combineLatest([this.additionsFromSelfArchivedCode$, this.selfArchivedCodeNames$, this.enabledSelfArchivedCodeFilters$]).pipe(
+ map(([additionsFromSelfArchivedCode, selfArchivedCodeNames, enabledFilters]) => additionsFromSelfArchivedCode.map(additionFromSelfArchivedCode => ({
+ id: additionFromSelfArchivedCode.id,
+ count: additionFromSelfArchivedCode.count,
+ name: selfArchivedCodeNames[additionFromSelfArchivedCode.id],
+ enabled: enabledFilters.includes(additionFromSelfArchivedCode.id)
+ })))
+ );
+
+ searchTermsCount$ = this.searchParams$.pipe(
+ map(params => Object.keys(params).filter(key => !['q', 'page', 'size', 'sort'].includes(key)).reduce((acc, key) => acc + params[key].length, 0))
+ );
+
+ public mainFieldOfScienceName = {
+ "1": $localize`:@@naturalSciences:Luonnontieteet`,
+ "2": $localize`:@@engineeringTecnology:Tekniikka`,
+ "3": $localize`:@@medicalHealthSciences:Lääke- ja terveystieteet`,
+ "4": $localize`:@@agriculturalSciences:Maatalous- ja metsätieteet`,
+ "5": $localize`:@@socialSciences:Yhteiskuntatieteet`,
+ "6": $localize`:@@humanities:Humanistiset tieteet`,
+ // 7 not used
+ "8": $localize`:@@fieldsOfArt:Taiteenala`,
+ }
+
+ public filterLimits = {
+ year: 10,
+ language: 10,
+ };
+
+ breakpointSubscription = this.breakpointObserver.observe(['(max-width: 1200px)', '(max-width: 990px)']).subscribe(result => {
+ this.displayedColumns = ['icon', 'publicationName', 'authorsText', 'publisherName', 'publicationYear'];
+
+ if (result.breakpoints['(max-width: 990px)']) {
+ this.displayedColumns = ['publicationName', 'publicationYear'];
+ } else if (result.breakpoints['(max-width: 1200px)']) {
+ this.displayedColumns = ['icon', 'publicationName', 'authorsText', 'publicationYear'];
+ }
+ });
+
+ narrowBreakpoint$: Observable = this.breakpointObserver.observe('(max-width: 990px)').pipe(map(result => result.matches));
+
+ searchParamsSubscription = this.searchParams$.subscribe(searchParams => {
+ this.publications2Service.updateSearchTerms(searchParams);
+
+ this.keywords = searchParams.q?.[0] ?? "";
+ this.page = parseInt(searchParams.page?.[0] ?? "1");
+ this.size = parseInt(searchParams.size?.[0] ?? "10");
+ this.sort = searchParams.sort?.[0] ?? "";
+ });
+
+ total$ = this.publications2Service.getTotal();
+
+ ngOnDestroy() {
+ this.searchParamsSubscription.unsubscribe();
+ this.breakpointSubscription.unsubscribe();
+ }
+
+ toggleParam(key: string, value: string) {
+ this.searchParams$.pipe(take(1)).subscribe(filterParams => {
+ const queryParams = { ...filterParams };
+
+ if (queryParams[key] == null) {
+ queryParams[key] = [];
+ }
+
+ const index = queryParams[key].indexOf(value);
+
+ if (index === -1) {
+ queryParams[key].push(value);
+ } else {
+ queryParams[key].splice(index, 1);
+ }
+
+ if (queryParams[key].length === 0) {
+ delete queryParams[key];
+ }
+
+ queryParams.page = ["1"];
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: concatFields(queryParams)
+ });
+ });
+ }
+
+ setParam(key: string, value: string) {
+ this.searchParams$.pipe(take(1)).subscribe(filterParams => {
+ const queryParams = { ...filterParams };
+
+ queryParams[key] = [value];
+
+ queryParams.page = ["1"];
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: concatFields(queryParams)
+ });
+ });
+ }
+
+ clearParam(key: string) {
+ this.searchParams$.pipe(take(1)).subscribe(filterParams => {
+ const queryParams = { ...filterParams };
+
+ delete queryParams[key];
+
+ queryParams.page = ["1"];
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: concatFields(queryParams)
+ });
+ });
+ }
+
+ toggleSort(field: string) {
+ this.searchParams$.pipe(take(1)).subscribe(params => {
+ const existingSort = params.sort?.pop() ?? "";
+
+ if (existingSort === field + "Desc") {
+ this.clearParam("sort");
+ } else if (existingSort === field) {
+ this.setParam("sort", field + "Desc");
+ } else {
+ this.setParam("sort", field);
+ }
+ });
+ }
+
+ setKeywords(keywords: string) {
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+
+ queryParams: {
+ q: keywords,
+ page: "1"
+ },
+
+ queryParamsHandling: 'merge'
+ });
+ }
+
+ nextPage() {
+ this.searchParams$.pipe(take(1)).subscribe(searchParams => {
+ const queryParams = { ...searchParams };
+
+ const page = parseInt(queryParams.page?.[0] ?? "1");
+ queryParams.page = [`${page + 1}`];
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: queryParams
+ });
+ });
+ }
+
+ previousPage() {
+ this.searchParams$.pipe(take(1)).subscribe(searchParams => {
+ const queryParams = { ...searchParams };
+
+ const page = parseInt(queryParams.page?.[0] ?? "1");
+ queryParams.page = [`${page - 1}`];
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: queryParams
+ });
+ });
+ }
+
+ setPageSize(size: number) {
+ console.log(size);
+
+ this.router.navigate([], {
+ relativeTo: this.route,
+ // skipLocationChange: true,
+ queryParams: { size }, queryParamsHandling: 'merge'
+ });
+ }
+
+ clamp(value: number, min: number, max: number) {
+ return Math.min(Math.max(value, min), max);
+ }
+}
+
+export class PublicationDataSource extends DataSource {
+ constructor(private data$: Observable) {
+ super();
+ }
+
+ connect(): Observable {
+ return this.data$;
+ }
+
+ disconnect() { /**/ }
+}
+
+function concatParams(strings: string[]): string {
+ return strings.sort().join(",");
+}
+
+function splitParams(input: string | string[]): string[] {
+ if (Array.isArray(input)) {
+ return input.flatMap(item => item.split(","));
+ }
+
+ return input.split(",");
+}
+
+function splitFields(obj: Record): Record {
+ const result: Record = {};
+
+ for (const key in obj) {
+ if (key === 'q') {
+ result[key] = [obj[key] as string];
+ } else {
+ const value = obj[key];
+ result[key] = splitParams(value);
+ }
+ }
+
+ return result;
+}
+
+function concatFields(obj: Record): Record {
+ const result: Record = {};
+
+ for (const key in obj) {
+ const value = obj[key];
+ result[key] = concatParams(value);
+ }
+
+ return result;
+}
diff --git a/src/app/portal/components/results/results.component.html b/src/app/portal/components/results/results.component.html
index 2882283a1..dc9a2f770 100644
--- a/src/app/portal/components/results/results.component.html
+++ b/src/app/portal/components/results/results.component.html
@@ -16,10 +16,49 @@
[allData]="tabValues"
>
+
+
+
+
+
+
+
+
+
+ Testaa haun uudistettua betaversiota. Betaversio toimii vain julkaisujen hakuun. Tiedot vastaavat nykyisen sivuston sisältöä julkaisujen osalta.
+
+
+
+
+
+
+
+
+
+ *ngIf="tab === 'persons' && mydataLoginSnackbarVisible">
@@ -46,7 +85,7 @@
-
+
diff --git a/src/app/portal/components/results/results.component.ts b/src/app/portal/components/results/results.component.ts
index 3533f059f..1021d5ad1 100644
--- a/src/app/portal/components/results/results.component.ts
+++ b/src/app/portal/components/results/results.component.ts
@@ -158,6 +158,7 @@ export class ResultsComponent implements OnInit, OnDestroy, AfterViewInit {
showLoginInfoDialog = false;
betaDialogTitle = $localize`:@@researchersProfile:Tutkijan tiedot`;
mydataLoginSnackbarVisible = false;
+ betaSearchBannerVisible = false;
private metaTagsList = [
MetaTags.publications,
@@ -235,6 +236,10 @@ export class ResultsComponent implements OnInit, OnDestroy, AfterViewInit {
if (sessionStorage.getItem('researchersLoginSnackbarVisible')) {
this.mydataLoginSnackbarVisible = true;
}
+
+ if (!sessionStorage.getItem('betaSearchBannerDismissed')) {
+ this.betaSearchBannerVisible = true;
+ }
}
// Subscribe to route params and query params in one subscription
@@ -599,6 +604,14 @@ export class ResultsComponent implements OnInit, OnDestroy, AfterViewInit {
}
}
+ hideBetaSearchBanner() {
+ this.betaSearchBannerVisible = false;
+
+ if (isPlatformBrowser(this.platformId)) {
+ sessionStorage.setItem('betaSearchBannerDismissed', 'true');
+ }
+ }
+
changeFocusTarget(target) {
this.tabChangeService.targetFocus(target);
diff --git a/src/app/portal/components/search-bar/search-bar.component.html b/src/app/portal/components/search-bar/search-bar.component.html
index 740f4ac6e..d032b3cc7 100644
--- a/src/app/portal/components/search-bar/search-bar.component.html
+++ b/src/app/portal/components/search-bar/search-bar.component.html
@@ -139,27 +139,24 @@
Hae
-
-
- Hakuohje
+
+
+
+
+ Hakuohje
+
+
Kirjoita hakukenttään julkaisun nimi, asiasana, hankkeen nimi
tai esimerkiksi organisaation nimi.
+
Hakutulosten rajaaminen onnistuu haun jälkeen hakunäkymässä.
+
Haku kohdistuu julkaisuihin, tutkijoihin, hankkeisiin, aineistoihin, rahoitushakuihin,
infrastruktuureihin ja organisaatioihin.
diff --git a/src/app/portal/components/search-bar/search-bar.component.ts b/src/app/portal/components/search-bar/search-bar.component.ts
index 6e9a7619e..7fd5ac400 100644
--- a/src/app/portal/components/search-bar/search-bar.component.ts
+++ b/src/app/portal/components/search-bar/search-bar.component.ts
@@ -201,17 +201,18 @@ export class SearchBarComponent implements OnInit, AfterViewInit, OnDestroy {
this.setCompletionWidth();
}
- fireAutoSuggest() {
- this.autoSuggestSub = this.queryField.valueChanges
- .pipe(debounceTime(1000), distinctUntilChanged())
+ fireAutoSuggest() { // TODO: Dense code warning; IS THIS DIFFERENT FROM NORMAL SEARCHES?
+ this.autoSuggestSub = this.queryField.valueChanges.pipe(debounceTime(1000), distinctUntilChanged())
.subscribe((result) => {
this.keyManager = new ActiveDescendantKeyManager(this.items)
.withWrap()
.withTypeAhead();
this.currentInput = result;
+
if (result.length > 2) {
this.topData = [];
this.otherData = [];
+
this.autoSuggestResponseSub = this.autosuggestService
.search(result)
.pipe(map((response) => [response]))
@@ -219,17 +220,18 @@ export class SearchBarComponent implements OnInit, AfterViewInit, OnDestroy {
// Sort indices with highest doc count
const arr = [];
this.autoSuggestResponse = response;
- const source =
- this.autoSuggestResponse[0].aggregations._index.buckets;
+ const source = this.autoSuggestResponse[0].aggregations._index.buckets;
+
Object.keys(source)
.sort((a, b) => source[b].doc_count - source[a].doc_count)
.forEach((key) => {
- arr.push({
+ arr.push({ // TODO: arr side-effect
index: key,
source: source[key],
translation: this.translations[key],
});
});
+
// Show hits for top 2 indices with most results
this.topData = arr.slice(0, 2);
// List other indices, filter out indices with no results
diff --git a/src/app/portal/pipes/limit.pipe.ts b/src/app/portal/pipes/limit.pipe.ts
new file mode 100644
index 000000000..6d193d56b
--- /dev/null
+++ b/src/app/portal/pipes/limit.pipe.ts
@@ -0,0 +1,20 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'limit',
+ standalone: true
+})
+export class LimitPipe implements PipeTransform {
+ transform(value: any[], limit: number, enabled = true): any[] {
+ // Async pipes return nulls
+ if (value == null) {
+ return [];
+ }
+
+ if (enabled) {
+ return value.slice(0, limit);
+ } else {
+ return value;
+ }
+ }
+}
diff --git a/src/app/portal/portal-routing.module.ts b/src/app/portal/portal-routing.module.ts
index 6e93cfbaf..ec32aebdd 100644
--- a/src/app/portal/portal-routing.module.ts
+++ b/src/app/portal/portal-routing.module.ts
@@ -34,6 +34,7 @@ import { SingleDatasetComponent } from './components/single/single-dataset/singl
import { SingleFundingCallComponent } from './components/single/single-funding-call/single-funding-call.component';
import { SingleIndicatorComponent } from './components/science-politics/open-science-and-research-indicators/single-indicator/single-indicator.component';
import { SinglePersonComponent } from './components/single/single-person/single-person.component';
+import { Publications2Component } from '@portal/components/results/publications2/publications2.component';
// import { TkiReportsComponent } from "@portal/components/science-politics/tki-reports/tki-reports.component";
const routes: Routes = [
@@ -98,6 +99,10 @@ const routes: Routes = [
redirectTo: 'results/publications',
pathMatch: 'full',
},
+ {
+ path: 'results/publications2',
+ component: Publications2Component,
+ },
{
path: 'results/:tab',
component: ResultsComponent,
diff --git a/src/app/portal/portal.module.ts b/src/app/portal/portal.module.ts
index fd4c8a5cf..941a00f0c 100644
--- a/src/app/portal/portal.module.ts
+++ b/src/app/portal/portal.module.ts
@@ -166,6 +166,7 @@ import { CheckEmptyFieldsPipe } from './pipes/check-empty-fields.pipe';
import { PersonGroupComponent } from './components/single/single-person/person-group/person-group.component';
import { PersonGroupAdditionalComponent } from './components/single/single-person/person-group-additional/person-group-additional.component';
import { FooterComponent } from '../layout/footer/footer.component';
+import { SearchBar2Component } from '@portal/search-bar2/search-bar2.component';
import { FixExternalUrlPipe } from '@portal/pipes/fix-external-url.pipe';
@NgModule({
@@ -289,6 +290,7 @@ import { FixExternalUrlPipe } from '@portal/pipes/fix-external-url.pipe';
MatTableModule,
MatSortModule,
FooterComponent,
+ SearchBar2Component,
FixExternalUrlPipe
],
exports: [DatasetAuthorComponent, FiltersComponent],
diff --git a/src/app/portal/search-bar2/search-bar2.component.html b/src/app/portal/search-bar2/search-bar2.component.html
new file mode 100644
index 000000000..fbfc672b0
--- /dev/null
+++ b/src/app/portal/search-bar2/search-bar2.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+ Hae
+
+
+
+
+ Hakuohje
+
+
+
+
+
diff --git a/src/app/portal/search-bar2/search-bar2.component.scss b/src/app/portal/search-bar2/search-bar2.component.scss
new file mode 100644
index 000000000..8d0a524de
--- /dev/null
+++ b/src/app/portal/search-bar2/search-bar2.component.scss
@@ -0,0 +1,112 @@
+.search-input {
+ width: 510px;
+ height: 55px;
+
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ border-width: 0;
+
+ font-size: 25px;
+
+ padding: 1px 44px 1px 16px;
+}
+
+input[placeholder] {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+::placeholder {
+ font-size: 17px;
+ font-style: normal;
+}
+
+/*! CSS Used from: Embedded */
+*, *:before, *:after {
+ box-sizing: border-box;
+}
+
+body:not(.user-tabbing) *:focus {
+ outline: none;
+}
+
+*, *:before, *:after {
+ box-sizing: inherit;
+}
+
+/*! CSS Used from: https://tiedejatutkimus.fi/fi/styles.cdbf57794ce87ed1.css */
+*, *:before, *:after {
+ box-sizing: border-box;
+}
+
+input {
+ margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+[type=search] {
+ outline-offset: -2px;
+ -webkit-appearance: textfield;
+}
+
+.row > * {
+ flex-shrink: 0;
+ width: 100%;
+ max-width: 100%;
+ padding-right: calc(var(--bs-gutter-x) * .5);
+ padding-left: calc(var(--bs-gutter-x) * .5);
+ margin-top: var(--bs-gutter-y);
+}
+
+.col-10 {
+ flex: 0 0 auto;
+ width: 83.33333333%;
+}
+
+@media (min-width: 576px) {
+ .col-sm-12 {
+ flex: 0 0 auto;
+ width: 100%;
+ }
+}
+
+body:not(.user-tabbing) input:focus, body:not(.user-tabbing) *:focus {
+ outline: none;
+}
+
+*, *:before, *:after {
+ box-sizing: inherit;
+}
+
+body app-search-bar .search-input input {
+ color: #495057 !important;
+}
+
+/*! CSS Used from: Embedded */
+app-search-bar input#searchInput {
+ border-top-right-radius: .25rem !important;
+ border-bottom-right-radius: .25rem !important;
+}
+
+app-search-bar .search-input input {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ border: none;
+ padding-right: 2.75rem;
+}
+
+app-search-bar input {
+ z-index: 102;
+}
+
+
+
+@media (max-width: 768px) {
+ app-search-bar input#searchInput {
+ border-radius: .25rem !important;
+ }
+}
diff --git a/src/app/portal/search-bar2/search-bar2.component.ts b/src/app/portal/search-bar2/search-bar2.component.ts
new file mode 100644
index 000000000..10fe119c5
--- /dev/null
+++ b/src/app/portal/search-bar2/search-bar2.component.ts
@@ -0,0 +1,29 @@
+import { Component, EventEmitter, inject, Input, Output } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatRippleModule } from '@angular/material/core';
+import { FormsModule } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+
+@Component({
+ selector: 'app-search-bar2',
+ standalone: true,
+ imports: [CommonModule, MatRippleModule, FormsModule],
+ templateUrl: './search-bar2.component.html',
+ styleUrls: ['./search-bar2.component.scss']
+})
+export class SearchBar2Component {
+ route = inject(ActivatedRoute);
+ router = inject(Router)
+
+ // two-way "value" binding that's a string; basically text input
+ @Input() value = "";
+ @Output() valueChange = new EventEmitter
();
+
+ // search is pressed output
+ @Output() search = new EventEmitter();
+
+ // instruction button is pressed output
+ @Output() instructions = new EventEmitter();
+
+ public keywords = this.route.snapshot.queryParams.q ?? "";
+}
diff --git a/src/app/portal/services/publication2.service.ts b/src/app/portal/services/publication2.service.ts
new file mode 100644
index 000000000..f9b721026
--- /dev/null
+++ b/src/app/portal/services/publication2.service.ts
@@ -0,0 +1,1226 @@
+import { inject, Injectable, LOCALE_ID, OnInit, SecurityContext } from '@angular/core';
+// import { object, Output, parse, string } from 'valibot';
+import { BehaviorSubject, forkJoin, Observable, of, shareReplay } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { map, switchMap, take, tap } from 'rxjs/operators';
+import { ActivatedRoute } from '@angular/router';
+import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
+
+import { locale } from "../../../environments/locale";
+const path = suffixer(locale);
+
+/*const PublicationSearchSchema = object({
+ publicationId: string(),
+ title: string(),
+});*/
+
+type TODO = any;
+type PublicationSearch = TODO; // Output;
+
+// function that validates API response into a PublicationSearch
+function parsePublicationSearch(data: TODO): PublicationSearch {
+ return data;
+ // return parse(PublicationSearchSchema, data);
+}
+
+// export interface HighlightedPublication {
+export type HighlightedPublication = {
+ id: string;
+ publicationName: SafeHtml;
+ authorsText: SafeHtml;
+ publisherName: SafeHtml;
+ publicationYear: SafeHtml;
+
+ badges: {
+ doi?: string;
+ peerReviewed?: boolean;
+ openAccess?: string;
+ };
+}
+
+type CachedPublicationSearch = {
+ keywords: string,
+ publicationSearch: PublicationSearch,
+}
+
+export type SearchParams = {
+ q?: string[],
+ page?: string[],
+ size?: string[],
+ sort?: string[],
+
+ year?: string[],
+ organization?: string[],
+
+ language?: string[],
+ format?: string[],
+ audience?: string[],
+ peerReviewed?: string[],
+
+ // new terms
+ parentPublicationType?: string[],
+ international?: string[],
+ articleType?: string[],
+ jufo?: string[],
+
+ // placeholders
+ publicationTypeCode?: string[],
+ publicationStatusCode?: string[],
+
+ fieldsOfScience?: string[],
+
+ openAccess?: string[],
+ publisherOpenAccessCode?: string[],
+ selfArchivedCode?: string[],
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class Publication2Service {
+ http = inject(HttpClient);
+ sanitizer = inject(DomSanitizer);
+ locale = inject(LOCALE_ID);
+
+ searchUrl = 'https://researchfi-api-qa.rahtiapp.fi/portalapi/publication/_search?'
+
+ searchParams = new BehaviorSubject({});
+
+ searchResults$: Observable = this.searchParams.pipe(
+ switchMap(searchParams => this.searchPublications(searchParams)),
+ tap((data) => console.log("searchPublications", data)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+
+ resultsTotal$ = this.searchResults$.pipe(
+ map((data) => data.hits.total.value)
+ );
+
+ publicationSearch$: Observable = this.searchResults$.pipe(
+ map((data) => parsePublicationSearch(data)),
+ map((publicationSearch: PublicationSearch) => this.createHighlightedPublications(publicationSearch))
+ );
+
+ publicationAggregations$ = this.searchResults$.pipe(
+ map((data) => data.aggregations)
+ );
+
+ getSearch() {
+ return this.publicationSearch$;
+ }
+
+ getTotal() {
+ return this.resultsTotal$;
+ }
+
+ getAggregations() {
+ return this.publicationAggregations$;
+ }
+
+ updateSearchTerms(searchParams: SearchParams): void {
+ this.searchParams.next(searchParams);
+ }
+
+ private searchPublications(searchParams: SearchParams): Observable {
+ const q = searchParams.q?.[0] ?? "";
+ const page = parseInt(searchParams.page?.[0] ?? "1");
+ const size = parseInt(searchParams.size?.[0] ?? "10");
+ const from = (page - 1) * size;
+
+ // Settings for highlighting in ElasticSearch
+ const largeHighlight = { fragment_size: 200, number_of_fragments: 15 };
+
+ return this.http.post(this.searchUrl, {
+ from: from,
+ size: size,
+ track_total_hits: true,
+ sort: [
+ ...sortingTerms(searchParams)
+ ],
+ query: {
+ bool: {
+ must: {
+ ...matchingTerms(searchParams)
+ },
+ filter: {
+ bool: {
+ must: [
+ ...filteringTerms(searchParams),
+ ]
+ }
+ }
+ },
+ },
+ highlight: {
+ fields: {
+ publicationName: { ...largeHighlight },
+ authorsText: { ...largeHighlight, type: "plain" },
+ publisherName: { ...largeHighlight }
+ },
+ pre_tags: [""],
+ post_tags: [" "]
+ },
+ aggregations: {
+ ...aggregationTerms(searchParams),
+ }
+ });
+ }
+
+ // sort column?
+ // sort=foo OR fooDesc
+
+ createHighlightedPublications(searchData: PublicationSearch): HighlightedPublication[] {
+ return searchData.hits.hits.map((hit: any/*ES doc for Publication*/) => this.createHighlightedPublication(hit));
+ }
+
+ createHighlightedPublication(searchData: any/*ES doc for Publication*/): HighlightedPublication {
+ const source = searchData._source;
+ const highlight = searchData.highlight;
+
+ /*const badgesExist = {
+ doi: source.doi != null || source.doiHandle != null,
+ peerReviewed: source.peerReviewed?.id === 1 ?? false,
+ openAccess: source.openAccess === 1
+ }*/
+
+ // badges are keys values with strings to strings
+
+
+ const badges: {
+ doi?: string;
+ peerReviewed?: boolean;
+ openAccess?: string;
+ } = {};
+
+ badges.doi = source.doi;
+ badges.openAccess = source.doiHandle;
+ badges.peerReviewed = source.peerReviewed[0].id === "1";
+
+ const publicationName = highlight?.publicationName?.join() ?? source.publicationName ?? '';
+ const authorsText = highlight?.authorsText?.join() ?? source.authorsText ?? '';
+ const publisherName = highlight?.publisherName?.join() ?? source.publisherName ?? '';
+ const publicationYear = highlight?.publicationYear?.join() ?? source.publicationYear ?? '';
+
+ return {
+ id: searchData._id,
+ publicationName: this.sanitizer.sanitize(SecurityContext.HTML, publicationName),
+ authorsText: this.sanitizer.sanitize(SecurityContext.HTML, authorsText),
+ publisherName: this.sanitizer.sanitize(SecurityContext.HTML, publisherName),
+ publicationYear: this.sanitizer.sanitize(SecurityContext.HTML, publicationYear),
+
+ badges: badges
+ };
+ }
+
+ getOrganizationNames(): Observable> {
+ const organizationNamesBySector = (sectorId: number) => ({
+ 'size': 0,
+ 'aggs': {
+ 'filtered_authors': {
+ 'nested': {
+ 'path': 'author'
+ },
+ 'aggs': {
+ 'single_sector': {
+ 'filter': {
+ 'term': {
+ 'author.sectorId': `${sectorId}`
+ }
+ },
+ 'aggs': {
+ 'organizations': {
+ 'nested': {
+ 'path': 'author.organization'
+ },
+ 'aggs': {
+ 'composite_orgs': {
+ 'composite': {
+ 'size': 65536,
+ 'sources': [
+ { 'id': { 'terms': { 'field': 'author.organization.organizationId.keyword' } } },
+ { 'name': { 'terms': { 'field': path`author.organization.OrganizationNameFi.keyword` } } } // TODO path template needed
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ const sectorIds = [1, 2, 3, 4, /*5,*/ 6];
+
+ const responses$ = sectorIds.map((sectorId) => {
+ return this.http.post(this.searchUrl, organizationNamesBySector(sectorId)).pipe(
+ map((res) => getOrganizationNameBuckets(res).map((bucket) => [bucket.key.id, {name: bucket.key.name, sectorId}])),
+ );
+ });
+
+ return forkJoin(responses$)
+ .pipe(map(responses => responses.flat()))
+ .pipe(map(pairs => Object.fromEntries(pairs)))
+ .pipe(shareReplay({ bufferSize: 1, refCount: true }));
+ }
+
+ getLanguageCodeNames(): Observable> {
+ const body = {
+ 'size': 0,
+ 'aggs': {
+ 'composite_pairs': {
+ 'composite': {
+ 'size': 1000,
+ 'sources': [
+ { 'id': { 'terms': { 'field': 'languages.languageCode.keyword' } } },
+ { 'nameFiLanguage': { 'terms': { 'field': path`languages.languageFi.keyword` } } } // TODO: does the key need to be localized? // TODO remove "fi" ?
+ ]
+ }
+ }
+ }
+ };
+
+ type LanguageAggregation = {
+ aggregations: {
+ composite_pairs: {
+ buckets: Array<{
+ key: {
+ id: string;
+ nameFiLanguage: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+
+ const response$ = this.http.post(this.searchUrl, body);
+
+ return response$.pipe(
+ map((res) => res.aggregations.composite_pairs.buckets.map((bucket) => [bucket.key.id, bucket.key.nameFiLanguage])), // TODO localized path needed
+ map(pairs => Object.fromEntries(pairs)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+ }
+
+ getPublicationFormatNames(): Observable> {
+ const body = {
+ 'size': 0,
+ 'aggs': {
+ 'composite_pairs': {
+ 'composite': {
+ 'size': 1000,
+ 'sources': [
+ { 'id': { 'terms': { 'field': 'publicationFormat.id.keyword' } } },
+ { 'nameFiFormat': { 'terms': { 'field': path`publicationFormat.nameFiPublicationFormat.keyword` } } } // TODO Does the key need to be localized? // TODO remove "fi" ?
+ ]
+ }
+ }
+ }
+ };
+
+ type PublicationFormatAggregation = {
+ aggregations: {
+ composite_pairs: {
+ buckets: Array<{
+ key: {
+ id: string;
+ nameFiFormat: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+
+ const response$ = this.http.post(this.searchUrl, body);
+
+ return response$.pipe(
+ map((res) => res.aggregations.composite_pairs.buckets.map((bucket) => [bucket.key.id, bucket.key.nameFiFormat])), // TODO localized path needed
+ map(pairs => Object.fromEntries(pairs)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+ }
+
+ getPublicationAudienceNames(): Observable> {
+ const body = {
+ 'size': 0,
+ 'aggs': {
+ 'composite_pairs': {
+ 'composite': {
+ 'size': 1000,
+ 'sources': [
+ { 'id': { 'terms': { 'field': 'publicationAudience.id.keyword' } } },
+ { 'nameFiAudience': { 'terms': { 'field': path`publicationAudience.nameFiPublicationAudience.keyword` } } } // TODO Does the key need to be localized? // TODO remove "fi" ?
+ ]
+ }
+ }
+ }
+ };
+
+ type PublicationAudienceAggregation = {
+ aggregations: {
+ composite_pairs: {
+ buckets: Array<{
+ key: {
+ id: string;
+ nameFiAudience: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+
+ const response$ = this.http.post(this.searchUrl, body);
+
+ return response$.pipe(
+ map((res) => res.aggregations.composite_pairs.buckets.map((bucket) => [bucket.key.id, bucket.key.nameFiAudience])), // TODO localized path needed
+ map(pairs => Object.fromEntries(pairs)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+ }
+
+ getParentPublicationTypeNames(): Observable> {
+ const body = {
+ "size": 0,
+ "aggs": {
+ "composite_pairs": {
+ "composite": {
+ "size": 1000,
+ "sources": [
+ { "id": { "terms": { "field": "parentPublicationType.id.keyword" } } },
+ { "nameFiParentPublicationType": { "terms": { "field": path`parentPublicationType.nameFiParentPublicationType.keyword` } } } // TODO Does the key need to be localized? // TODO remove "fi" ?
+ ]
+ }
+ }
+ }
+ }
+
+ type ParentPublicationTypeAggregation = {
+ aggregations: {
+ composite_pairs: {
+ buckets: Array<{
+ key: {
+ id: string;
+ nameFiParentPublicationType: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+
+ const response$ = this.http.post(this.searchUrl, body);
+
+ return response$.pipe(
+ map((res) => res.aggregations.composite_pairs.buckets.map((bucket) => [bucket.key.id, bucket.key.nameFiParentPublicationType])), // TODO localized path needed
+ map(pairs => Object.fromEntries(pairs)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+ }
+
+ getPeerReviewedNames(): Observable> {
+ return of({
+ "0": $localize`:@@notPeerReviewed:Ei-vertaisarvioitu`, // TODO does not exist in localization file
+ "1": $localize`:@@peerReviewed:Vertaisarvioitu`
+ });
+ }
+
+ getInternationalPublicationNames(): Observable> {
+ return of({
+ "0": $localize`:@@domesticPublication:Kotimainen julkaisu`,
+ "1": $localize`:@@internationalPublication:Kansainvälinen julkaisu`
+ });
+ }
+
+ getArticleTypeCodeNames(): Observable> {
+ return of({
+ "0": $localize`:@@journal:Lehti`,
+ "1": $localize`:@@researchBook:Kokoomateos`,
+ "2": $localize`:@@Conference:Konferenssi`,
+ "3": $localize`:@@onlinePlatform:Verkkoalusta`
+ });
+ }
+
+ getFieldsOfScienceNames(): Observable> {
+ const body = {
+ "size": 0,
+ "aggs": {
+ "fieldsOfScience_nested": {
+ "nested": {
+ "path": "fieldsOfScience"
+ },
+ "aggs": {
+ "composite_field_of_science": {
+ "composite": {
+ "size": 65536,
+ "sources": [
+ {
+ "id": {
+ "terms": {
+ "field": "fieldsOfScience.fieldIdScience"
+ }
+ }
+ },
+ {
+ "name": {
+ "terms": {
+ 'field': path`fieldsOfScience.nameFiScience.keyword` // TODO localized path needed
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ };
+
+ type FieldsOfScienceAggregation = {
+ aggregations: {
+ fieldsOfScience_nested: {
+ composite_field_of_science: {
+ buckets: Array<{
+ key: {
+ id: string;
+ name: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+ };
+
+ const response$ = this.http.post(this.searchUrl, body);
+
+ return response$.pipe(
+ map((res) => res.aggregations.fieldsOfScience_nested.composite_field_of_science.buckets.map((bucket) => [bucket.key.id, bucket.key.name])),
+ map(pairs => Object.fromEntries(pairs)),
+ shareReplay({ bufferSize: 1, refCount: true })
+ );
+ }
+
+ getPublicationTypeCodeNames(): Observable> {
+ const labels = [
+ { value: `A`, name: $localize`:@@publicationClassA:Vertaisarvioidut tieteelliset artikkelit` },
+ { value: `A1`, name: $localize`:@@publicationClassA1:Alkuperäisartikkeli tieteellisessä aikakauslehdessä` },
+ { value: `A2`, name: $localize`:@@publicationClassA2:Katsausartikkeli tieteellisessä aikakauslehdessä` },
+ { value: `A3`, name: $localize`:@@publicationClassA3:Kirjan tai muun kokoomateoksen osa` },
+ { value: `A4`, name: $localize`:@@publicationClassA4:Artikkeli konferenssijulkaisussa` },
+ { value: `B`, name: $localize`:@@publicationClassB:Vertaisarvioimattomat tieteelliset kirjoitukset` },
+ { value: `B1`, name: $localize`:@@publicationClassB1:Kirjoitus tieteellisessä aikakausilehdessä` },
+ { value: `B2`, name: $localize`:@@publicationClassB2:Kirjan tai muun kokoomateoksen osa` },
+ { value: `B3`, name: $localize`:@@publicationClassB3:Vertaisarvioimaton artikkeli konferenssijulkaisussa` },
+ { value: `C`, name: $localize`:@@publicationClassC:Tieteelliset kirjat` },
+ { value: `C1`, name: $localize`:@@publicationClassC1:Kustannettu tieteellinen erillisteos` },
+ { value: `C2`, name: $localize`:@@publicationClassC2:Toimitettu kirja, kokoomateos, konferenssijulkaisu tai lehden erikoisnumero` },
+ { value: `D`, name: $localize`:@@publicationClassD:Ammattiyhteisölle suunnatut julkaisut` },
+ { value: `D1`, name: $localize`:@@publicationClassD1:Artikkeli ammattilehdessä` },
+ { value: `D2`, name: $localize`:@@publicationClassD2:Artikkeli ammatillisessa kokoomateoksessa` },
+ { value: `D3`, name: $localize`:@@publicationClassD3:Artikkeli ammatillisessa konferenssijulkaisussa` },
+ { value: `D4`, name: $localize`:@@publicationClassD4:Julkaistu kehittämis- tai tutkimusraportti taikka -selvitys` },
+ { value: `D5`, name: $localize`:@@publicationClassD5:Ammatillinen kirja` },
+ { value: `D6`, name: $localize`:@@publicationClassD6:Toimitettu ammatillinen teos` },
+ { value: `E`, name: $localize`:@@publicationClassE:Suurelle yleisölle suunnatut julkaisut` },
+ { value: `E1`, name: $localize`:@@publicationClassE1:Yleistajuinen artikkeli, sanomalehtiartikkeli` },
+ { value: `E2`, name: $localize`:@@publicationClassE2:Yleistajuinen monografia` },
+ { value: `E3`, name: $localize`:@@publicationClassE3:Toimitettu yleistajuinen teos` },
+ { value: `F`, name: $localize`:@@publicationClassF:Julkinen taiteellinen ja taideteollinen toiminta` },
+ { value: `F1`, name: $localize`:@@publicationClassF1:Itsenäinen teos tai esitys` },
+ { value: `F2`, name: $localize`:@@publicationClassF2:Taiteellisen teoksen tai esityksen osatoteutus` },
+ { value: `F3`, name: $localize`:@@publicationClassF3:Ei-taiteellisen julkaisun taiteellinen osa` },
+ { value: `G`, name: $localize`:@@publicationClassG:Opinnäytteet` },
+ { value: `G1`, name: $localize`:@@publicationClassG1:Ammattikorkeakoulututkinnon opinnäytetyö, kandidaatintyö` },
+ { value: `G2`, name: $localize`:@@publicationClassG2:Pro gradu, diplomityö, ylempi amk-opinnäytetyö` },
+ { value: `G3`, name: $localize`:@@publicationClassG3:Lisensiaatintyö` },
+ { value: `G4`, name: $localize`:@@publicationClassG4:Monografiaväitöskirja` },
+ { value: `G5`, name: $localize`:@@publicationClassG5:Artikkeliväitöskirja` },
+ { value: `H`, name: $localize`:@@publicationClassH:Patentit ja keksintöilmoitukset` },
+ { value: `H1`, name: $localize`:@@publicationClassH1:Myönnetty patentti` },
+ { value: `H2`, name: $localize`:@@publicationClassH2:Keksintöilmoitus` },
+ { value: `I`, name: $localize`:@@publicationClassI:Audiovisuaaliset julkaisut ja tieto- ja viestintätekniset sovellukset` },
+ { value: `I1`, name: $localize`:@@publicationClassI1:Audiovisuaaliset julkaisut` },
+ { value: `I2`, name: $localize`:@@publicationClassI2:Tieto- ja viestintätekniset sovellukset` }
+ ];
+
+ // Turn into key value object
+ const lookup = labels.reduce((acc, label) => {
+ acc[label.value] = label.name;
+ return acc;
+ });
+
+ return of({
+ ...lookup
+ });
+ }
+
+ getOpenAccessNames(): Observable> {
+ return of({
+ "0": $localize`:@@publication2.openAccess.No:Ei avoin`,
+ "1": $localize`:@@publication2.openAccess.Yes:Avoimesti saatavilla`,
+ "9": $localize`:@@publication2.openAccess.Unknown:Ei tietoa`
+ });
+ }
+
+ getPublisherOpenAccessNames(): Observable> {
+ return of({
+ "1": $localize`:@@publication2.publisherOpenAccess.fullyOpen:Kokonaan avoin julkaisukanava`,
+ "2": $localize`:@@publication2.publisherOpenAccess.partiallyOpen:Osittain avoin julkaisukanava`,
+ "3": $localize`:@@publication2.publisherOpenAccess.delayedOpen:Viivästetysti avoin julkaisukanava`,
+ "0": $localize`:@@publication2.publisherOpenAccess.Unspecified:Ei vastausta`,
+ // "9": $localize`:@@publication2.publisherOpenAccess.Unknown:Ei tietoa`
+ });
+ }
+
+ getSelfArchivedCodeNames(): Observable> {
+ return of({
+ "1": $localize`:@@publication2.selfArchived.Yes:Rinnakkaistallennettu`,
+ "0": $localize`:@@publication2.selfArchived.No:Ei rinnakkaistallennettu`,
+ " ": $localize`:@@publication2.selfArchived.Unknown:Ei tietoa`
+ });
+
+ }
+}
+
+function escapeLastQuoteIfOdd(inputString: string): string {
+ // Count the number of quotation marks in the string
+ const quoteCount = (inputString.match(/"/g) || []).length;
+
+ // If the count is odd, escape the last quotation mark
+ if (quoteCount % 2 !== 0) {
+ const lastQuoteIndex = inputString.lastIndexOf('"');
+ // Replace the last quotation mark with an escaped version
+ inputString = inputString.substring(0, lastQuoteIndex) + '\\"' + inputString.substring(lastQuoteIndex + 1);
+ }
+
+ return inputString;
+}
+
+function matchingTerms(searchParams: SearchParams) {
+ let q = (searchParams.q ?? [""])[0];
+ q = escapeLastQuoteIfOdd(q);
+
+ if (q === "") {
+ return {
+ match_all: {}
+ };
+ }
+
+ return {
+ query_string: {
+ query: q,
+ fields: ["publicationName", "authorsText", "publisherName"],
+ default_operator: "OR"
+ }
+ };
+}
+
+function termsForYear(searchParams: SearchParams) {
+ if (searchParams.year) {
+ return [{
+ terms: {
+ "publicationYear": searchParams.year
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForTypeCode(searchParams: SearchParams) {
+ if (searchParams.publicationTypeCode) {
+ return [{
+ terms: {
+ "publicationTypeCode.keyword": searchParams.publicationTypeCode
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForStatusCode(searchParams: SearchParams) {
+ if (searchParams.publicationStatusCode) {
+ return [{
+ terms: {
+ "publicationStatusCode.keyword": searchParams.publicationStatusCode
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForLanguageCode(searchParams: SearchParams) {
+ if (searchParams.language) {
+ return [{
+ terms: {
+ "languages.languageCode.keyword": searchParams.language
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForPublicationFormat(searchParams: SearchParams) {
+ if (searchParams.format) {
+ return [{
+ terms: {
+ "publicationFormat.id.keyword": searchParams.format
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForPublicationAudience(searchParams: SearchParams) {
+ if (searchParams.audience) {
+ return [{
+ terms: {
+ "publicationAudience.id.keyword": searchParams.audience
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForPeerReviewed(searchParams: SearchParams) {
+ if (searchParams.peerReviewed) {
+ return [{
+ terms: {
+ "peerReviewed.id.keyword": searchParams.peerReviewed
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForParentPublicationType(searchParams: SearchParams) {
+ if (searchParams.parentPublicationType) {
+ return [{
+ terms: {
+ "parentPublicationType.id.keyword": searchParams.parentPublicationType
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForInternationalPublication(searchParams: SearchParams) {
+ if (searchParams.international) {
+ return [{
+ terms: {
+ "internationalPublication": searchParams.international
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForArticleTypeCode(searchParams: SearchParams) {
+ if (searchParams.articleType) {
+ return [{
+ terms: {
+ "articleTypeCode": searchParams.articleType
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForJufoClassCode(searchParams: SearchParams) {
+ if (searchParams.jufo) {
+ return [{
+ terms: {
+ "jufoClassCode.keyword": searchParams.jufo
+ }
+ }];
+ }
+ return [];
+}
+
+function generateAggregationStep(name: SearchParamKey, lookup: Record) {
+ const fieldPath = lookup[name].fieldPath;
+
+ const fieldName = lookup[name].fieldName; // TODO: "fieldName" is just redundant
+ const topLevelPath = `top_level_${fieldName}`; // TODO: Use the established SearchParamKey and arbitary fieldPath
+ const middleLevelPath = `filtered_except_${fieldName}`; //
+ const bottomLevelPath = `all_${fieldName}`; //
+
+ function getAdditions(searchParams: SearchParams) {
+ const global = searchParams[name] != null
+
+ if (global) {
+ // console.log("global", name);
+
+ return {
+ [topLevelPath]: {
+ global: {},
+ aggregations: {
+ [middleLevelPath]: {
+ filter: {
+ bool: {
+ must: [
+ ...additionFilterTerms(name, searchParams)
+ ]
+ }
+ },
+ aggregations: {
+ [bottomLevelPath]: {
+ terms: {
+ field: fieldPath,
+ size: 100
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+ } else {
+ // console.log("local", name);
+
+ return {
+ [topLevelPath]: {
+ filter: {
+ bool: {
+ must: [
+ ...additionFilterTerms(name, searchParams)
+ ]
+ }
+ },
+ aggregations: {
+ [bottomLevelPath]: {
+ terms: {
+ field: fieldPath,
+ size: 100
+ }
+ }
+ }
+ }
+ };
+ }
+ }
+
+ function getBuckets(aggregations: any) {
+ if (aggregations == null) {
+ return [];
+ }
+
+ if (aggregations[topLevelPath]?.[middleLevelPath]?.[bottomLevelPath]?.buckets) {
+ return aggregations[topLevelPath][middleLevelPath][bottomLevelPath].buckets;
+ }
+ else if (aggregations[topLevelPath]?.[bottomLevelPath]?.buckets) {
+ return aggregations[topLevelPath][bottomLevelPath].buckets;
+ } else {
+ return [];
+ }
+ }
+
+ return [getAdditions, getBuckets];
+}
+
+type SearchParamKey = Exclude;
+
+const lookup: Record = {
+ year: {fieldName: "publicationYear", fieldPath: "publicationYear"},
+ language: {fieldName: "languages", fieldPath: "languages.languageCode.keyword"},
+ format: {fieldName: "publicationFormat", fieldPath: "publicationFormat.id.keyword"},
+ audience: {fieldName: "publicationAudience", fieldPath: "publicationAudience.id.keyword"},
+ peerReviewed: {fieldName: "peerReviewed", fieldPath: "peerReviewed.id.keyword"},
+ parentPublicationType: {fieldName: "parentPublicationType", fieldPath: "parentPublicationType.id.keyword"},
+ international: {fieldName: "internationalPublication", fieldPath: "internationalPublication"},
+ articleType: {fieldName: "articleTypeCode", fieldPath: "articleTypeCode"},
+ jufo: {fieldName: "jufoClassCode", fieldPath: "jufoClassCode.keyword"},
+ publicationTypeCode: {fieldName: "publicationTypeCode", fieldPath: "publicationTypeCode.keyword"},
+ publicationStatusCode: {fieldName: "publicationStatusCode", fieldPath: "publicationStatusCode.keyword"},
+ fieldsOfScience: {fieldName: "fieldsOfScience", fieldPath: "fieldsOfScience.fieldIdScience"},
+ openAccess: {fieldName: "openAccess", fieldPath: "openAccess"},
+ publisherOpenAccessCode: {fieldName: "publisherOpenAccessCode", fieldPath: "publisherOpenAccessCode"},
+ selfArchivedCode: {fieldName: "selfArchivedCode", fieldPath: "selfArchivedCode.keyword"},
+ organization: {fieldName: "organization", fieldPath: "author.organization.organizationId.keyword"},
+}
+
+const [additionsFromYear, getYearAdditions] = generateAggregationStep("year", lookup);
+export { getYearAdditions };
+
+const [additionsFromLanguageCode, getLanguageCodeAdditions] = generateAggregationStep("language", lookup);
+export { getLanguageCodeAdditions };
+
+const [additionsFromPublicationFormat, getPublicationFormatAdditions] = generateAggregationStep("format", lookup);
+export { getPublicationFormatAdditions };
+
+const [additionsFromPublicationAudience, getPublicationAudienceAdditions] = generateAggregationStep("audience", lookup);
+export { getPublicationAudienceAdditions };
+
+const [additionsFromPeerReviewed, getPeerReviewedAdditions] = generateAggregationStep("peerReviewed", lookup);
+export { getPeerReviewedAdditions };
+
+const [additionsFromParentPublicationType, getParentPublicationTypeAdditions] = generateAggregationStep("parentPublicationType", lookup);
+export { getParentPublicationTypeAdditions };
+
+const [additionsFromInternationalPublication, getPublisherInternationalityAdditions] = generateAggregationStep("international", lookup);
+export { getPublisherInternationalityAdditions };
+
+const [additionsFromArticleTypeCode, getArticleTypeCodeAdditions] = generateAggregationStep("articleType", lookup);
+export { getArticleTypeCodeAdditions };
+
+const [additionsFromJufoClassCode, getJufoClassCodeAdditions] = generateAggregationStep("jufo", lookup);
+export { getJufoClassCodeAdditions };
+
+function suffixer(locale) {
+ const capitalized = locale.charAt(0).toUpperCase() + locale.slice(1).toLowerCase();
+ return strings => strings[0].replace(/(Fi|Sv|En)(?=[\.A-Z]|$)/g, capitalized);
+}
+
+function termsForOrganization(searchParams: SearchParams) {
+ if (searchParams.organization && searchParams.organization.length > 0) {
+ return [{
+ "nested": {
+ "path": "author",
+ "query": {
+ "bool": {
+ "must": [
+ {
+ "terms": {
+ "author.organization.organizationId.keyword": searchParams.organization
+ }
+ }
+ ]
+ }
+ }
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForFieldsOfScience(searchParams: SearchParams) {
+ if (searchParams.fieldsOfScience && searchParams.fieldsOfScience.length > 0) {
+ return [{
+ "nested": {
+ "path": "fieldsOfScience",
+ "query": {
+ "bool": {
+ "must": [
+ {
+ "terms": {
+ "fieldsOfScience.fieldIdScience": searchParams.fieldsOfScience
+ }
+ }
+ ]
+ }
+ }
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForPublicationTypeCode(searchParams: SearchParams) {
+ if (searchParams.publicationTypeCode) {
+ return [{
+ terms: {
+ "publicationTypeCode.keyword": searchParams.publicationTypeCode
+ }
+ }];
+ }
+ return [];
+}
+
+function termsForOpenAccess(searchParams: SearchParams) {
+ if (searchParams.openAccess) {
+ return [{
+ terms: {
+ "openAccess": searchParams.openAccess
+ }
+ }];
+ }
+ return [];
+}
+
+const [additionsFromOpenAccess, getOpenAccessAdditions] = generateAggregationStep("openAccess", lookup);
+export { getOpenAccessAdditions };
+
+function termsForPublisherOpenAccessCode(searchParams: SearchParams) {
+ if (searchParams.publisherOpenAccessCode) {
+ return [{
+ terms: {
+ "publisherOpenAccessCode": searchParams.publisherOpenAccessCode
+ }
+ }];
+ }
+ return [];
+}
+
+const [additionsFromPublisherOpenAccessCode, getPublisherOpenAccessCodeAdditions] = generateAggregationStep("publisherOpenAccessCode", lookup);
+export { getPublisherOpenAccessCodeAdditions };
+
+function termsForSelfArchivedCode(searchParams: SearchParams) {
+ if (searchParams.selfArchivedCode) {
+ return [{
+ terms: {
+ "selfArchivedCode.keyword": searchParams.selfArchivedCode
+ }
+ }];
+ }
+ return [];
+}
+
+const [additionsFromSelfArchivedCode, getSelfArchivedCodeAdditions] = generateAggregationStep("selfArchivedCode", lookup);
+export { getSelfArchivedCodeAdditions };
+
+const [additionsFromPublicationTypeCode, getPublicationTypeCodeAdditions] = generateAggregationStep("publicationTypeCode", lookup);
+export { getPublicationTypeCodeAdditions };
+
+type OrgsAggsResponse = {
+ aggregations: {
+ filtered_authors: {
+ single_sector: {
+ organizations: {
+ composite_orgs: {
+ buckets: Array<{
+ key: {
+ id: string;
+ name: string;
+ };
+ doc_count: number;
+ }>;
+ };
+ };
+ };
+ };
+ };
+};
+
+function filteringTerms(searchParams: SearchParams) {
+ return [
+ ...termsForYear(searchParams),
+ ...termsForTypeCode(searchParams),
+ ...termsForStatusCode(searchParams),
+ ...termsForOrganization(searchParams),
+ ...termsForLanguageCode(searchParams),
+ ...termsForPublicationFormat(searchParams),
+ ...termsForPublicationAudience(searchParams),
+ ...termsForPeerReviewed(searchParams),
+ ...termsForParentPublicationType(searchParams),
+ ...termsForInternationalPublication(searchParams),
+ ...termsForArticleTypeCode(searchParams),
+ ...termsForJufoClassCode(searchParams),
+ ...termsForFieldsOfScience(searchParams),
+ ...termsForPublicationTypeCode(searchParams),
+ ...termsForOpenAccess(searchParams),
+ ...termsForPublisherOpenAccessCode(searchParams),
+ ...termsForSelfArchivedCode(searchParams),
+ ];
+}
+
+function aggregationTerms(searchParams: SearchParams) {
+ return {
+ ...additionsFromYear(searchParams),
+ ...additionsFromOrganization(searchParams),
+ ...additionsFromLanguageCode(searchParams),
+ ...additionsFromPublicationFormat(searchParams),
+ ...additionsFromPublicationAudience(searchParams),
+ ...additionsFromPeerReviewed(searchParams),
+ ...additionsFromParentPublicationType(searchParams),
+ ...additionsFromInternationalPublication(searchParams),
+ ...additionsFromArticleTypeCode(searchParams),
+ ...additionsFromJufoClassCode(searchParams),
+ ...additionsFromFieldsOfScience(searchParams),
+ ...additionsFromPublicationTypeCode(searchParams),
+ ...additionsFromOpenAccess(searchParams),
+ ...additionsFromPublisherOpenAccessCode(searchParams),
+ ...additionsFromSelfArchivedCode(searchParams),
+ };
+}
+
+function additionFilterTerms(excluded: SearchParamKey, searchParams: SearchParams) {
+ return [
+ matchingTerms(searchParams),
+ ...(excluded === "year" ? [] : termsForYear(searchParams)),
+ ...(excluded === "publicationStatusCode" ? [] : termsForStatusCode(searchParams)),
+ ...(excluded === "organization" ? [] : termsForOrganization(searchParams)),
+ ...(excluded === "language" ? [] : termsForLanguageCode(searchParams)),
+ ...(excluded === "format" ? [] : termsForPublicationFormat(searchParams)),
+ ...(excluded === "audience" ? [] : termsForPublicationAudience(searchParams)),
+ ...(excluded === "peerReviewed" ? [] : termsForPeerReviewed(searchParams)),
+ ...(excluded === "parentPublicationType" ? [] : termsForParentPublicationType(searchParams)),
+ ...(excluded === "international" ? [] : termsForInternationalPublication(searchParams)),
+ ...(excluded === "articleType" ? [] : termsForArticleTypeCode(searchParams)),
+ ...(excluded === "jufo" ? [] : termsForJufoClassCode(searchParams)),
+ ...(excluded === "fieldsOfScience" ? [] : termsForFieldsOfScience(searchParams)),
+ ...(excluded === "publicationTypeCode" ? [] : termsForPublicationTypeCode(searchParams)),
+ ...(excluded === "openAccess" ? [] : termsForOpenAccess(searchParams)),
+ ...(excluded === "publisherOpenAccessCode" ? [] : termsForPublisherOpenAccessCode(searchParams)),
+ ...(excluded === "selfArchivedCode" ? [] : termsForSelfArchivedCode(searchParams)),
+ ];
+}
+
+function sortingTerms(searchParams: SearchParams) {
+ let sortedField = "publicationYear";
+ let direction = "desc";
+
+ const textFields = ["publicationName", "authorsText", "publisherName"];
+
+ if (searchParams.sort != null) {
+ const value = searchParams.sort.pop();
+
+ sortedField = value.replace(/Desc$/, "");
+ direction = value.endsWith("Desc") ? "desc" : "asc";
+
+ if (textFields.includes(sortedField)) {
+ sortedField += ".keyword";
+ }
+ }
+
+ return [
+ { [sortedField]: { "order": direction } },
+ "_score"
+ ];
+}
+
+function getOrganizationNameBuckets(response: OrgsAggsResponse) {
+ return response.aggregations.filtered_authors.single_sector.organizations.composite_orgs.buckets;
+}
+
+function additionsFromOrganization(searchParams: SearchParams, global = false) {
+ if (global)
+ return {
+ "all_data_except_organizationId": {
+ "global": {},
+ "aggregations": {
+ "filtered_except_organizationId": {
+ "filter": {
+ "bool": {
+ "must": [
+ ...additionFilterTerms("organization", searchParams),
+ ]
+ }
+ },
+ "aggregations": {
+ "organization_nested": {
+ "nested": {
+ "path": "author"
+ },
+ "aggregations": {
+ "all_organizationIds": {
+ "terms": {
+ "field": "author.organization.organizationId.keyword",
+ "size": 250,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ else
+ return {
+ "all_data_except_organizationId": {
+ "filter": {
+ "bool": {
+ "must": [
+ ...additionFilterTerms("organization", searchParams),
+ ]
+ }
+ },
+ "aggregations": {
+ "organization_nested": {
+ "nested": {
+ "path": "author"
+ },
+ "aggregations": {
+ "all_organizationIds": {
+ "terms": {
+ "field": "author.organization.organizationId.keyword",
+ "size": 250,
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+export function getOrganizationAdditions(aggregations: /*OrganizationAggregation*/ any, global = false) {
+ if (global)
+ return aggregations.all_data_except_organizationId?.filtered_except_organizationId.organization_nested.all_organizationIds.buckets ?? [];
+ else
+ return aggregations.all_data_except_organizationId.organization_nested.all_organizationIds.buckets ?? [];
+}
+
+function additionsFromFieldsOfScience(searchParams: SearchParams, global = false) {
+ if (global)
+ return {
+ "all_data_except_fieldsOfScience": {
+ "global": {},
+ "aggregations": {
+ "filtered_except_fieldsOfScience": {
+ "filter": {
+ "bool": {
+ "must": [
+ ...additionFilterTerms("fieldsOfScience", searchParams),
+ ]
+ }
+ },
+ "aggregations": {
+ "fieldsOfScience_nested": {
+ "nested": {
+ "path": "fieldsOfScience"
+ },
+ "aggregations": {
+ "all_fieldsOfScience": {
+ "terms": {
+ "field": "fieldsOfScience.fieldIdScience",
+ "size": 1000,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ else
+ return {
+ "all_data_except_fieldsOfScience": {
+ "filter": {
+ "bool": {
+ "must": [
+ ...additionFilterTerms("fieldsOfScience", searchParams),
+ ]
+ }
+ },
+ "aggregations": {
+ "fieldsOfScience_nested": {
+ "nested": {
+ "path": "fieldsOfScience"
+ },
+ "aggregations": {
+ "all_fieldsOfScience": {
+ "terms": {
+ "field": "fieldsOfScience.fieldIdScience",
+ "size": 1000,
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+export function getFieldsOfScienceAdditions(aggregations: any, global = false) {
+ if (global)
+ return aggregations.all_data_except_fieldsOfScience?.filtered_except_fieldsOfScience.fieldsOfScience_nested.all_fieldsOfScience.buckets ?? [];
+ else
+ return aggregations.all_data_except_fieldsOfScience.fieldsOfScience_nested.all_fieldsOfScience.buckets ?? [];
+}
diff --git a/src/app/shared/components/column-sorter/column-sorter.component.html b/src/app/shared/components/column-sorter/column-sorter.component.html
new file mode 100644
index 000000000..793fea2d0
--- /dev/null
+++ b/src/app/shared/components/column-sorter/column-sorter.component.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/shared/components/column-sorter/column-sorter.component.scss b/src/app/shared/components/column-sorter/column-sorter.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/shared/components/column-sorter/column-sorter.component.ts b/src/app/shared/components/column-sorter/column-sorter.component.ts
new file mode 100644
index 000000000..5ab1ba7c4
--- /dev/null
+++ b/src/app/shared/components/column-sorter/column-sorter.component.ts
@@ -0,0 +1,29 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { NgIf, NgSwitch, NgSwitchCase } from '@angular/common';
+
+@Component({
+ selector: 'app-column-sorter',
+ templateUrl: './column-sorter.component.html',
+ styleUrls: ['./column-sorter.component.scss'],
+ imports: [
+ NgSwitch, NgSwitchCase, NgIf
+ ],
+ standalone: true
+})
+export class ColumnSorterComponent {
+ @Input() name: string;
+ @Input() value: string;
+
+ /*@Input() direction: number;
+ @Output() directionChange = new EventEmitter();
+
+ toggleSort() {
+ if (this.direction === 0) {
+ this.directionChange.emit(1);
+ } else if (this.direction === 1) {
+ this.directionChange.emit(-1);
+ } else {
+ this.directionChange.emit(0);
+ }
+ }*/
+}
diff --git a/src/app/shared/components/pagination/pagination.component.ts b/src/app/shared/components/pagination/pagination.component.ts
index 465ee9a72..486a1a131 100644
--- a/src/app/shared/components/pagination/pagination.component.ts
+++ b/src/app/shared/components/pagination/pagination.component.ts
@@ -5,9 +5,9 @@
// :author: CSC - IT Center for Science Ltd., Espoo Finland servicedesk@csc.fi
// :license: MIT
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, Input, OnChanges } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { Observable } from 'rxjs';
+import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import {
faAngleDoubleLeft,
faAngleDoubleRight,
@@ -23,30 +23,36 @@ import { map } from 'rxjs/operators';
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.scss'],
})
-export class PaginationComponent implements OnInit {
- @Input() page: number = 1;
- @Input() pageSize: number = 10;
- @Input() total: any;
+export class PaginationComponent implements OnChanges {
+ @Input() page = 1;
+ @Input() pageSize = 10;
+ @Input() total = 0;
- fromPage: number; // Used for HTML rendering
+ changed$ = new BehaviorSubject(null);
- ngOnInit(): void {
+ fromPage = (this.page - 1) * this.pageSize;
+
+ ngOnChanges() {
this.fromPage = (this.page - 1) * this.pageSize;
+ this.changed$.next();
}
- pages$: Observable = this.breakpointObserver.observe('(min-width: 1200px)').pipe(
- map(result => generatePages(this.page, result.matches ? 9 : 5, this.total, this.pageSize))
+ pages$: Observable = combineLatest([
+ this.breakpointObserver.observe('(min-width: 1200px)'),
+ this.changed$,
+ ]).pipe(
+ map(([result]) => generatePages(this.page, result.matches ? 9 : 5, this.total, this.pageSize))
);
order$ = this.breakpointObserver.observe('(min-width: 768px)').pipe(
map(result => result.matches)
);
+ previous = $localize`:@@previous:Edellinen`;
+ next = $localize`:@@next:Seuraava`;
previousPage = $localize`:@@previousPage:Edellinen sivu`;
nextPage = $localize`:@@nextPage:Seuraava sivu`;
tooManyResultstext = $localize`:@@tooManyResultsNavigationDisabled:Liikaa tuloksia. Haun loppuun navigoiminen estetty.`;
- previous = $localize`:@@previous:Edellinen`;
- next = $localize`:@@next:Seuraava`;
faAngleRight = faAngleRight;
faAngleLeft = faAngleLeft;
@@ -99,8 +105,8 @@ function generatePages(currentPage: number, range: 5 | 9, results: number, pageS
output = [i-4, i-3, i-2, i-1, i, i+1, i+2, i+3, i+4];
}
- let min = Math.min(...output);
- let max = Math.max(...output);
+ const min = Math.min(...output);
+ const max = Math.max(...output);
if (min < 1) {
const increment = 1 - output[0];
diff --git a/src/app/shared/pipes/first-digit.pipe.ts b/src/app/shared/pipes/first-digit.pipe.ts
new file mode 100644
index 000000000..629c18b00
--- /dev/null
+++ b/src/app/shared/pipes/first-digit.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'firstDigit',
+ standalone: true
+})
+export class FirstDigitPipe implements PipeTransform {
+ transform(value: number): number {
+ const firstDigit = Math.abs(value).toString()[0];
+ return parseInt(firstDigit);
+ }
+}
diff --git a/src/app/shared/pipes/first-letter.pipe.ts b/src/app/shared/pipes/first-letter.pipe.ts
new file mode 100644
index 000000000..15240c59f
--- /dev/null
+++ b/src/app/shared/pipes/first-letter.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'firstLetter',
+ standalone: true
+})
+export class FirstLetterPipe implements PipeTransform {
+ transform(value: string): string {
+ return value[0];
+ }
+}
diff --git a/src/environments/locale.en.ts b/src/environments/locale.en.ts
new file mode 100644
index 000000000..a1f375fae
--- /dev/null
+++ b/src/environments/locale.en.ts
@@ -0,0 +1 @@
+export const locale = "en";
diff --git a/src/environments/locale.sv.ts b/src/environments/locale.sv.ts
new file mode 100644
index 000000000..8d8ad389c
--- /dev/null
+++ b/src/environments/locale.sv.ts
@@ -0,0 +1 @@
+export const locale = "sv";
diff --git a/src/environments/locale.ts b/src/environments/locale.ts
new file mode 100644
index 000000000..bd6bfcf3c
--- /dev/null
+++ b/src/environments/locale.ts
@@ -0,0 +1,2 @@
+// Intended to be used outside of Angular's Dependency Injection system.
+export const locale = "fi";
diff --git a/src/i18n/messages.en.xlf b/src/i18n/messages.en.xlf
index b1ee06746..4a5cb3781 100644
--- a/src/i18n/messages.en.xlf
+++ b/src/i18n/messages.en.xlf
@@ -570,6 +570,10 @@
50
+
+ Ei-vertaisarvioitu
+ Non Peer-Reviewed
+
Avoin saatavuus
Open access
@@ -6475,6 +6479,289 @@
Tieto- ja viestintätekniset sovellukset
ICT applications
+
+
+ Yliopisto
+ University
+
+
+
+ Ammattikorkeakoulu
+ University of Applied Sciences
+
+
+
+ Tutkimuslaitos
+ Research institute
+
+
+
+ Yliopistollisen sairaalan erityisvastuualue
+ University Hospital
+
+
+
+ Muu
+ Other
+
+
+
+ Kotimainen
+ Domestic
+
+
+
+ Kansainvälinen
+ International
+
+
+
+
+
+ Avoimesti saatavilla:
+ Open access:
+
+
+
+ Julkaisu on avoimesti saatavilla kustantajan palvelussa.
+ The publication is openly available in the publisher's service.
+
+
+
+ Ei avoin:
+ Not open:
+
+
+
+ Julkaisu ei ole avoimesti saatavilla kustantajan palvelussa.
+ The publication is not openly available in the publisher's service.
+
+
+
+ Ei tietoa:
+ Unknown:
+
+
+
+ Tietoa ei ole raportoitu.
+ No information has been reported.
+
+
+
+
+
+ Kokonaan avoin julkaisukanava:
+ Fully open publication channel:
+
+
+
+ Julkaisukanavan kaikki julkaisut ovat pysyvästi avoimesti saatavilla.
+ All publications are permanently openly available.
+
+
+
+ Osittain avoin julkaisukanava:
+ Partially open publication channel:
+
+
+
+ Julkaisukanavassa vain osa julkaisuista on pysyvästi avoimesti saatavilla.
+ Only some of the publications are permanently openly available.
+
+
+
+ Viivästetysti avoin julkaisukanava:
+ Delayed open publication channel:
+
+
+
+ Julkaisukanavan tieteelliset artikkelit avataan kustantajan määrittelemän viiveajan jälkeen.
+ Scientific articles are opened after a delay time defined by the publisher.
+
+
+
+ Ei vastausta:
+ No response:
+
+
+
+ Julkaisukanava ei ole kokonaan, osittain tai viivästetysti avoin.
+ The publication channel is not fully, partially, or delayed open.
+
+
+
+ Ei tietoa:
+ Unknown:
+
+
+
+ Tietoa ei ole raportoitu.
+ No information has been reported.
+
+
+
+
+
+ Rinnakkaistallennettu:
+ Self-archived:
+
+
+
+ Julkaisu on tallennettu organisaatio- tai tieteenalakohtaiseen julkaisuarkistoon joko välittömästi tai kustantajan määrittämän kohtuullisen embargoajan jälkeen.
+ The publication has been published in a publication archive specific to an organization or field of science either immediately or after a reasonable embargo period set by the publisher.
+
+
+
+
+
+ Avoimesti saatavilla
+ Open access
+
+
+
+ Ei avoin
+ Not open
+
+
+
+ Ei tietoa
+ Unknown
+
+
+
+ Kokonaan avoin julkaisukanava
+ Fully open publication channel
+
+
+
+ Osittain avoin julkaisukanava
+ Partially open publication channel
+
+
+
+ Viivästetysti avoin julkaisukanava
+ Delayed open publication channel
+
+
+
+ Ei vastausta
+ No answer
+
+
+
+ Ei tietoa
+ No information
+
+
+
+ Rinnakkaistallennettu
+ Self-archived
+
+
+
+ Ei rinnakkaistallennettu
+ Not self-archived
+
+
+
+ Ei tietoa
+ Unknown
+
+
+
+
+ Rinnakkaistallenne
+ Self-archived
+
+
+
+
+
+ Uuteen hakuun
+ To new search
+
+
+
+ Testaa haun uudistettua betaversiota. Betaversio toimii vain julkaisujen hakuun. Tiedot vastaavat nykyisen sivuston sisältöä julkaisujen osalta.
+ Test the beta version of the improved search. The improved search only works in publications. The information corresponds to the content of the current site for publications.
+
+
+
+ Nykyiseen versioon
+ Back to the current version
+
+
+
+ Olet haun uudistetussa betaversiossa. Voit palata takaisin koko sivustolle.
+ You are in the improved beta version of the search. You can go back to the whole site.
+
+
+
+ Testaa uudistetun haun betaversiota. Uudistettua hakua on toistaiseksi mahdollista testata ainoastaan julkaisutietojen hakemisessa.
+ Test the beta version of the improved search function. The new search can currently only be tested in publication information retrieval.
+
+
+
+ Kirjoita hakukenttään esimerkiksi julkaisun tai tekijän nimi, tai julkaisuun liittyvä asiasana.
+ Write in the search field, for example, the name of the publication or author, or a keyword related to the publication.
+
+
+
+ Haku käyttää sanojen välissä oletuksena OR-hakuoperaattioria. Halutessasi voit käyttää myös AND- ja NOT- hakuoperaattoireita. Esimerkiksi haku Innovation AND gamification NOT games etsii julkaisuja, joissa esiintyvät yhtä aikaa sanat "innovation" ja "gamification", mutta joissa ei ole mukana sanaa "games". Huomioithan, että AND, NOT ja OR -hakuoperaattorit tulee kirjoittaa hakukenttään isoilla kirjaimilla.
+ The search uses the OR search operator by default between words. If you wish, you can also use the AND and NOT search operators. For example, the search Innovation AND gamification NOT games search for publications that simultaneously contain the words “innovation” and “gamification”, but do not include the word “games”. Please note that the AND, NOT, and OR search operators must be written in capital letters in the search field.
+
+
+
+ Sulkemalla haussa esiintyvät sanat lainausmerkeillä fraasiksi voit etsiä osumia jotka ovat täsmälleen samassa muodossa. Esimerkiksi fraasihaku "Innovations in gamifications" etsii julkaisuja, joissa esiintyy juuri tämä lause. Voit myös yhdistää useampia fraasihakuja AND-, OR- tai NOT-hakuoperaattoreilla.
+ By enclosing the phrases appearing in the search in quotation marks, you can search for matches exactly in the same form. For example, the phrase search "Innovations in gamifications" searches for publications that contain exactly this sentence. You can also combine several phrase searches with AND, OR, or NOT search operators.
+
+
+
+ Voit hakea kohdistetusti joitakin metatietoja suoraan hakukentässä käyttämällä niille erikseen määriteltyjä komentoja. Haussa on käytettävä fraasihakua. Toistaiseksi haku on mahdollista kohdistaa seuraaviin metatietoihin:
+ You can also search for some metadata directly in the search field using commands defined separately for them. Phrase search must be used in the search. At the moment, the search can be targeted at the following metadata:
+
+
+
+ DOI, kirjoita hakukenttään doi:"haettava tunnus", esimerkiksi doi:"100100100"
+ DOI, write in the search field doi:"searched identifier", for example, doi:"100100100"
+
+
+
+ ISSN, kirjoita hakukenttään issn:"haettava tunnnus", esimerkiksi issn:"1234-1234"
+ ISSN, write in the search field issn:"searched identifier", for example, issn:"1234-1234"
+
+
+
+ ISBN, kirjoita hakukenttään isbn:"haettava tunnus", esimerkiksi isbn:"123-0-456-12345-1"
+ ISBN, write in the search field isbn:"searched identifier", for example, isbn:"123-0-456-12345-1"
+
+
+
+ JUFO-tunnus, kirjoita hakukenttään jufoCode:"haettava tunnus", esimerkiksi jufoCode:"12345"
+ JUFO code, write in the search field jufoCode:"searched identifier", for example, jufoCode:"12345"
+
+
+
+ Huomioithan, että kohdistetussa haussa metatietokentät tulee kirjoittaa pienillä kirjaimilla.
+ Please note that metadata fields must be written in lowercase in targeted search.
+
+
+
+
+ Mitä julkaisutietoja palvelu sisältää?
+ What publication information is included in the service?
+
+
+
+ tulosta
+ results
+
+
+
+ sivu
+ page
+
+