diff --git a/package-lock.json b/package-lock.json
index b3b49a24dca..27c5e2a86cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -432,150 +432,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/aix-ppc64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
- "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
- "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
- "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/android-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
- "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
- "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/darwin-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
- "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
- "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/freebsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
- "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
- "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
@@ -592,214 +448,6 @@
"node": ">=12"
}
},
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ia32": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
- "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-loong64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
- "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-mips64el": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
- "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-ppc64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
- "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-riscv64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
- "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-s390x": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
- "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/linux-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
- "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/netbsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
- "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/openbsd-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
- "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/sunos-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
- "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-arm64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
- "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-ia32": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
- "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@angular-devkit/build-angular/node_modules/@esbuild/win32-x64": {
- "version": "0.19.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
- "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@angular-devkit/build-angular/node_modules/@types/node": {
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
@@ -4847,150 +4495,6 @@
"node": ">=10.0.0"
}
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz",
- "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz",
- "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz",
- "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz",
- "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz",
- "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz",
- "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz",
- "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz",
- "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz",
- "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@esbuild/linux-arm64": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz",
@@ -5007,214 +4511,6 @@
"node": ">=12"
}
},
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz",
- "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz",
- "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz",
- "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz",
- "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz",
- "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz",
- "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz",
- "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz",
- "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz",
- "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz",
- "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz",
- "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz",
- "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz",
- "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@gulp-sourcemaps/identity-map": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz",
@@ -6899,84 +6195,6 @@
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
"dev": true
},
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz",
- "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz",
- "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz",
- "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz",
- "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz",
- "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz",
- "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.14.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz",
@@ -7003,110 +6221,6 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz",
- "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz",
- "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz",
- "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz",
- "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz",
- "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz",
- "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz",
- "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.14.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz",
- "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/@schematics/angular": {
"version": "17.3.4",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.4.tgz",
diff --git a/package.json b/package.json
index 20b57a64b6d..33d9170816a 100644
--- a/package.json
+++ b/package.json
@@ -145,7 +145,7 @@
"test-coverage": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng test wise --source-map=false --browsers ChromeHeadlessNoSandbox --watch=false --code-coverage",
"test-e2e-comment": "test-e2e assumes wise is already running.",
"test-e2e": "node ./node_modules/protractor/bin/protractor src/assets/wise5/test-e2e/conf.js",
- "extract-i18n": "ng extract-i18n --output-path=src"
+ "extract-i18n": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng extract-i18n --output-path=src"
},
"repository": {
"type": "git",
@@ -224,4 +224,4 @@
]
]
}
-}
+}
\ No newline at end of file
diff --git a/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts b/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts
index a658ba9d660..3c9cea77c49 100644
--- a/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts
+++ b/src/app/authoring-tool/edit-advanced-component/edit-advanced-component.component.ts
@@ -21,6 +21,7 @@ export abstract class EditAdvancedComponentComponent {
ngOnInit() {
this.componentContent = this.teacherProjectService.getComponent(this.nodeId, this.componentId);
this.component = new Component(this.componentContent, this.nodeId);
+ this.teacherProjectService.uiChanged();
}
setShowSubmitButtonValue(show: boolean = false): void {
diff --git a/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.html b/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.html
index 7585bbc3179..0e9007fed3f 100644
--- a/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.html
+++ b/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.html
@@ -108,5 +108,7 @@
Advanced Settings
- Close
+
+ Close
+
diff --git a/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.ts b/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.ts
index d552abea441..b665cade816 100644
--- a/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.ts
+++ b/src/app/authoring-tool/edit-component-advanced/edit-component-advanced.component.ts
@@ -1,4 +1,4 @@
-import { Component, Inject, OnInit } from '@angular/core';
+import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Component as WiseComponent } from '../../../assets/wise5/common/Component';
@@ -7,8 +7,6 @@ import { Component as WiseComponent } from '../../../assets/wise5/common/Compone
templateUrl: './edit-component-advanced.component.html',
styleUrls: ['./edit-component-advanced.component.scss']
})
-export class EditComponentAdvancedComponent implements OnInit {
+export class EditComponentAdvancedComponent {
constructor(@Inject(MAT_DIALOG_DATA) protected component: WiseComponent) {}
-
- ngOnInit(): void {}
}
diff --git a/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.html b/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.html
index 8e869467ba4..43c6681eefe 100644
--- a/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.html
+++ b/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.html
@@ -1,12 +1,11 @@
-
- Prompt
-
-
+
diff --git a/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.ts b/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.ts
index 0842366cf93..510240641b7 100644
--- a/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.ts
+++ b/src/app/authoring-tool/edit-component-prompt/edit-component-prompt.component.ts
@@ -1,14 +1,12 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { ComponentContent } from '../../../assets/wise5/common/ComponentContent';
@Component({
selector: 'edit-component-prompt',
- styles: ['.prompt {width: 100%; }'],
+ styles: ['.prompt {width: 100%; mat-form-field { width:100%} }'],
templateUrl: 'edit-component-prompt.component.html'
})
export class EditComponentPrompt {
- @Input()
- prompt: string;
-
- @Output()
- promptChangedEvent = new EventEmitter();
+ @Input() componentContent: ComponentContent;
+ @Output() promptChangedEvent = new EventEmitter();
}
diff --git a/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html b/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html
index 791c0d31ddb..0c8f2e614a1 100644
--- a/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html
+++ b/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.html
@@ -3,7 +3,7 @@
message
-
-
+ [content]="componentContent"
+ key="rubric"
+ (defaultLanguageTextChanged)="rubricChanged()"
+/>
diff --git a/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts b/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts
index 2ab06f7ce1d..dc27a3c1e71 100644
--- a/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts
+++ b/src/app/authoring-tool/edit-component-rubric/edit-component-rubric.component.ts
@@ -1,10 +1,5 @@
import { Component, Input } from '@angular/core';
-import { ConfigService } from '../../../assets/wise5/services/configService';
import { TeacherProjectService } from '../../../assets/wise5/services/teacherProjectService';
-import {
- insertWiseLinks,
- replaceWiseLinks
-} from '../../../assets/wise5/common/wise-link/wise-link';
@Component({
selector: 'edit-component-rubric',
@@ -12,31 +7,12 @@ import {
styleUrls: ['edit-component-rubric.component.scss']
})
export class EditComponentRubricComponent {
- @Input()
- componentContent: any;
- rubric: string;
- showRubricAuthoring: boolean = false;
+ @Input() componentContent: any;
+ protected showRubricAuthoring: boolean = false;
- constructor(
- private ConfigService: ConfigService,
- private ProjectService: TeacherProjectService
- ) {}
+ constructor(private projectService: TeacherProjectService) {}
- ngOnInit() {
- const componentContent = this.ConfigService.replaceStudentNames(
- this.ProjectService.injectAssetPaths(this.componentContent)
- );
- if (componentContent.rubric == null) {
- this.rubric = '';
- } else {
- this.rubric = replaceWiseLinks(componentContent.rubric);
- }
- }
-
- rubricChanged(): void {
- this.componentContent.rubric = this.ConfigService.removeAbsoluteAssetPaths(
- insertWiseLinks(this.rubric)
- );
- this.ProjectService.componentChanged();
+ protected rubricChanged(): void {
+ this.projectService.componentChanged();
}
}
diff --git a/src/app/authoring-tool/edit-dynamic-prompt-rules/edit-dynamic-prompt-rules.component.html b/src/app/authoring-tool/edit-dynamic-prompt-rules/edit-dynamic-prompt-rules.component.html
index 86f54705ed7..eef7d78ecac 100644
--- a/src/app/authoring-tool/edit-dynamic-prompt-rules/edit-dynamic-prompt-rules.component.html
+++ b/src/app/authoring-tool/edit-dynamic-prompt-rules/edit-dynamic-prompt-rules.component.html
@@ -43,16 +43,14 @@
(ngModelChange)="inputChanged.next($event)"
/>
-
- Prompt
-
-
+
-
-
- Pre Prompt (Optional)
-
-
-
+
-
-
- Post Prompt (Optional)
-
-
-
+
diff --git a/src/app/authoring-tool/edit-dynamic-prompt/edit-dynamic-prompt.component.scss b/src/app/authoring-tool/edit-dynamic-prompt/edit-dynamic-prompt.component.scss
index 573b30f080a..625669d5b10 100644
--- a/src/app/authoring-tool/edit-dynamic-prompt/edit-dynamic-prompt.component.scss
+++ b/src/app/authoring-tool/edit-dynamic-prompt/edit-dynamic-prompt.component.scss
@@ -1,9 +1,5 @@
@import 'style/abstracts/variables';
-.prompt {
- width: 100%;
-}
-
.section-container {
padding: 16px;
border: 2px solid #dddddd;
diff --git a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.html b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.html
index ee4bac274c3..2ceed4bdbc0 100644
--- a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.html
+++ b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.html
@@ -56,6 +56,7 @@
fxLayoutGap="8px"
>
@@ -63,25 +64,13 @@
1" i18n
>Question #{{ questionIndex + 1 }}
-
-
-
-
-
-
-
+
clear
-
- add_circle
-
+
+
+
+ 1"
+ matTooltip="Delete question"
+ i18n-matTooltip
+ matTooltipPosition="before"
+ (click)="deleteFeedbackInRule(rule, questionIndex)"
+ >
+ clear
+
+
+
+
+
+ add_circle
+
+
diff --git a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.scss b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.scss
index 41baff19086..e2b65bf609f 100644
--- a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.scss
+++ b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.scss
@@ -48,3 +48,7 @@ li {
.question-input {
width: 100%;
}
+
+.question-button-div {
+ height: 100%;
+}
\ No newline at end of file
diff --git a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.ts b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.ts
index 8292bafc22a..f9489c8cd45 100644
--- a/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.ts
+++ b/src/app/authoring-tool/edit-question-bank-rules/edit-question-bank-rules.component.ts
@@ -54,4 +54,10 @@ export class EditQuestionBankRulesComponent extends EditFeedbackRulesComponent {
customTrackBy(index: number): number {
return index;
}
+
+ protected getQuestionLabel(rule: QuestionBankRule, questionIndex: number): string {
+ return rule.questions.length === 1
+ ? $localize`Question`
+ : $localize`Question #${questionIndex + 1}`;
+ }
}
diff --git a/src/app/authoring-tool/edit-question-bank/edit-question-bank.component.html b/src/app/authoring-tool/edit-question-bank/edit-question-bank.component.html
index f176c47c459..8404976fb3f 100644
--- a/src/app/authoring-tool/edit-question-bank/edit-question-bank.component.html
+++ b/src/app/authoring-tool/edit-question-bank/edit-question-bank.component.html
@@ -11,16 +11,13 @@
-
- Custom Label
-
-
+
+ arrow_drop_down translate {{ selectedLanguage.language }}
+
+
+
+ {{ language.language }}
+
+
diff --git a/src/app/common/project-language-chooser/project-language-chooser.component.scss b/src/app/common/project-language-chooser/project-language-chooser.component.scss
new file mode 100644
index 00000000000..2cf5c68da5b
--- /dev/null
+++ b/src/app/common/project-language-chooser/project-language-chooser.component.scss
@@ -0,0 +1,3 @@
+.mat-mdc-button {
+ text-transform: none;
+}
diff --git a/src/app/common/project-language-chooser/project-language-chooser.component.spec.ts b/src/app/common/project-language-chooser/project-language-chooser.component.spec.ts
new file mode 100644
index 00000000000..3dfb285e3fe
--- /dev/null
+++ b/src/app/common/project-language-chooser/project-language-chooser.component.spec.ts
@@ -0,0 +1,69 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ProjectLanguageChooserComponent } from './project-language-chooser.component';
+import { ProjectLocale } from '../../domain/projectLocale';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { HarnessLoader } from '@angular/cdk/testing';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
+import { MatMenuHarness, MatMenuItemHarness } from '@angular/material/menu/testing';
+import { ProjectService } from '../../../assets/wise5/services/projectService';
+
+class MockProjectService {
+ currentLanguage() {
+ return null;
+ }
+}
+
+let loader: HarnessLoader;
+let component: ProjectLanguageChooserComponent;
+let fixture: ComponentFixture;
+describe('ProjectLanguageChooserComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [BrowserAnimationsModule, HttpClientTestingModule, ProjectLanguageChooserComponent],
+ providers: [{ provide: ProjectService, useClass: MockProjectService }]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProjectLanguageChooserComponent);
+ loader = TestbedHarnessEnvironment.loader(fixture);
+ component = fixture.componentInstance;
+ setProjectLocale(new ProjectLocale({ default: 'en_US', supported: ['ja', 'es'] }));
+ });
+
+ it('shows available languages and selects the default language', async () => {
+ const options = await getOptions();
+ expect(options.length).toEqual(3);
+ expect(await options[0].getText()).toMatch('English');
+ expect(await options[1].getText()).toMatch('Japanese');
+ expect(await options[2].getText()).toMatch('Spanish');
+ const selected = await options[0].host();
+ expect(await selected.getAttribute('class')).toContain('primary');
+ });
+
+ it('keeps selected language option when language option changes', async () => {
+ const menuHarness = await loader.getHarness(MatMenuHarness);
+ await menuHarness.clickItem({ text: 'Japanese' });
+ spyOn(TestBed.inject(ProjectService), 'currentLanguage').and.returnValue({
+ locale: 'ja',
+ language: 'Japanese'
+ });
+ setProjectLocale(new ProjectLocale({ default: 'it', supported: ['de', 'fr', 'ja', 'es'] }));
+ const options = await getOptions();
+ const selected = await options[3].host();
+ expect(await selected.getAttribute('class')).toContain('primary');
+ });
+});
+
+function setProjectLocale(locale: ProjectLocale): void {
+ component.projectLocale = locale;
+ component.ngOnChanges();
+ fixture.detectChanges();
+}
+
+async function getOptions(): Promise {
+ const menuHarness = await loader.getHarness(MatMenuHarness);
+ await menuHarness.open();
+ return await menuHarness.getItems();
+}
diff --git a/src/app/common/project-language-chooser/project-language-chooser.component.ts b/src/app/common/project-language-chooser/project-language-chooser.component.ts
new file mode 100644
index 00000000000..b0b91913d0a
--- /dev/null
+++ b/src/app/common/project-language-chooser/project-language-chooser.component.ts
@@ -0,0 +1,42 @@
+import { CommonModule } from '@angular/common';
+import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
+import { Language } from '../../domain/language';
+import { ProjectLocale } from '../../domain/projectLocale';
+import { MatMenuModule } from '@angular/material/menu';
+import { MatIconModule } from '@angular/material/icon';
+import { MatButtonModule } from '@angular/material/button';
+import { MatTooltipModule } from '@angular/material/tooltip';
+import { ProjectService } from '../../../assets/wise5/services/projectService';
+
+@Component({
+ standalone: true,
+ selector: 'project-language-chooser',
+ styleUrl: './project-language-chooser.component.scss',
+ imports: [CommonModule, MatButtonModule, MatIconModule, MatMenuModule, MatTooltipModule],
+ templateUrl: './project-language-chooser.component.html'
+})
+export class ProjectLanguageChooserComponent implements OnChanges {
+ protected availableLanguages: Language[];
+ @Output() languageChangedEvent = new EventEmitter();
+ @Input() projectLocale: ProjectLocale;
+ protected selectedLanguage: Language;
+ @Input() tooltip: string = $localize`Select language`;
+
+ constructor(private projectService: ProjectService) {}
+
+ ngOnChanges(): void {
+ this.availableLanguages = this.projectLocale.getAvailableLanguages();
+ this.selectedLanguage = this.projectService.currentLanguage();
+ if (
+ this.selectedLanguage == null ||
+ !this.availableLanguages.some((lang) => lang.locale === this.selectedLanguage.locale)
+ ) {
+ this.selectedLanguage = this.projectLocale.getDefaultLanguage();
+ }
+ }
+
+ protected changeLanguage(language: Language): void {
+ this.selectedLanguage = language;
+ this.languageChangedEvent.emit(language);
+ }
+}
diff --git a/src/app/domain/language.ts b/src/app/domain/language.ts
new file mode 100644
index 00000000000..9a73bb4f2a8
--- /dev/null
+++ b/src/app/domain/language.ts
@@ -0,0 +1,4 @@
+export interface Language {
+ language: string;
+ locale: string;
+}
diff --git a/src/app/domain/localeToLanguage.ts b/src/app/domain/localeToLanguage.ts
new file mode 100644
index 00000000000..c18f05d2bbc
--- /dev/null
+++ b/src/app/domain/localeToLanguage.ts
@@ -0,0 +1,12 @@
+export const localeToLanguage: { [locale: string]: string } = {
+ zh_CN: $localize`Chinese (Simplified)`,
+ zh_TW: $localize`Chinese (Traditional)`,
+ nl: $localize`Dutch`,
+ en_US: $localize`English`,
+ de: $localize`German`,
+ it: $localize`Italian`,
+ ja: $localize`Japanese`,
+ ko: $localize`Korean`,
+ es: $localize`Spanish`,
+ vi: $localize`Vietnamese`
+};
diff --git a/src/app/domain/projectLocale.ts b/src/app/domain/projectLocale.ts
new file mode 100644
index 00000000000..6c1e676d0b2
--- /dev/null
+++ b/src/app/domain/projectLocale.ts
@@ -0,0 +1,52 @@
+import { Language } from './language';
+import { localeToLanguage } from './localeToLanguage';
+
+export class ProjectLocale {
+ private locale: { default: string; supported: string[] };
+
+ constructor(locale: any) {
+ this.locale = locale;
+ }
+
+ getAvailableLanguages(): Language[] {
+ return [this.getDefaultLanguage()].concat(this.getSupportedLanguages());
+ }
+
+ getDefaultLanguage(): Language {
+ return { language: localeToLanguage[this.locale.default], locale: this.locale.default };
+ }
+
+ setDefaultLocale(locale: string): void {
+ this.locale.default = locale;
+ this.locale.supported = this.locale.supported.filter(
+ (supportedLocale) => supportedLocale != locale
+ );
+ }
+
+ getSupportedLanguages(): Language[] {
+ return this.locale.supported.map((locale) => ({
+ language: localeToLanguage[locale],
+ locale: locale
+ }));
+ }
+
+ setSupportedLanguages(languages: Language[]): void {
+ this.locale.supported = languages.map((language) => language.locale);
+ }
+
+ hasTranslations(): boolean {
+ return this.locale.supported.length > 0;
+ }
+
+ hasTranslationsToApply(locale: string): boolean {
+ return !this.isDefaultLocale(locale) && this.hasLocale(locale);
+ }
+
+ isDefaultLocale(locale: string): boolean {
+ return this.locale.default === locale;
+ }
+
+ private hasLocale(locale: string): boolean {
+ return this.locale.supported.includes(locale);
+ }
+}
diff --git a/src/app/domain/translations.ts b/src/app/domain/translations.ts
new file mode 100644
index 00000000000..c8aadf8f3bf
--- /dev/null
+++ b/src/app/domain/translations.ts
@@ -0,0 +1,6 @@
+export interface Translations extends Record {}
+
+export interface TranslationValue {
+ value: string;
+ modified: number;
+}
diff --git a/src/app/notebook/notebook-item/notebook-item.component.ts b/src/app/notebook/notebook-item/notebook-item.component.ts
index 938e3af8212..3e8441110fa 100644
--- a/src/app/notebook/notebook-item/notebook-item.component.ts
+++ b/src/app/notebook/notebook-item/notebook-item.component.ts
@@ -55,6 +55,10 @@ export class NotebookItemComponent {
);
}
+ ngOnChanges(): void {
+ this.label = this.config.itemTypes[this.type].label;
+ }
+
ngOnDestroy(): void {
this.notebookUpdatedSubscription.unsubscribe();
}
diff --git a/src/app/notebook/notebook-launcher/notebook-launcher.component.ts b/src/app/notebook/notebook-launcher/notebook-launcher.component.ts
index 5f47babf9b4..8140bc35885 100644
--- a/src/app/notebook/notebook-launcher/notebook-launcher.component.ts
+++ b/src/app/notebook/notebook-launcher/notebook-launcher.component.ts
@@ -1,5 +1,7 @@
import { Component, Input } from '@angular/core';
import { NotebookService } from '../../../assets/wise5/services/notebookService';
+import { ProjectService } from '../../../assets/wise5/services/projectService';
+import { Subscription } from 'rxjs';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
@@ -13,10 +15,20 @@ import { MatTooltipModule } from '@angular/material/tooltip';
export class NotebookLauncherComponent {
protected label: string = '';
@Input() notebookConfig: any;
+ private subscription: Subscription = new Subscription();
- constructor(private notebookService: NotebookService) {}
+ constructor(private notebookService: NotebookService, private projectService: ProjectService) {}
ngOnInit(): void {
+ this.setLabel();
+ this.subscription.add(this.projectService.projectParsed$.subscribe(() => this.setLabel()));
+ }
+
+ ngOnDestroy(): void {
+ this.subscription.unsubscribe();
+ }
+
+ private setLabel(): void {
this.label = this.notebookConfig.itemTypes.note.label.link;
}
diff --git a/src/app/notebook/notebook-notes/notebook-notes.component.ts b/src/app/notebook/notebook-notes/notebook-notes.component.ts
index 70f3193ae21..40c86cc97e6 100644
--- a/src/app/notebook/notebook-notes/notebook-notes.component.ts
+++ b/src/app/notebook/notebook-notes/notebook-notes.component.ts
@@ -37,7 +37,7 @@ export class NotebookNotesComponent extends NotebookParentComponent {
ngOnInit(): void {
super.ngOnInit();
- this.label = this.config.itemTypes.note.label;
+ this.setLabel();
this.addPersonalGroupToGroups();
this.addSpacesToGroups();
this.hasPrivateNotes = this.isHasPrivateNotes();
@@ -76,6 +76,13 @@ export class NotebookNotesComponent extends NotebookParentComponent {
})
);
+ this.subscriptions.add(
+ this.ProjectService.projectParsed$.subscribe(() => {
+ this.setConfig();
+ this.setLabel();
+ })
+ );
+
this.NotebookService.retrievePublicNotebookItems('public');
}
@@ -83,6 +90,10 @@ export class NotebookNotesComponent extends NotebookParentComponent {
this.subscriptions.unsubscribe();
}
+ private setLabel(): void {
+ this.label = this.config.itemTypes.note.label;
+ }
+
isHasPrivateNotes(): boolean {
return this.groupNameToGroup['private'].items.some((note) => note.serverDeleteTime == null);
}
diff --git a/src/app/notebook/notebook-report/notebook-report.component.ts b/src/app/notebook/notebook-report/notebook-report.component.ts
index 0b4c4804257..178efd3d989 100644
--- a/src/app/notebook/notebook-report/notebook-report.component.ts
+++ b/src/app/notebook/notebook-report/notebook-report.component.ts
@@ -56,9 +56,6 @@ export class NotebookReportComponent extends NotebookParentComponent {
if (this.mode !== 'classroomMonitor') {
this.reportItem.id = null; // set the id to null so it can be inserted as initial version, as opposed to updated. this is true for both new and just-loaded reports.
}
- this.reportItemContent = this.ProjectService.injectAssetPaths(
- replaceWiseLinks(this.reportItem.content.content)
- );
this.latestAnnotations = this.AnnotationService.getLatestNotebookItemAnnotations(
this.workgroupId,
this.reportId
@@ -105,6 +102,15 @@ export class NotebookReportComponent extends NotebookParentComponent {
}
})
);
+
+ this.subscriptions.add(
+ this.ProjectService.projectParsed$.subscribe(() => {
+ if (this.saveTime == null) {
+ this.setConfig();
+ this.setReportItem();
+ }
+ })
+ );
}
ngOnDestroy(): void {
@@ -125,6 +131,11 @@ export class NotebookReportComponent extends NotebookParentComponent {
} else {
this.reportItem = this.NotebookService.getTemplateReportItemByReportId(this.reportId);
}
+ if (this.reportItem != null) {
+ this.reportItemContent = this.ProjectService.injectAssetPaths(
+ replaceWiseLinks(this.reportItem.content.content)
+ );
+ }
}
calculateHasAnnotation(latestAnnotations: any): boolean {
diff --git a/src/app/services/copyTranslationsService.spec.ts b/src/app/services/copyTranslationsService.spec.ts
new file mode 100644
index 00000000000..f6cdf577fc1
--- /dev/null
+++ b/src/app/services/copyTranslationsService.spec.ts
@@ -0,0 +1,50 @@
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { TeacherProjectService } from '../../assets/wise5/services/teacherProjectService';
+import { StudentTeacherCommonServicesModule } from '../student-teacher-common-services.module';
+import { ProjectLocale } from '../domain/projectLocale';
+import { ComponentContent } from '../../assets/wise5/common/ComponentContent';
+import { ConfigService } from '../../assets/wise5/services/configService';
+import { CopyTranslationsService } from '../../assets/wise5/services/copyTranslationsService';
+import { Node } from '../../assets/wise5/common/Node';
+
+let configService: ConfigService;
+let http: HttpTestingController;
+let projectService: TeacherProjectService;
+let service: CopyTranslationsService;
+describe('CopyTranslationsService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule, StudentTeacherCommonServicesModule],
+ providers: [CopyTranslationsService, TeacherProjectService],
+ teardown: { destroyAfterEach: false }
+ });
+ configService = TestBed.inject(ConfigService);
+ http = TestBed.inject(HttpTestingController);
+ projectService = TestBed.inject(TeacherProjectService);
+ service = TestBed.inject(CopyTranslationsService);
+ });
+ tryCopyComponents();
+});
+
+function tryCopyComponents() {
+ describe('tryCopyComponents()', () => {
+ it('fetches all supported translations', () => {
+ spyOn(projectService, 'getLocale').and.returnValue(
+ new ProjectLocale({ default: 'en_us', supported: ['es', 'ja'] })
+ );
+ spyOn(configService, 'getProjectId').and.returnValue('123');
+ spyOn(configService, 'getConfigParam').and.returnValue('/123/project.json');
+ service.tryCopyComponents({ components: [{ id: 'abc' }] } as Node, [
+ {
+ id: 'abc',
+ type: 'OpenResponse',
+ prompt: 'hello',
+ 'prompt.i18n': { id: 'xyz' }
+ } as ComponentContent
+ ]);
+ http.expectOne(`/123/translations.es.json`).flush({ xyz: {} });
+ http.expectOne(`/123/translations.ja.json`).flush({ xyz: {} });
+ });
+ });
+}
diff --git a/src/app/services/deleteTranslationsService.spec.ts b/src/app/services/deleteTranslationsService.spec.ts
new file mode 100644
index 00000000000..febac4b9ff5
--- /dev/null
+++ b/src/app/services/deleteTranslationsService.spec.ts
@@ -0,0 +1,49 @@
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { TeacherProjectService } from '../../assets/wise5/services/teacherProjectService';
+import { StudentTeacherCommonServicesModule } from '../student-teacher-common-services.module';
+import { DeleteTranslationsService } from '../../assets/wise5/services/deleteTranslationsService';
+import { ProjectLocale } from '../domain/projectLocale';
+import { ComponentContent } from '../../assets/wise5/common/ComponentContent';
+import { ConfigService } from '../../assets/wise5/services/configService';
+
+let configService: ConfigService;
+let http: HttpTestingController;
+let projectService: TeacherProjectService;
+let service: DeleteTranslationsService;
+describe('DeleteTranslationsService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule, StudentTeacherCommonServicesModule],
+ providers: [DeleteTranslationsService, TeacherProjectService],
+ teardown: { destroyAfterEach: false }
+ });
+ configService = TestBed.inject(ConfigService);
+ http = TestBed.inject(HttpTestingController);
+ projectService = TestBed.inject(TeacherProjectService);
+ service = TestBed.inject(DeleteTranslationsService);
+ });
+ tryDeleteComponents();
+});
+
+function tryDeleteComponents() {
+ describe('tryDeleteComponents()', () => {
+ it('fetches all supported translations', () => {
+ spyOn(projectService, 'getLocale').and.returnValue(
+ new ProjectLocale({ default: 'en_us', supported: ['es', 'ja'] })
+ );
+ spyOn(configService, 'getProjectId').and.returnValue('123');
+ spyOn(configService, 'getConfigParam').and.returnValue('/123/project.json');
+ service.tryDeleteComponents([
+ {
+ id: 'abc',
+ type: 'OpenResponse',
+ prompt: 'hello',
+ 'prompt.i18n': { id: 'xyz' }
+ } as ComponentContent
+ ]);
+ http.expectOne(`/123/translations.es.json`).flush({ xyz: {} });
+ http.expectOne(`/123/translations.ja.json`).flush({ xyz: {} });
+ });
+ });
+}
diff --git a/src/app/services/projectService.spec.ts b/src/app/services/projectService.spec.ts
index ae0a23af26e..b2a884d6fcb 100644
--- a/src/app/services/projectService.spec.ts
+++ b/src/app/services/projectService.spec.ts
@@ -514,7 +514,8 @@ function calculateNodeNumbersWhenNoLessons(): void {
service.project = {
nodes: [{ id: 'group0', type: 'group', ids: [], startId: '' }],
startNodeId: 'group0',
- startGroupId: 'group0'
+ startGroupId: 'group0',
+ metadata: {}
};
service.parseProject();
expectNodeIdsToHaveNumbers([{ nodeId: 'group0', number: '0' }]);
diff --git a/src/app/services/sampleData/curriculum/Demo.project.json b/src/app/services/sampleData/curriculum/Demo.project.json
index 0f03547f92b..995b19eb9e6 100644
--- a/src/app/services/sampleData/curriculum/Demo.project.json
+++ b/src/app/services/sampleData/curriculum/Demo.project.json
@@ -2679,6 +2679,7 @@
},
"metadata": {
"title": "Demo Project",
+ "title.i18n": { "id": "d66d3be571e16cf8d0166286a0a632ec", "modified": 123 },
"authors": [
{
"firstName": "g",
diff --git a/src/app/services/sampleData/curriculum/TeacherProjectServiceSpec.project.json b/src/app/services/sampleData/curriculum/TeacherProjectServiceSpec.project.json
index bd80120f958..89021c0d7b9 100644
--- a/src/app/services/sampleData/curriculum/TeacherProjectServiceSpec.project.json
+++ b/src/app/services/sampleData/curriculum/TeacherProjectServiceSpec.project.json
@@ -1,6 +1,7 @@
{
"startGroupId": "group0",
"startNodeId": "node1",
+ "metadata": {},
"nodes": [
{
"id": "group0",
diff --git a/src/app/services/studentProjectTranslationService.spec.ts b/src/app/services/studentProjectTranslationService.spec.ts
new file mode 100644
index 00000000000..c5f62ccb908
--- /dev/null
+++ b/src/app/services/studentProjectTranslationService.spec.ts
@@ -0,0 +1,69 @@
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+import { StudentTeacherCommonServicesModule } from '../student-teacher-common-services.module';
+import { StudentProjectTranslationService } from '../../assets/wise5/services/studentProjectTranslationService';
+import { ProjectService } from '../../assets/wise5/services/projectService';
+import { StudentDataService } from '../../assets/wise5/services/studentDataService';
+import { ProjectLocale } from '../domain/projectLocale';
+import demoProjectJSON_import from './sampleData/curriculum/Demo.project.json';
+import { copy } from '../../assets/wise5/common/object/object';
+import { ConfigService } from '../../assets/wise5/services/configService';
+
+let http: HttpTestingController;
+let demoProjectJSON: any;
+let configService: ConfigService;
+let dataService: StudentDataService;
+let projectService: ProjectService;
+let service: StudentProjectTranslationService;
+describe('StudentProjectTranslationService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [HttpClientTestingModule, StudentTeacherCommonServicesModule],
+ providers: [
+ {
+ provide: StudentDataService,
+ useValue: jasmine.createSpyObj('StudentDataService', ['saveVLEEvent', 'getCurrentNodeId'])
+ }
+ ]
+ });
+ http = TestBed.inject(HttpTestingController);
+ demoProjectJSON = copy(demoProjectJSON_import);
+ configService = TestBed.inject(ConfigService);
+ dataService = TestBed.inject(StudentDataService);
+ projectService = TestBed.inject(ProjectService);
+ service = TestBed.inject(StudentProjectTranslationService);
+ spyOn(projectService, 'getOriginalProject').and.returnValue(demoProjectJSON);
+ });
+ describe('switchLanguage()', () => {
+ describe('has no translations to apply', () => {
+ beforeEach(() => {
+ spyOn(projectService, 'getLocale').and.returnValue(
+ new ProjectLocale({ default: 'en_US', supported: [] })
+ );
+ spyOn(projectService, 'setCurrentLanguage').and.stub();
+ spyOn(configService, 'isRunActive').and.returnValue(true);
+ });
+ it('should keep original project in tact', () => {
+ service.switchLanguage({ language: 'Japanese', locale: 'ja' }, 'student').then(() => {
+ expect(projectService.getProjectTitle()).toEqual('Demo Project');
+ });
+ });
+ });
+ describe('has translations to apply', () => {
+ beforeEach(() => {
+ spyOn(projectService, 'getLocale').and.returnValue(
+ new ProjectLocale({ default: 'en_US', supported: ['es'] })
+ );
+ spyOn(configService, 'getConfigParam').and.returnValue('/123/project.json');
+ });
+ it('should retrieve translation mapping file and translate project', () => {
+ service.switchLanguage({ language: 'Spanish', locale: 'es' }, 'student').then(() => {
+ expect(projectService.getProjectTitle()).toEqual('Proyecto de demostración');
+ http.expectOne('/123/translations.es.json').flush({
+ d66d3be571e16cf8d0166286a0a632ec: { value: 'Proyecto de demostración', modified: 456 }
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/src/app/services/studentStatusService.spec.ts b/src/app/services/studentStatusService.spec.ts
index 871e87d4171..21d8550b150 100644
--- a/src/app/services/studentStatusService.spec.ts
+++ b/src/app/services/studentStatusService.spec.ts
@@ -9,6 +9,8 @@ import { of } from 'rxjs';
import { MatDialogModule } from '@angular/material/dialog';
import { StudentTeacherCommonServicesModule } from '../student-teacher-common-services.module';
import { NodeProgressService } from '../../assets/wise5/services/nodeProgressService';
+import { ProjectService } from '../../assets/wise5/services/projectService';
+import { ProjectLocale } from '../domain/projectLocale';
let configService: ConfigService;
let http: HttpClient;
@@ -92,6 +94,9 @@ function saveStudentStatus_nodeStatusChanged_PostStudentStatus() {
spyOn(configService, 'getStudentStatusURL').and.returnValue(studentStatusUrl);
spyOn(studentDataService, 'getCurrentNodeId').and.returnValue(nodeId);
spyOn(nodeProgressService, 'getNodeProgress').and.returnValue(projectCompletion);
+ spyOn(TestBed.inject(ProjectService), 'getLocale').and.returnValue(
+ new ProjectLocale({ default: 'en_US', supported: [] })
+ );
const httpPostSpy = spyOn(http, 'post').and.callFake((url: string, body: any) => {
return of({} as any);
});
diff --git a/src/app/services/teacherProjectTranslationService.spec.ts b/src/app/services/teacherProjectTranslationService.spec.ts
new file mode 100644
index 00000000000..d7ddb632fa6
--- /dev/null
+++ b/src/app/services/teacherProjectTranslationService.spec.ts
@@ -0,0 +1,51 @@
+import { TestBed } from '@angular/core/testing';
+import { TeacherProjectTranslationService } from '../../assets/wise5/services/teacherProjectTranslationService';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { TeacherProjectService } from '../../assets/wise5/services/teacherProjectService';
+import { ConfigService } from '../../assets/wise5/services/configService';
+import { signal } from '@angular/core';
+import { ProjectService } from '../../assets/wise5/services/projectService';
+
+class ConfigServiceStub {
+ getProjectId() {
+ return 1;
+ }
+}
+
+class TeacherProjectServiceStub {
+ readonly currentLanguage = signal({
+ language: 'Spanish',
+ locale: 'es'
+ });
+ broadcastSavingProject(): void {}
+ broadcastProjectSaved(): void {}
+}
+
+let http: HttpTestingController;
+let projectService: TeacherProjectService;
+let service: TeacherProjectTranslationService;
+describe('TeacherProjectTranslationService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ TeacherProjectTranslationService,
+ HttpClientTestingModule,
+ { provide: ConfigService, useClass: ConfigServiceStub },
+ { provide: ProjectService, useClass: TeacherProjectServiceStub },
+ { provide: TeacherProjectService, useClass: TeacherProjectServiceStub }
+ ],
+ imports: [HttpClientTestingModule]
+ });
+ http = TestBed.inject(HttpTestingController);
+ projectService = TestBed.inject(TeacherProjectService);
+ service = TestBed.inject(TeacherProjectTranslationService);
+ });
+ describe('saveCurrentTranslations()', () => {
+ it('makes a POST request to backend', () => {
+ service.saveCurrentTranslations({}).subscribe();
+ const request = http.expectOne(`/api/author/project/translate/1/es`);
+ expect(request.request.method).toEqual('POST');
+ expect(request.request.body).toEqual({});
+ });
+ });
+});
diff --git a/src/app/student-teacher-common-services.module.ts b/src/app/student-teacher-common-services.module.ts
index 49de92ff787..bd5925dab02 100644
--- a/src/app/student-teacher-common-services.module.ts
+++ b/src/app/student-teacher-common-services.module.ts
@@ -53,6 +53,7 @@ import { PeerGroupService } from '../assets/wise5/services/peerGroupService';
import { NodeProgressService } from '../assets/wise5/services/nodeProgressService';
import { CompletionService } from '../assets/wise5/services/completionService';
import { StudentNodeService } from '../assets/wise5/services/studentNodeService';
+import { StudentProjectTranslationService } from '../assets/wise5/services/studentProjectTranslationService';
import { AiChatService } from '../assets/wise5/components/aiChat/aiChatService';
@NgModule({
@@ -110,6 +111,7 @@ import { AiChatService } from '../assets/wise5/components/aiChat/aiChatService';
TableService,
TabulatorDataService,
TagService,
+ StudentProjectTranslationService,
VLEProjectService,
WiseLinkService
]
diff --git a/src/app/student/top-bar/top-bar.component.html b/src/app/student/top-bar/top-bar.component.html
index bb822b04bc3..30ce917c820 100644
--- a/src/app/student/top-bar/top-bar.component.html
+++ b/src/app/student/top-bar/top-bar.component.html
@@ -25,24 +25,27 @@ {{ projectName }}
Constraints Are Off
-
-
-
+
+
+ notifications
- notifications
-
-
+