Merged in feature/VBV-704-eine-note-im-kn-für-ük (pull request #369)

VBV-704 Eine Note im KN für ÜK
This commit is contained in:
Elia Bieri 2024-08-07 16:17:29 +00:00
commit c84b25cc32
19 changed files with 817 additions and 240 deletions

404
client/package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@headlessui/tailwindcss": "^0.2.1", "@headlessui/tailwindcss": "^0.2.1",
"@headlessui/vue": "^1.7.22", "@headlessui/vue": "^1.7.22",
"@parcel/watcher": "^2.4.1",
"@sentry/tracing": "^7.114.0", "@sentry/tracing": "^7.114.0",
"@sentry/vue": "^8.17.0", "@sentry/vue": "^8.17.0",
"@urql/exchange-graphcache": "^7.1.2", "@urql/exchange-graphcache": "^7.1.2",
@ -3008,6 +3009,266 @@
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
"dev": true "dev": true
}, },
"node_modules/@parcel/watcher": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz",
"integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==",
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.4.1",
"@parcel/watcher-darwin-arm64": "2.4.1",
"@parcel/watcher-darwin-x64": "2.4.1",
"@parcel/watcher-freebsd-x64": "2.4.1",
"@parcel/watcher-linux-arm-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-musl": "2.4.1",
"@parcel/watcher-linux-x64-glibc": "2.4.1",
"@parcel/watcher-linux-x64-musl": "2.4.1",
"@parcel/watcher-win32-arm64": "2.4.1",
"@parcel/watcher-win32-ia32": "2.4.1",
"@parcel/watcher-win32-x64": "2.4.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz",
"integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz",
"integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz",
"integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz",
"integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz",
"integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz",
"integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz",
"integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz",
"integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz",
"integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz",
"integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz",
"integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz",
"integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@peculiar/asn1-schema": { "node_modules/@peculiar/asn1-schema": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz",
@ -5448,7 +5709,6 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
}, },
@ -6982,6 +7242,17 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/didyoumean": { "node_modules/didyoumean": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -7972,7 +8243,6 @@
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -9031,7 +9301,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -9048,7 +9317,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
}, },
@ -9102,7 +9370,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -10048,7 +10315,6 @@
"version": "4.0.5", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": { "dependencies": {
"braces": "^3.0.2", "braces": "^3.0.2",
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
@ -10212,6 +10478,11 @@
"tslib": "^2.0.3" "tslib": "^2.0.3"
} }
}, },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@ -10780,7 +11051,6 @@
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": { "engines": {
"node": ">=8.6" "node": ">=8.6"
}, },
@ -12380,7 +12650,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },
@ -16593,6 +16862,101 @@
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
"dev": true "dev": true
}, },
"@parcel/watcher": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz",
"integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==",
"requires": {
"@parcel/watcher-android-arm64": "2.4.1",
"@parcel/watcher-darwin-arm64": "2.4.1",
"@parcel/watcher-darwin-x64": "2.4.1",
"@parcel/watcher-freebsd-x64": "2.4.1",
"@parcel/watcher-linux-arm-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-musl": "2.4.1",
"@parcel/watcher-linux-x64-glibc": "2.4.1",
"@parcel/watcher-linux-x64-musl": "2.4.1",
"@parcel/watcher-win32-arm64": "2.4.1",
"@parcel/watcher-win32-ia32": "2.4.1",
"@parcel/watcher-win32-x64": "2.4.1",
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
}
},
"@parcel/watcher-android-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz",
"integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==",
"optional": true
},
"@parcel/watcher-darwin-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz",
"integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==",
"optional": true
},
"@parcel/watcher-darwin-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz",
"integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==",
"optional": true
},
"@parcel/watcher-freebsd-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz",
"integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==",
"optional": true
},
"@parcel/watcher-linux-arm-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz",
"integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==",
"optional": true
},
"@parcel/watcher-linux-arm64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz",
"integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==",
"optional": true
},
"@parcel/watcher-linux-arm64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz",
"integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==",
"optional": true
},
"@parcel/watcher-linux-x64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz",
"integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==",
"optional": true
},
"@parcel/watcher-linux-x64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz",
"integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==",
"optional": true
},
"@parcel/watcher-win32-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz",
"integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==",
"optional": true
},
"@parcel/watcher-win32-ia32": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz",
"integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==",
"optional": true
},
"@parcel/watcher-win32-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz",
"integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==",
"optional": true
},
"@peculiar/asn1-schema": { "@peculiar/asn1-schema": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz",
@ -18406,7 +18770,6 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": { "requires": {
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
} }
@ -19508,6 +19871,11 @@
"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
"dev": true "dev": true
}, },
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
},
"didyoumean": { "didyoumean": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -20261,7 +20629,6 @@
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": { "requires": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
} }
@ -21019,8 +21386,7 @@
"is-extglob": { "is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
"dev": true
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
@ -21031,7 +21397,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"requires": { "requires": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
} }
@ -21069,8 +21434,7 @@
"is-number": { "is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
"dev": true
}, },
"is-number-object": { "is-number-object": {
"version": "1.0.7", "version": "1.0.7",
@ -21768,7 +22132,6 @@
"version": "4.0.5", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": { "requires": {
"braces": "^3.0.2", "braces": "^3.0.2",
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
@ -21884,6 +22247,11 @@
"tslib": "^2.0.3" "tslib": "^2.0.3"
} }
}, },
"node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="
},
"node-fetch": { "node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@ -22315,8 +22683,7 @@
"picomatch": { "picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
"dev": true
}, },
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
@ -23412,7 +23779,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": { "requires": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
} }

View File

@ -20,6 +20,7 @@
"dependencies": { "dependencies": {
"@headlessui/tailwindcss": "^0.2.1", "@headlessui/tailwindcss": "^0.2.1",
"@headlessui/vue": "^1.7.22", "@headlessui/vue": "^1.7.22",
"@parcel/watcher": "^2.4.1",
"@sentry/tracing": "^7.114.0", "@sentry/tracing": "^7.114.0",
"@sentry/vue": "^8.17.0", "@sentry/vue": "^8.17.0",
"@urql/exchange-graphcache": "^7.1.2", "@urql/exchange-graphcache": "^7.1.2",

View File

@ -337,7 +337,7 @@ const mentorTabTitle = computed(() =>
v-if="selectedCourseSessionTitle" v-if="selectedCourseSessionTitle"
class="nav-item hidden items-center lg:inline-flex" class="nav-item hidden items-center lg:inline-flex"
> >
<div class=""> <div class="" data-cy="current-course-session-title">
{{ selectedCourseSessionTitle }} {{ selectedCourseSessionTitle }}
</div> </div>
</div> </div>

View File

@ -32,6 +32,7 @@ import { useUserStore } from "@/stores/user";
import type { import type {
ActionCompetence, ActionCompetence,
CircleType, CircleType,
CompetenceCertificate,
Course, Course,
CourseCompletion, CourseCompletion,
CourseCompletionStatus, CourseCompletionStatus,
@ -52,6 +53,8 @@ import log from "loglevel";
import type { ComputedRef, Ref } from "vue"; import type { ComputedRef, Ref } from "vue";
import { computed, onMounted, ref, watchEffect } from "vue"; import { computed, onMounted, ref, watchEffect } from "vue";
import { useRouter, type RouteLocationRaw } from "vue-router"; import { useRouter, type RouteLocationRaw } from "vue-router";
import { getCertificates } from "./services/competence";
import { mergeCompetenceCertificates } from "./pages/competence/utils";
export function useCurrentCourseSession() { export function useCurrentCourseSession() {
/** /**
@ -693,15 +696,18 @@ export function useCourseStatisticsv2(courseSlug: string) {
}; };
} }
export function useCertificateQuery(userId: string | undefined, courseSlug: string) { export function useCertificateQuery(
userId: string | undefined,
courseSlug: string,
courseSession: CourseSession
) {
const certificatesQuery = (() => { const certificatesQuery = (() => {
const courseSession = useCurrentCourseSession();
if (userId) { if (userId) {
return useQuery({ return useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY, query: COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY,
variables: { variables: {
courseSlug: courseSlug, courseSlug: courseSlug,
courseSessionId: courseSession.value.id, courseSessionId: courseSession.id,
userId: userId, userId: userId,
}, },
}); });
@ -710,7 +716,7 @@ export function useCertificateQuery(userId: string | undefined, courseSlug: stri
query: COMPETENCE_NAVI_CERTIFICATE_QUERY, query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
variables: { variables: {
courseSlug: courseSlug, courseSlug: courseSlug,
courseSessionId: courseSession.value.id, courseSessionId: courseSession.id,
}, },
}); });
} }
@ -743,3 +749,30 @@ export function useVVByLink() {
return { href }; return { href };
} }
export function useAllCompetenceCertificates(
userId: string | undefined,
courseSlug: string
) {
const courseSessionsStore = useCourseSessionsStore();
const certificateQueries = courseSessionsStore.allCourseSessions.map(
(courseSession) => {
return useCertificateQuery(userId, courseSlug, courseSession).certificatesQuery;
}
);
const competenceCertificatesPerCs = computed(() =>
certificateQueries.map((query) => {
return getCertificates(query.data.value, userId ?? null)
?.competence_certificates as unknown as CompetenceCertificate[];
})
);
const isLoaded = computed(() => !certificateQueries.some((q) => q.fetching.value));
const competenceCertificates = computed(() =>
mergeCompetenceCertificates(competenceCertificatesPerCs.value.flat())
);
return {
competenceCertificates,
isLoaded,
};
}

View File

@ -18,8 +18,8 @@ const documents = {
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc, "\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument, "\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n solution_sample {\n id\n url\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n first_name\n last_name\n }\n assignment_user {\n avatar_url\n first_name\n last_name\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_points_deducted\n evaluation_points_deducted_reason\n evaluation_points_final\n\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument, "\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n solution_sample {\n id\n url\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n first_name\n last_name\n }\n assignment_user {\n avatar_url\n first_name\n last_name\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_points_deducted\n evaluation_points_deducted_reason\n evaluation_points_final\n\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument, "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
"\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateForUserQueryDocument, "\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateForUserQueryDocument,
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n optional_attendance\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument, "\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n optional_attendance\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
"\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument, "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument,
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n": types.DashboardConfigDocument, "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n": types.DashboardConfigDocument,
@ -67,11 +67,11 @@ export function graphql(source: "\n query assignmentCompletionQuery(\n $assi
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"]; export function graphql(source: "\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function graphql(source: "\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"]; export function graphql(source: "\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\n evaluation_max_points\n evaluation_passed\n course_session {\n id\n title\n }\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n"];
/** /**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

File diff suppressed because one or more lines are too long

View File

@ -109,6 +109,10 @@ export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
evaluation_points_final evaluation_points_final
evaluation_max_points evaluation_max_points
evaluation_passed evaluation_passed
course_session {
id
title
}
} }
learning_content { learning_content {
...CoursePageFields ...CoursePageFields
@ -148,6 +152,10 @@ export const COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY = graphql(`
evaluation_points_deducted evaluation_points_deducted
evaluation_max_points evaluation_max_points
evaluation_passed evaluation_passed
course_session {
id
title
}
} }
learning_content { learning_content {
...CoursePageFields ...CoursePageFields

View File

@ -1,11 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { useCurrentCourseSession } from "@/composables";
import router from "@/router";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import type { CompetenceCertificateAssignment } from "@/types"; import type { CompetenceCertificateAssignment } from "@/types";
import * as log from "loglevel"; import * as log from "loglevel";
log.debug("CompetenceAssignmentRow setup"); log.debug("CompetenceAssignmentRow setup");
const currentCourseSession = useCurrentCourseSession();
const { switchCourseSessionById } = useCourseSessionsStore();
export interface Props { export interface Props {
assignment: CompetenceCertificateAssignment; assignment: CompetenceCertificateAssignment;
showCourseSession: boolean;
addBorderBottom?: boolean; addBorderBottom?: boolean;
} }
@ -19,6 +26,13 @@ const getIconName = () => {
} }
return "it-icon-assignment-large"; return "it-icon-assignment-large";
}; };
const openInCircle = (assignment: CompetenceCertificateAssignment) => {
if (assignment.completion?.course_session !== currentCourseSession.value) {
switchCourseSessionById(assignment.completion!.course_session.id);
}
router.push(assignment.frontend_url);
};
</script> </script>
<template> <template>
@ -30,19 +44,26 @@ const getIconName = () => {
<component :is="getIconName()" class="mr-4 hidden h-9 w-9 lg:block"></component> <component :is="getIconName()" class="mr-4 hidden h-9 w-9 lg:block"></component>
<div class="flex flex-col lg:w-[420px]"> <div class="flex flex-col lg:w-[420px]">
<h3 class="text-bold flex items-center gap-2">{{ assignment.title }}</h3> <h3 class="text-bold flex items-center gap-2">{{ assignment.title }}</h3>
<p
v-if="showCourseSession"
:data-cy="`assignment-${assignment.slug}-course-session`"
>
{{ assignment?.completion?.course_session.title }}
</p>
<p class="text-gray-800"> <p class="text-gray-800">
<a <button
v-if="assignment.learning_content" v-if="assignment.learning_content"
:href="assignment.frontend_url" :href="assignment.frontend_url"
class="link" class="link"
data-cy="open-learning-content" data-cy="open-learning-content"
@click="() => openInCircle(assignment)"
> >
{{ {{
$t("general.im circle x anschauen", { $t("general.im circle x anschauen", {
x: assignment.learning_content.circle.title, x: assignment.learning_content.circle.title,
}) })
}} }}
</a> </button>
<span v-else>Fehler, Lerninhalt nicht korrekt verknüpft</span> <span v-else>Fehler, Lerninhalt nicht korrekt verknüpft</span>
</p> </p>
</div> </div>

View File

@ -9,6 +9,7 @@ import {
calcCompetenceCertificateGrade, calcCompetenceCertificateGrade,
competenceCertificateProgressStatusCount, competenceCertificateProgressStatusCount,
} from "@/pages/competence/utils"; } from "@/pages/competence/utils";
import { useCurrentCourseSession } from "@/composables";
log.debug("CompetenceCertificateComponent setup"); log.debug("CompetenceCertificateComponent setup");
@ -51,6 +52,15 @@ const frontendUrl = computed(() => {
? props.frontendUrl ? props.frontendUrl
: props.competenceCertificate.frontend_url; : props.competenceCertificate.frontend_url;
}); });
const showCourseSession = computed(() => {
const currentCourseSession = useCurrentCourseSession();
return props.competenceCertificate.assignments.some((assignment) => {
return (
assignment.completion?.course_session.title !== currentCourseSession.value.title
);
});
});
</script> </script>
<template> <template>
@ -134,6 +144,7 @@ const frontendUrl = computed(() => {
<CompetenceAssignmentRow <CompetenceAssignmentRow
:assignment="assignment" :assignment="assignment"
:add-border-bottom="index < competenceCertificate.assignments.length - 1" :add-border-bottom="index < competenceCertificate.assignments.length - 1"
:show-course-session="showCourseSession"
></CompetenceAssignmentRow> ></CompetenceAssignmentRow>
</div> </div>
</div> </div>

View File

@ -1,12 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import log from "loglevel"; import log from "loglevel";
import { computed } from "vue"; import { computed } from "vue";
import type { CompetenceCertificate } from "@/types"; import { useAllCompetenceCertificates } from "@/composables";
import { useCertificateQuery } from "@/composables";
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue"; import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
import { getCertificates } from "@/services/competence";
import { getPreviousRoute } from "@/router/history"; import { getPreviousRoute } from "@/router/history";
const props = defineProps<{ const props = defineProps<{
courseSlug: string; courseSlug: string;
certificateSlug: string; certificateSlug: string;
@ -15,24 +12,19 @@ const props = defineProps<{
log.debug("CompetenceCertificateDetailPage setup", props); log.debug("CompetenceCertificateDetailPage setup", props);
const certificatesQuery = useCertificateQuery( const { competenceCertificates } = useAllCompetenceCertificates(
props.userId, props.userId,
props.courseSlug props.courseSlug
).certificatesQuery; );
const certificate = computed(() => { const certificate = computed(() => {
const certificates = getCertificates( if (!competenceCertificates) {
certificatesQuery.data.value,
props.userId ?? null
);
if (!certificates) {
return null; return null;
} }
return ( return competenceCertificates.value.find((cc) =>
(certificates.competence_certificates as unknown as CompetenceCertificate[]) ?? [] cc.slug.endsWith(props.certificateSlug)
).find((cc) => cc.slug.endsWith(props.certificateSlug)); );
}); });
</script> </script>

View File

@ -1,16 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import log from "loglevel"; import log from "loglevel";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
import type { CompetenceCertificate } from "@/types"; import { useAllCompetenceCertificates } from "@/composables";
import { useCertificateQuery } from "@/composables";
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue"; import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
import { import {
assignmentsUserPoints, assignmentsUserPoints,
calcCompetencesTotalGrade, calcCompetencesTotalGrade,
} from "@/pages/competence/utils"; } from "@/pages/competence/utils";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { getCertificates } from "@/services/competence";
const props = defineProps<{ const props = defineProps<{
courseSlug: string; courseSlug: string;
userId?: string; userId?: string;
@ -20,28 +17,13 @@ log.debug("CompetenceCertificateListPage setup", props);
const route = useRoute(); const route = useRoute();
const certificatesQuery = useCertificateQuery( const { competenceCertificates } = useAllCompetenceCertificates(
props.userId, props.userId,
props.courseSlug props.courseSlug
).certificatesQuery; );
const competenceCertificates = computed(() => {
const certificates = getCertificates(
certificatesQuery.data.value,
props.userId ?? null
);
if (!certificates) {
return null;
}
return (
(certificates?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
);
});
const assignments = computed(() => { const assignments = computed(() => {
return competenceCertificates?.value?.flatMap((cc) => cc.assignments); return competenceCertificates?.value.flatMap((cc) => cc.assignments);
}); });
const totalGrade = computed(() => { const totalGrade = computed(() => {

View File

@ -1,10 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import log from "loglevel"; import log from "loglevel";
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
import { useQuery } from "@urql/vue";
import { computed } from "vue"; import { computed } from "vue";
import type { CompetenceCertificate } from "@/types"; import { useAllCompetenceCertificates, useCurrentCourseSession } from "@/composables";
import { useCurrentCourseSession } from "@/composables";
import { import {
assignmentsUserPoints, assignmentsUserPoints,
calcCompetenceCertificateGrade, calcCompetenceCertificateGrade,
@ -22,22 +19,10 @@ const props = defineProps<{
log.debug("CompetenceIndexPage setup", props); log.debug("CompetenceIndexPage setup", props);
const courseSession = useCurrentCourseSession(); const { competenceCertificates, isLoaded } = useAllCompetenceCertificates(
undefined,
const certificatesQuery = useQuery({ props.courseSlug
query: COMPETENCE_NAVI_CERTIFICATE_QUERY, );
variables: {
courseSlug: props.courseSlug,
courseSessionId: courseSession.value.id,
},
});
const competenceCertificates = computed(() => {
return (
(certificatesQuery.data.value?.competence_certificate_list
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
);
});
const allAssignments = computed(() => { const allAssignments = computed(() => {
return competenceCertificates.value.flatMap((cc) => cc.assignments); return competenceCertificates.value.flatMap((cc) => cc.assignments);
@ -49,8 +34,6 @@ const userPointsEvaluatedAssignments = computed(() => {
const currentCourseSession = useCurrentCourseSession(); const currentCourseSession = useCurrentCourseSession();
const isLoaded = computed(() => !certificatesQuery.fetching.value);
const router = useRouter(); const router = useRouter();
</script> </script>

View File

@ -1,6 +1,7 @@
import type { StatusCount } from "@/components/ui/ItProgress.vue"; import type { StatusCount } from "@/components/ui/ItProgress.vue";
import { percentToRoundedGrade } from "@/services/assignmentService"; import { percentToRoundedGrade } from "@/services/assignmentService";
import type { CompetenceCertificate, CompetenceCertificateAssignment } from "@/types"; import type { CompetenceCertificate, CompetenceCertificateAssignment } from "@/types";
import dayjs from "dayjs";
import _ from "lodash"; import _ from "lodash";
export function assignmentsMaxEvaluationPoints( export function assignmentsMaxEvaluationPoints(
@ -84,3 +85,55 @@ export function competenceCertificateProgressStatusCount(
FAIL: 0, FAIL: 0,
} as StatusCount; } as StatusCount;
} }
export function mergeCompetenceCertificates(
competenceCertificates: CompetenceCertificate[]
) {
const groupedCompetenceCertificates: Record<
string,
Array<CompetenceCertificate>
> = {};
competenceCertificates.forEach((certificate) => {
if (!certificate) {
return;
}
if (!groupedCompetenceCertificates[certificate.id]) {
groupedCompetenceCertificates[certificate.id] = [];
}
groupedCompetenceCertificates[certificate.id].push(certificate);
});
console.log(
`Found ${Object.keys(groupedCompetenceCertificates).length} competence certificates over all course sessions`
);
return Object.values(groupedCompetenceCertificates).map((certificates) => {
const mergedCertificate: CompetenceCertificate = {
...certificates[0],
assignments: [],
};
certificates.forEach((certificate) => {
certificate.assignments.forEach((assignment) => {
const existingAssignment = mergedCertificate.assignments.find(
(a) => a.id === assignment.id
);
if (!existingAssignment) {
mergedCertificate.assignments.push(assignment);
} else if (
assignment.completion != null &&
(existingAssignment.completion == null ||
dayjs(existingAssignment.completion.evaluation_submitted_at).isBefore(
assignment.completion.evaluation_submitted_at
))
) {
mergedCertificate.assignments.splice(
mergedCertificate.assignments.findIndex((a) => a.id === assignment.id),
1
);
mergedCertificate.assignments.push(assignment);
}
});
});
return mergedCertificate;
});
}

View File

@ -408,6 +408,10 @@ export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
evaluation_points_reason: string; evaluation_points_reason: string;
evaluation_max_points: number | null; evaluation_max_points: number | null;
evaluation_passed: boolean | null; evaluation_passed: boolean | null;
course_session: {
id: string;
title: string;
};
} | null; } | null;
} }

View File

@ -1,26 +1,26 @@
import { login } from "../helpers"; import { login } from "../helpers"
describe("competenceCertificate.cy.js", () => { describe("competenceCertificate.cy.js", () => {
beforeEach(() => {}); beforeEach(() => { })
it("check without points", () => { it("check without points", () => {
cy.manageCommand("cypress_reset"); cy.manageCommand("cypress_reset")
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
cy.visit("/course/test-lehrgang/competence"); cy.visit("/course/test-lehrgang/competence")
cy.get('[data-cy="certificate-total-points-text"]').contains( cy.get('[data-cy="certificate-total-points-text"]').contains(
"Der Punktestand wird zu einem späteren Zeitpunkt berechnet." "Der Punktestand wird zu einem späteren Zeitpunkt berechnet."
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
).and("contain", "0 von 2 Kompetenznachweis-Elementen"); ).and("contain", "0 von 2 Kompetenznachweis-Elementen")
// check on certificates page // check on certificates page
cy.get('[data-cy="certificates-show-all-button"]').click(); cy.get('[data-cy="certificates-show-all-button"]').click()
cy.get('[data-cy="certificate-total-points-text"]').contains( cy.get('[data-cy="certificate-total-points-text"]').contains(
"Der Punktestand wird zu einem späteren Zeitpunkt berechnet." "Der Punktestand wird zu einem späteren Zeitpunkt berechnet."
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
@ -29,100 +29,100 @@ describe("competenceCertificate.cy.js", () => {
"contain", "contain",
"Der Punktestand wird zu einem späteren Zeitpunkt berechnet." "Der Punktestand wird zu einem späteren Zeitpunkt berechnet."
) )
.and("contain", "0 von 2 Kompetenznachweis-Elementen"); .and("contain", "0 von 2 Kompetenznachweis-Elementen")
// check certificate detail page // check certificate detail page
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
).click(); ).click()
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]' '[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
).should("contain", "Höchstpunktzahl"); ).should("contain", "Höchstpunktzahl")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
).should("contain", "Höchstpunktzahl"); ).should("contain", "Höchstpunktzahl")
}); })
it("check with finished passed edoniq test", () => { it("check with finished passed edoniq test", () => {
cy.manageCommand( cy.manageCommand(
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 0" "cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 0"
); )
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
cy.visit("/course/test-lehrgang/competence"); cy.visit("/course/test-lehrgang/competence")
cy.get('[data-cy="certificate-total-points-text"]').contains( cy.get('[data-cy="certificate-total-points-text"]').contains(
"Erfahrungsnote üK: 5" "Erfahrungsnote üK: 5"
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.should("contain", "Note: 5") .should("contain", "Note: 5")
.and("contain", "1 von 2 Kompetenznachweis-Elementen"); .and("contain", "1 von 2 Kompetenznachweis-Elementen")
// check on certificates page // check on certificates page
cy.get('[data-cy="certificates-show-all-button"]').click(); cy.get('[data-cy="certificates-show-all-button"]').click()
cy.get('[data-cy="certificate-total-points-text"]') cy.get('[data-cy="certificate-total-points-text"]')
.should("contain", "Erfahrungsnote üK") .should("contain", "Erfahrungsnote üK")
.and("contain", "Zwischenstand"); .and("contain", "Zwischenstand")
cy.get('[data-cy="certificate-total-grade"]').should("contain", "Note: 5"); cy.get('[data-cy="certificate-total-grade"]').should("contain", "Note: 5")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 5"); ).should("contain", "Note: 5")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 4.96"); ).should("contain", "Ungerundete Note: 4.96")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.and("contain", "Zwischenstand") .and("contain", "Zwischenstand")
.and("contain", "1 von 2 Kompetenznachweis-Elementen"); .and("contain", "1 von 2 Kompetenznachweis-Elementen")
// check certificate detail page // check certificate detail page
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
).click(); ).click()
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]' '[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
) )
.should("contain", "Höchstpunktzahl") .should("contain", "Höchstpunktzahl")
.and("contain", "Ergebnisse abgegeben"); .and("contain", "Ergebnisse abgegeben")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
) )
.should("contain", "19") .should("contain", "19")
.and("contain", "Bewertung freigegeben") .and("contain", "Bewertung freigegeben")
.and("not.contain", "Nicht Bestanden"); .and("not.contain", "Nicht Bestanden")
// it can open learning content page directly // it can open learning content page directly
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click(); ).click()
cy.get('[data-cy="test-result"]') cy.get('[data-cy="test-result"]')
.should("contain", "19 von 24 Punkten") .should("contain", "19 von 24 Punkten")
.and("contain", "79%"); .and("contain", "79%")
}); })
it("check with finished failed edoniq test", () => { it("check with finished failed edoniq test", () => {
cy.manageCommand( cy.manageCommand(
"cypress_reset --create-assignment-completion --create-edoniq-test-results 10 24 0" "cypress_reset --create-assignment-completion --create-edoniq-test-results 10 24 0"
); )
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
// go to certificate detail page // go to certificate detail page
cy.visit( cy.visit(
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1" "/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 3"); ).should("contain", "Note: 3")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 3.08"); ).should("contain", "Ungerundete Note: 3.08")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
@ -130,90 +130,170 @@ describe("competenceCertificate.cy.js", () => {
.should("contain", "10") .should("contain", "10")
.and("contain", "Bewertung freigegeben") .and("contain", "Bewertung freigegeben")
.and("contain", "42%") .and("contain", "42%")
.and("contain", "Nicht bestanden"); .and("contain", "Nicht bestanden")
// it can open learning content page directly // it can open learning content page directly
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click(); ).click()
cy.get('[data-cy="test-result"]') cy.get('[data-cy="test-result"]')
.should("contain", "10 von 24 Punkten") .should("contain", "10 von 24 Punkten")
.and("contain", "42%") .and("contain", "42%")
.and("contain", "Nicht bestanden"); .and("contain", "Nicht bestanden")
}); })
it("check with finished edoniq test and finished casework", () => { it("check with finished edoniq test and finished casework", () => {
cy.manageCommand( cy.manageCommand(
"cypress_reset --create-assignment-evaluation --create-edoniq-test-results 19 24 0" "cypress_reset --create-assignment-evaluation --create-edoniq-test-results 19 24 0"
); )
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
cy.visit("/course/test-lehrgang/competence"); cy.visit("/course/test-lehrgang/competence")
cy.get('[data-cy="certificate-total-points-text"]').contains( cy.get('[data-cy="certificate-total-points-text"]').contains(
"Erfahrungsnote üK: 5.5" "Erfahrungsnote üK: 5.5"
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.should("contain", "Note: 5.5") .should("contain", "Note: 5.5")
.and("contain", "2 von 2 Kompetenznachweis-Elementen"); .and("contain", "2 von 2 Kompetenznachweis-Elementen")
// check on certificates page // check on certificates page
cy.get('[data-cy="certificates-show-all-button"]').click(); cy.get('[data-cy="certificates-show-all-button"]').click()
cy.get('[data-cy="certificate-total-points-text"]') cy.get('[data-cy="certificate-total-points-text"]')
.should("contain", "Erfahrungsnote üK") .should("contain", "Erfahrungsnote üK")
.and("contain", "Note: 5.5") .and("contain", "Note: 5.5")
.and("not.contain", "Zwischenstand"); .and("not.contain", "Zwischenstand")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.and("not.contain", "Zwischenstand") .and("not.contain", "Zwischenstand")
.and("contain", "2 von 2 Kompetenznachweis-Elementen"); .and("contain", "2 von 2 Kompetenznachweis-Elementen")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 5.5"); ).should("contain", "Note: 5.5")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 5.48"); ).should("contain", "Ungerundete Note: 5.48")
// check certificate detail page // check certificate detail page
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
).click(); ).click()
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]' '[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
) )
.should("contain", "24") .should("contain", "24")
.and("contain", "von 24 Punkten") .and("contain", "von 24 Punkten")
.and("contain", "Bewertung freigegeben"); .and("contain", "Bewertung freigegeben")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
) )
.should("contain", "19") .should("contain", "19")
.and("contain", "von 24 Punkten") .and("contain", "von 24 Punkten")
.and("contain", "Bewertung freigegeben"); .and("contain", "Bewertung freigegeben")
}); })
it("check with finished edoniq test and finished casework in different course sessions", () => {
const TEST_TRAINER2_USER_ID = "299941ae-1e4b-4f45-8180-876c3ad340b4"
const TEST_STUDENT2_USER_ID = "19c40d94-15cc-4198-aaad-ef707c4b0900"
const TEST_COURSE_SESSION_ZURICH_ID = -2
cy.manageCommand(
`cypress_reset --create-assignment-evaluation --assignment-evaluation-user-id ${TEST_TRAINER2_USER_ID} --assignment-completion-user-id ${TEST_STUDENT2_USER_ID} --edoniq-user-id ${TEST_STUDENT2_USER_ID} --edoniq-course-session-id '${TEST_COURSE_SESSION_ZURICH_ID}' --create-edoniq-test-results 19 24 0`
)
login("test-student2@example.com", "test")
cy.visit("/course/test-lehrgang/competence")
cy.get('[data-cy="certificate-total-points-text"]').contains(
"Erfahrungsnote üK: 5.5"
)
cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
)
.should("contain", "Note: 5.5")
.and("contain", "2 von 2 Kompetenznachweis-Elementen")
// check on certificates page
cy.get('[data-cy="certificates-show-all-button"]').click()
cy.get('[data-cy="certificate-total-points-text"]')
.should("contain", "Erfahrungsnote üK")
.and("contain", "Note: 5.5")
.and("not.contain", "Zwischenstand")
cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
)
.and("not.contain", "Zwischenstand")
.and("contain", "2 von 2 Kompetenznachweis-Elementen")
cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 5.5")
cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 5.48")
// check certificate detail page
cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
).click()
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
)
.should("contain", "24")
.and("contain", "von 24 Punkten")
.and("contain", "Bewertung freigegeben")
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
)
.should("contain", "19")
.and("contain", "von 24 Punkten")
.and("contain", "Bewertung freigegeben")
cy.get('[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice-course-session"]').should("contain", "Test Bern 2022 a")
cy.get('[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo-course-session"]').should("contain", "Test Zürich 2022 a")
})
it("check show assignment in different course session", () => {
const TEST_TRAINER2_USER_ID = "299941ae-1e4b-4f45-8180-876c3ad340b4"
const TEST_STUDENT2_USER_ID = "19c40d94-15cc-4198-aaad-ef707c4b0900"
const TEST_COURSE_SESSION_ZURICH_ID = -2
cy.manageCommand(
`cypress_reset --create-assignment-evaluation --assignment-evaluation-user-id ${TEST_TRAINER2_USER_ID} --assignment-completion-user-id ${TEST_STUDENT2_USER_ID} --edoniq-user-id ${TEST_STUDENT2_USER_ID} --edoniq-course-session-id '${TEST_COURSE_SESSION_ZURICH_ID}' --create-edoniq-test-results 19 24 0`
)
login("test-student2@example.com", "test")
cy.visit("course/test-lehrgang/competence/certificates/kompetenznachweis-1")
cy.get('[data-cy="current-course-session-title"]').should("contain", "Test Bern 2022 a")
cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click()
cy.get('[data-cy="current-course-session-title"]').should("contain", "Test Zürich 2022 a")
})
it("check with finished edoniq test with deducted points", () => { it("check with finished edoniq test with deducted points", () => {
cy.manageCommand( cy.manageCommand(
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 8" "cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 8"
); )
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
// go to certificate detail page // go to certificate detail page
cy.visit( cy.visit(
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1" "/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 3.5"); ).should("contain", "Note: 3.5")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 3.29"); ).should("contain", "Ungerundete Note: 3.29")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
@ -222,61 +302,61 @@ describe("competenceCertificate.cy.js", () => {
.and("contain", "Bewertung freigegeben") .and("contain", "Bewertung freigegeben")
.and("contain", "46%") .and("contain", "46%")
.and("contain", "mit Abzug") .and("contain", "mit Abzug")
.and("contain", "Nicht bestanden"); .and("contain", "Nicht bestanden")
// it can open learning content page directly // it can open learning content page directly
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]' '[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
).click(); ).click()
cy.get('[data-cy="test-result"]') cy.get('[data-cy="test-result"]')
.should("contain", "11 von 24 Punkten") .should("contain", "11 von 24 Punkten")
.and("contain", "46%") .and("contain", "46%")
.and("contain", "Punkte aus Bewertung: 19") .and("contain", "Punkte aus Bewertung: 19")
.and("contain", "Abgezogene Punkte: 8") .and("contain", "Abgezogene Punkte: 8")
.and("contain", "Grund: Edoniq Punkteabzug Test") .and("contain", "Grund: Edoniq Punkteabzug Test")
.and("contain", "Nicht bestanden"); .and("contain", "Nicht bestanden")
}); })
it("check with finished casework and points deducted", () => { it("check with finished casework and points deducted", () => {
cy.manageCommand( cy.manageCommand(
"cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 4,6,4,3,2 --assignment-points-deducted 5" "cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 4,6,4,3,2 --assignment-points-deducted 5"
); )
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
cy.visit("/course/test-lehrgang/competence"); cy.visit("/course/test-lehrgang/competence")
cy.get('[data-cy="certificate-total-points-text"]').contains( cy.get('[data-cy="certificate-total-points-text"]').contains(
"Erfahrungsnote üK: 4" "Erfahrungsnote üK: 4"
); )
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.should("contain", "Note: 4") .should("contain", "Note: 4")
.and("contain", "1 von 2 Kompetenznachweis-Elementen"); .and("contain", "1 von 2 Kompetenznachweis-Elementen")
// check on certificates page // check on certificates page
cy.get('[data-cy="certificates-show-all-button"]').click(); cy.get('[data-cy="certificates-show-all-button"]').click()
cy.get('[data-cy="certificate-total-points-text"]') cy.get('[data-cy="certificate-total-points-text"]')
.should("contain", "Erfahrungsnote üK") .should("contain", "Erfahrungsnote üK")
.and("contain", "Note: 4") .and("contain", "Note: 4")
.and("contain", "Zwischenstand"); .and("contain", "Zwischenstand")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
) )
.and("contain", "Zwischenstand") .and("contain", "Zwischenstand")
.and("contain", "1 von 2 Kompetenznachweis-Elementen"); .and("contain", "1 von 2 Kompetenznachweis-Elementen")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
).should("contain", "Note: 4"); ).should("contain", "Note: 4")
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
).should("contain", "Ungerundete Note: 3.92"); ).should("contain", "Ungerundete Note: 3.92")
// check certificate detail page // check certificate detail page
cy.get( cy.get(
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]' '[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
).click(); ).click()
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]' '[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
@ -285,28 +365,28 @@ describe("competenceCertificate.cy.js", () => {
.and("contain", "von 24 Punkten") .and("contain", "von 24 Punkten")
.and("contain", "58%") .and("contain", "58%")
.and("contain", "mit Abzug") .and("contain", "mit Abzug")
.and("contain", "Bewertung freigegeben"); .and("contain", "Bewertung freigegeben")
cy.get( cy.get(
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"] [data-cy="open-learning-content"]' '[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"] [data-cy="open-learning-content"]'
).click(); ).click()
cy.get('[data-cy="user-points"]').should("contain", "14"); cy.get('[data-cy="user-points"]').should("contain", "14")
cy.get('[data-cy="total-points"]').should( cy.get('[data-cy="total-points"]').should(
"contain", "contain",
"von 24 Punkten (58%)" "von 24 Punkten (58%)"
); )
cy.get('[data-cy="points-deducted"]') cy.get('[data-cy="points-deducted"]')
.should("contain", "Punkte aus Bewertung: 19") .should("contain", "Punkte aus Bewertung: 19")
.and("contain", "Abgezogene Punkte: 5") .and("contain", "Abgezogene Punkte: 5")
.and("contain", "Grund: Assignment Punkteabzug Test"); .and("contain", "Grund: Assignment Punkteabzug Test")
}); })
it("should display link to details", () => { it("should display link to details", () => {
cy.manageCommand("cypress_reset"); cy.manageCommand("cypress_reset")
login("test-student1@example.com", "test"); login("test-student1@example.com", "test")
cy.visit("/course/test-lehrgang/competence/self-evaluation-and-feedback"); cy.visit("/course/test-lehrgang/competence/self-evaluation-and-feedback")
cy.get('[data-cy^="self-eval-"][data-cy$="-detail-url"]:first').contains( cy.get('[data-cy^="self-eval-"][data-cy$="-detail-url"]:first').contains(
"Selbsteinschätzung anschauen" "Selbsteinschätzung anschauen"
); )
}); })
}); })

View File

@ -50,20 +50,20 @@
// -- This is will overwrite an existing command -- // -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
const _ = Cypress._; const _ = Cypress._
Cypress.Commands.add("manageCommand", (command, preCommand = "") => { Cypress.Commands.add("manageCommand", (command, preCommand = "") => {
const execCommand = `${preCommand} python server/manage.py ${command} --settings=config.settings.test_cypress`; const execCommand = `${preCommand} python server/manage.py ${command} --settings=config.settings.test_cypress`
console.log(execCommand); console.log(execCommand)
// hack to add my asdf python instance to the path // hack to add my asdf python instance to the path
// so I can run the test directly from within IntelliJ // so I can run the test directly from within IntelliJ
let pythonPaths = [ let pythonPaths = [
"/Users/daniel/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin", "/Users/daniel/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin",
"/Users/eliabieri/iterativ/vbv_lernwelt/.direnv/python-3.10.6/bin", "/Users/eliabieri/iterativ/vbv_lernwelt/.direnv/python-3.10/bin",
"/Users/christiancueni/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin", "/Users/christiancueni/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin",
"/Users/renzo/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin", "/Users/renzo/workspace/vbv_lernwelt/.direnv/python-3.10.6/bin",
]; ]
let bashCommand = `PATH=${pythonPaths.join(":")}:$PATH && ${execCommand}`; let bashCommand = `PATH=${pythonPaths.join(":")}:$PATH && ${execCommand}`
return cy return cy
.exec(`bash -c "${bashCommand}"`, { .exec(`bash -c "${bashCommand}"`, {
failOnNonZeroExit: true, failOnNonZeroExit: true,
@ -73,14 +73,14 @@ Cypress.Commands.add("manageCommand", (command, preCommand = "") => {
throw new Error(`Execution of "${command}" failed throw new Error(`Execution of "${command}" failed
Exit code: ${result.code} Exit code: ${result.code}
Stdout:\n${result.stdout} Stdout:\n${result.stdout}
Stderr:\n${result.stderr}`); Stderr:\n${result.stderr}`)
} }
}); })
}); })
Cypress.Commands.add("manageShellCommand", (command) => { Cypress.Commands.add("manageShellCommand", (command) => {
return cy.manageCommand(`shell -c '${command}'`); return cy.manageCommand(`shell -c '${command}'`)
}); })
function loadObjectJson( function loadObjectJson(
key, key,
@ -89,28 +89,28 @@ function loadObjectJson(
serializerModelPath, serializerModelPath,
valueAsString = false valueAsString = false
) { ) {
const djangoModel = _.last(djangoModelPath.split(".")); const djangoModel = _.last(djangoModelPath.split("."))
const djangoModelImportPath = _.initial(djangoModelPath.split(".")).join("."); const djangoModelImportPath = _.initial(djangoModelPath.split(".")).join(".")
const serializerModel = _.last(serializerModelPath.split(".")); const serializerModel = _.last(serializerModelPath.split("."))
const serializerModelImportPath = _.initial( const serializerModelImportPath = _.initial(
serializerModelPath.split(".") serializerModelPath.split(".")
).join("."); ).join(".")
let filterPart = `${key}=${value}`; let filterPart = `${key}=${value}`
if (valueAsString) { if (valueAsString) {
filterPart = `${key}=\\"${value}\\"`; filterPart = `${key}=\\"${value}\\"`
} }
if (_.isArray(key)) { if (_.isArray(key)) {
filterPart = _.zip(key, value) filterPart = _.zip(key, value)
.map(([k, v]) => { .map(([k, v]) => {
if (valueAsString) { if (valueAsString) {
return `${k}=\\"${v}\\"`; return `${k}=\\"${v}\\"`
} else { } else {
return `${k}=${v}`; return `${k}=${v}`
} }
}) })
.join(","); .join(",")
} }
const command = `from ${djangoModelImportPath} import ${djangoModel}; const command = `from ${djangoModelImportPath} import ${djangoModel};
@ -119,13 +119,13 @@ function loadObjectJson(
object = ${djangoModel}.objects.filter(${filterPart}).first(); object = ${djangoModel}.objects.filter(${filterPart}).first();
print(create_json_from_objects(object, ${serializerModel}, many=False)); print(create_json_from_objects(object, ${serializerModel}, many=False));
exit(); exit();
`.replace(/(?:\r\n|\r|\n)/g, ""); `.replace(/(?:\r\n|\r|\n)/g, "")
return cy.manageShellCommand(command).then((result) => { return cy.manageShellCommand(command).then((result) => {
const objectJson = JSON.parse(result.stdout); const objectJson = JSON.parse(result.stdout)
// console.log(command); // console.log(command);
console.log(objectJson); console.log(objectJson)
return objectJson; return objectJson
}); })
} }
Cypress.Commands.add("loadAssignmentCompletion", (key, value) => { Cypress.Commands.add("loadAssignmentCompletion", (key, value) => {
@ -135,8 +135,8 @@ Cypress.Commands.add("loadAssignmentCompletion", (key, value) => {
"vbv_lernwelt.assignment.models.AssignmentCompletion", "vbv_lernwelt.assignment.models.AssignmentCompletion",
"vbv_lernwelt.assignment.serializers.CypressAssignmentCompletionSerializer", "vbv_lernwelt.assignment.serializers.CypressAssignmentCompletionSerializer",
true true
); )
}); })
Cypress.Commands.add("loadSecurityRequestResponseLog", (key, value) => { Cypress.Commands.add("loadSecurityRequestResponseLog", (key, value) => {
return loadObjectJson( return loadObjectJson(
@ -145,8 +145,8 @@ Cypress.Commands.add("loadSecurityRequestResponseLog", (key, value) => {
"vbv_lernwelt.core.models.SecurityRequestResponseLog", "vbv_lernwelt.core.models.SecurityRequestResponseLog",
"vbv_lernwelt.core.serializers.CypressSecurityRequestResponseLogSerializer", "vbv_lernwelt.core.serializers.CypressSecurityRequestResponseLogSerializer",
true true
); )
}); })
Cypress.Commands.add("loadExternalApiRequestLog", (key, value) => { Cypress.Commands.add("loadExternalApiRequestLog", (key, value) => {
return loadObjectJson( return loadObjectJson(
@ -155,8 +155,8 @@ Cypress.Commands.add("loadExternalApiRequestLog", (key, value) => {
"vbv_lernwelt.core.models.ExternalApiRequestLog", "vbv_lernwelt.core.models.ExternalApiRequestLog",
"vbv_lernwelt.core.serializers.CypressExternalApiRequestLogSerializer", "vbv_lernwelt.core.serializers.CypressExternalApiRequestLogSerializer",
true true
); )
}); })
Cypress.Commands.add("loadFeedbackResponse", (key, value) => { Cypress.Commands.add("loadFeedbackResponse", (key, value) => {
return loadObjectJson( return loadObjectJson(
@ -165,8 +165,8 @@ Cypress.Commands.add("loadFeedbackResponse", (key, value) => {
"vbv_lernwelt.feedback.models.FeedbackResponse", "vbv_lernwelt.feedback.models.FeedbackResponse",
"vbv_lernwelt.feedback.serializers.CypressFeedbackResponseSerializer", "vbv_lernwelt.feedback.serializers.CypressFeedbackResponseSerializer",
true true
); )
}); })
Cypress.Commands.add("loadCheckoutInformation", (key, value) => { Cypress.Commands.add("loadCheckoutInformation", (key, value) => {
return loadObjectJson( return loadObjectJson(
@ -175,8 +175,8 @@ Cypress.Commands.add("loadCheckoutInformation", (key, value) => {
"vbv_lernwelt.shop.models.CheckoutInformation", "vbv_lernwelt.shop.models.CheckoutInformation",
"vbv_lernwelt.shop.serializers.CypressCheckoutInformationSerializer", "vbv_lernwelt.shop.serializers.CypressCheckoutInformationSerializer",
true true
); )
}); })
Cypress.Commands.add("loadUser", (key, value) => { Cypress.Commands.add("loadUser", (key, value) => {
return loadObjectJson( return loadObjectJson(
@ -191,32 +191,32 @@ Cypress.Commands.add("loadUser", (key, value) => {
Cypress.Commands.add("makeSelfEvaluation", (answers) => { Cypress.Commands.add("makeSelfEvaluation", (answers) => {
for (let i = 0; i < answers.length; i++) { for (let i = 0; i < answers.length; i++) {
const answer = answers[i]; const answer = answers[i]
if (answer) { if (answer) {
cy.get('[data-cy="success"]').click(); cy.get('[data-cy="success"]').click()
} else { } else {
cy.get('[data-cy="fail"]').click(); cy.get('[data-cy="fail"]').click()
} }
if (i < answers.length - 1) { if (i < answers.length - 1) {
cy.get('[data-cy="next-step"]').click({ force: true }); cy.get('[data-cy="next-step"]').click({ force: true })
} else { } else {
cy.get('[data-cy="complete-and-continue"]').click({ force: true }); cy.get('[data-cy="complete-and-continue"]').click({ force: true })
} }
} }
}); })
Cypress.Commands.add("learningContentMultiLayoutNextStep", () => { Cypress.Commands.add("learningContentMultiLayoutNextStep", () => {
return cy.get('[data-cy="next-step"]').click({ force: true }); return cy.get('[data-cy="next-step"]').click({ force: true })
}); })
Cypress.Commands.add("learningContentMultiLayoutPreviousStep", () => { Cypress.Commands.add("learningContentMultiLayoutPreviousStep", () => {
return cy.get('[data-cy="previous-step"]').click({ force: true }); return cy.get('[data-cy="previous-step"]').click({ force: true })
}); })
Cypress.Commands.add("testLearningContentTitle", (title) => { Cypress.Commands.add("testLearningContentTitle", (title) => {
return cy.get('[data-cy="lc-title"]').should("contain", title); return cy.get('[data-cy="lc-title"]').should("contain", title)
}); })
Cypress.Commands.add("testLearningContentSubtitle", (subtitle) => { Cypress.Commands.add("testLearningContentSubtitle", (subtitle) => {
return cy.get('[data-cy="lc-subtitle"]').should("contain", subtitle); return cy.get('[data-cy="lc-subtitle"]').should("contain", subtitle)
}); })

Binary file not shown.

View File

@ -6,6 +6,7 @@
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"cypress:ci": "cypress-cloud run --parallel --record", "cypress:ci": "cypress-cloud run --parallel --record",
"cypress:install": "cypress install",
"prettier": "npm run prettier --prefix client" "prettier": "npm run prettier --prefix client"
}, },
"devDependencies": { "devDependencies": {

View File

@ -61,12 +61,32 @@ from vbv_lernwelt.shop.models import CheckoutInformation
@click.option( @click.option(
"--create-assignment-completion/--no-create-assignment-completion", "--create-assignment-completion/--no-create-assignment-completion",
default=False, default=False,
help="will create assignment completion data for test-student1@example.com", help="will create assignment completion data for test-student1@example.com by default. Other user can be specified with --assignment-completion-user",
)
@click.option(
"--assignment-completion-user-id",
default=TEST_STUDENT1_USER_ID,
help="user to create assignment completion for. Defaults to test-student1@example.com. Hint: Is only evaluated if --create-assignment-completion is set.",
)
@click.option(
"--assignment-completion-course-session-id",
default=TEST_COURSE_SESSION_BERN_ID,
help="course session to create assignment completion in. Defaults to 'Test Bern 2022 a'. Hint: Is only evaluated if --create-assignment-completion is set.",
) )
@click.option( @click.option(
"--create-assignment-evaluation/--no-create-assignment-evaluation", "--create-assignment-evaluation/--no-create-assignment-evaluation",
default=False, default=False,
help="will create assignment evaluation data for test-student1@example.com", help="will create assignment evaluation data for test-student1@example.com by default. Other user can be specified with --assignment-evaluation-user",
)
@click.option(
"--assignment-evaluation-user-id",
default=TEST_TRAINER1_USER_ID,
help="user to create assignment evaluation for. Defaults to test-trainer1@example.com. Hint: Is only evaluated if --create-assignment-completion is set.",
)
@click.option(
"--assignment-evaluation-course-session-id",
default=TEST_COURSE_SESSION_BERN_ID,
help="course session to create assignment evaluation in. Defaults to 'Test Bern 2022 a'. Hint: Is only evaluated if --create-assignment-completion is set.",
) )
@click.option( @click.option(
"--assignment-evaluation-scores", "--assignment-evaluation-scores",
@ -83,7 +103,17 @@ from vbv_lernwelt.shop.models import CheckoutInformation
type=(int, int, float), type=(int, int, float),
default=(None, None, 0.0), default=(None, None, 0.0),
metavar="USER_POINTS MAX_POINTS POINTS_DEDUCTED", metavar="USER_POINTS MAX_POINTS POINTS_DEDUCTED",
help="Create edoniq result data for test-student1@example.com with user points and max points", help="Create edoniq result data for test-student1@example.com by default with user points and max points. Use --edoniq-user-id to specify a different user.",
)
@click.option(
"--edoniq-user-id",
default=TEST_STUDENT1_USER_ID,
help="User to create edoniq test results for. Defaults to test-student1@example.com. Hint: Is only evaluated if --create-edoniq-test-results is set.",
)
@click.option(
"--edoniq-course-session-id",
default=TEST_COURSE_SESSION_BERN_ID,
help="course session to create edoniq test results in. Defaults to 'Test Bern 2022 a'. Hint: Is only evaluated if --create-edoniq-test-results is set.",
) )
@click.option( @click.option(
"--create-feedback-responses/--no-create-feedback-responses", "--create-feedback-responses/--no-create-feedback-responses",
@ -127,10 +157,16 @@ from vbv_lernwelt.shop.models import CheckoutInformation
) )
def command( def command(
create_assignment_completion, create_assignment_completion,
assignment_completion_user_id,
assignment_completion_course_session_id,
create_assignment_evaluation, create_assignment_evaluation,
assignment_evaluation_user_id,
assignment_evaluation_course_session_id,
assignment_evaluation_scores, assignment_evaluation_scores,
assignment_points_deducted, assignment_points_deducted,
create_edoniq_test_results, create_edoniq_test_results,
edoniq_user_id,
edoniq_course_session_id,
create_feedback_responses, create_feedback_responses,
create_course_completion_performance_criteria, create_course_completion_performance_criteria,
create_attendance_days, create_attendance_days,
@ -181,15 +217,19 @@ def command(
assignment=Assignment.objects.get( assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice" slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
), ),
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID), course_session=CourseSession.objects.get(
user=User.objects.get(id=TEST_STUDENT1_USER_ID), id=assignment_completion_course_session_id
),
user=User.objects.get(id=assignment_completion_user_id),
) )
create_test_assignment_submitted_data( create_test_assignment_submitted_data(
assignment=Assignment.objects.get( assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-mein-kundenstamm" slug="test-lehrgang-assignment-mein-kundenstamm"
), ),
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID), course_session=CourseSession.objects.get(
user=User.objects.get(id=TEST_STUDENT1_USER_ID), id=assignment_completion_course_session_id
),
user=User.objects.get(id=assignment_completion_user_id),
) )
if create_assignment_evaluation: if create_assignment_evaluation:
if not assignment_evaluation_scores: if not assignment_evaluation_scores:
@ -206,9 +246,11 @@ def command(
assignment=Assignment.objects.get( assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice" slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"
), ),
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID), course_session=CourseSession.objects.get(
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID), id=assignment_evaluation_course_session_id
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID), ),
assignment_user=User.objects.get(id=assignment_completion_user_id),
evaluation_user=User.objects.get(id=assignment_evaluation_user_id),
input_scores=assignment_evaluation_scores, input_scores=assignment_evaluation_scores,
points_deducted=assignment_points_deducted, points_deducted=assignment_points_deducted,
) )
@ -222,8 +264,8 @@ def command(
assignment=Assignment.objects.get( assignment=Assignment.objects.get(
slug="test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo" slug="test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"
), ),
course_session=CourseSession.objects.get(id=TEST_COURSE_SESSION_BERN_ID), course_session=CourseSession.objects.get(id=edoniq_course_session_id),
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID), assignment_user=User.objects.get(id=edoniq_user_id),
user_points=user_points, user_points=user_points,
max_points=max_points, max_points=max_points,
evaluation_points_deducted=points_deducted, evaluation_points_deducted=points_deducted,