Merge branch 'feature/snapshots' into develop

This commit is contained in:
Ramon Wenger 2021-06-06 19:06:14 +02:00
commit dcd8420419
251 changed files with 5718 additions and 1806 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ node_modules
# Envs
.env
.env.ireland
.envrc
# Editor directories and files
.idea

View File

@ -1,19 +1,3 @@
{
"name": "skillbox",
"projects": {
"private": {
"schemaPath": "schema.graphql"
}
},
"extensions": {
"endpoints": {
"localhost": {
"url": "http://localhost:8000/api/graphql",
"headers": {
"user-agent": "JS GraphQL"
},
"introspect": false
}
}
}
"schemaPath": "server/schema.graphql"
}

1
.graphqlrc Normal file
View File

@ -0,0 +1 @@
schema: 'server/schema.graphql'

2
.tool-versions Normal file
View File

@ -0,0 +1,2 @@
nodejs 12.22.1
python 3.8.10

241
Pipfile.lock generated
View File

@ -25,10 +25,10 @@
},
"autopep8": {
"hashes": [
"sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea",
"sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"
"sha256:276ced7e9e3cb22e5d7c14748384a5cf5d9002257c0ed50c0e075b68011bb6d0",
"sha256:aa213493c30dcdac99537249ee65b24af0b2c29f2e83cd8b3f68760441ed0db9"
],
"version": "==1.5.5"
"version": "==1.5.7"
},
"backcall": {
"hashes": [
@ -55,18 +55,19 @@
},
"boto3": {
"hashes": [
"sha256:1ca39de26205439832fa740fcc200de85f107b615dae445f869a16b76672e663",
"sha256:d6716f6701aae771f6ff2386a1d641855a99a9fedd64bd25476b83bacdd50c94"
"sha256:56f1766f1271b6b4e979c7b56225377f8912050e5935adc5c1c9e3a0338b949e",
"sha256:c61c809d288e88b9a0d926f56f803d0128b498aa9b45a42a6e03cd9a83e5c124"
],
"index": "pypi",
"version": "==1.17.23"
"version": "==1.17.68"
},
"botocore": {
"hashes": [
"sha256:d5ea913331e93cecde25773841397c5d2abbc04f38b1c96c636444e8bb3478f8",
"sha256:f3ae5cae250c3fb200d33abacbee3f0c6513fc6f1d17a851b04a3998de68c829"
"sha256:0f693f5ad6348ec1a62b3a66fee2840d3b722d66b44896022d644275ff8b143d",
"sha256:eb3544911cb0316a33b328a27d137130af278a9c0006be0c95e5e402b01d9865"
],
"version": "==1.20.23"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.68"
},
"certifi": {
"hashes": [
@ -80,14 +81,16 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"decorator": {
"hashes": [
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760",
"sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"
"sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060",
"sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"
],
"version": "==4.4.2"
"markers": "python_version >= '3.5'",
"version": "==5.0.7"
},
"dj-database-url": {
"hashes": [
@ -98,11 +101,11 @@
},
"django": {
"hashes": [
"sha256:30c235dec87e05667597e339f194c9fed6c855bda637266ceee891bf9093da43",
"sha256:e319a7164d6d30cb177b3fd74d02c52f1185c37304057bb76d74047889c605d9"
"sha256:db2214db1c99017cbd971e58824e6f424375154fe358afc30e976f5b99fc6060",
"sha256:e831105edb153af1324de44d06091ca75520a227456387dda4a47d2f1cc2731a"
],
"index": "pypi",
"version": "==2.2.19"
"version": "==2.2.22"
},
"django-appconf": {
"hashes": [
@ -113,11 +116,11 @@
},
"django-compressor": {
"hashes": [
"sha256:57ac0a696d061e5fc6fbc55381d2050f353b973fb97eee5593f39247bc0f30af",
"sha256:d2ed1c6137ddaac5536233ec0a819e14009553fee0a869bea65d03e5285ba74f"
"sha256:3358077605c146fdcca5f9eaffb50aa5dbe15f238f8854679115ebf31c0415e0",
"sha256:f8313f59d5e65712fc28787d084fe834997c9dfa92d064a1a3ec3d3366594d04"
],
"index": "pypi",
"version": "==2.4"
"version": "==2.4.1"
},
"django-cors-headers": {
"hashes": [
@ -186,6 +189,7 @@
"sha256:710b4d15ec1996550cc68a0abbc41903ca7d832540e52b1336e6858737e410d8",
"sha256:bb8f27684814cd1414b2af75b857b5e26a40912631904038a7ecacd2bfafc3ac"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.24.0"
},
"django-treebeard": {
@ -193,6 +197,7 @@
"sha256:7c2b1cdb1e9b46d595825186064a1228bc4d00dbbc186db5b0b9412357fba91c",
"sha256:80150017725239702054e5fa64dc66e383dc13ac262c8d47ee5a82cb005969da"
],
"markers": "python_version >= '3.6'",
"version": "==4.5.1"
},
"djangorestframework": {
@ -220,15 +225,17 @@
},
"faker": {
"hashes": [
"sha256:97fb6748031569a65265d38d85370b3c3970f4333b40430d52a60b37dea97877",
"sha256:ee86c3140e1d1651b76a7aab637c7da068660f33937143e7063c663f4cec45ab"
"sha256:156854f36d4086bb21ff85a79b4d6a6403a240cd2c17a33a44b8ea4ff4e957c2",
"sha256:a2ed065342e91a7672407325848cd5728d5e5eb4928d0a1c478fd4f0dd97d1f7"
],
"version": "==6.5.2"
"markers": "python_version >= '3.6'",
"version": "==8.1.2"
},
"future": {
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2"
},
"gprof2dot": {
@ -279,6 +286,7 @@
"sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d",
"sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.1"
},
"idna": {
@ -286,15 +294,16 @@
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"ipython": {
"hashes": [
"sha256:04323f72d5b85b606330b6d7e2dc8d2683ad46c3905e955aa96ecc7a99388e70",
"sha256:34207ffb2f653bced2bc8e3756c1db86e7d93e44ed049daae9814fed66d408ec"
"sha256:714810a5c74f512b69d5f3b944c86e592cee0a5fb9c728e582f074610f6cf038",
"sha256:f78c6a3972dde1cc9e4041cbf4de583546314ba52d3c97208e5b6b2221a9cb7d"
],
"index": "pypi",
"version": "==7.21.0"
"version": "==7.23.1"
},
"ipython-genutils": {
"hashes": [
@ -316,6 +325,7 @@
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.3"
},
"jmespath": {
@ -323,6 +333,7 @@
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0"
},
"libsass": {
@ -398,8 +409,17 @@
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"matplotlib-inline": {
"hashes": [
"sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811",
"sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"
],
"markers": "python_version >= '3.5'",
"version": "==0.1.2"
},
"newrelic": {
"hashes": [
"sha256:242a5e901d684f7ffdd621bc58da8fe9a85d5545b4b63e1070589f5ab45c9e1e",
@ -422,6 +442,7 @@
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.9"
},
"parso": {
@ -429,6 +450,7 @@
"sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea",
"sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.7.1"
},
"pexpect": {
@ -501,10 +523,11 @@
},
"prompt-toolkit": {
"hashes": [
"sha256:0fa02fa80363844a4ab4b8d6891f62dd0645ba672723130423ca4037b80c1974",
"sha256:62c811e46bd09130fb11ab759012a4ae385ce4fb2073442d1898867a824183bd"
"sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
],
"version": "==3.0.16"
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.18"
},
"psycopg2": {
"hashes": [
@ -536,23 +559,26 @@
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
],
"version": "==2.6.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.7.0"
},
"pygments": {
"hashes": [
"sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94",
"sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
],
"version": "==2.8.1"
"markers": "python_version >= '3.5'",
"version": "==2.9.0"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"python-dateutil": {
@ -560,6 +586,7 @@
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
},
"python-dotenv": {
@ -635,17 +662,18 @@
},
"s3transfer": {
"hashes": [
"sha256:1e28620e5b444652ed752cf87c7e0cb15b0e578972568c6609f0f18212f259ed",
"sha256:7fdddb4f22275cf1d32129e21f056337fd2a80b6ccef1664528145b72c49e6d2"
"sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc",
"sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2"
],
"version": "==0.3.4"
"version": "==0.4.2"
},
"sendgrid": {
"hashes": [
"sha256:2eb1dcb1f7d8656eed4db586e428c2c86f347590b8511d7f92993882d0e4fab9",
"sha256:e422c8263563ac7d664066d2f87b90bcb005b067eb7c33a9b1396442b2ed285b"
"sha256:273bdc0abec649bf6319df7b6267980f79e53ab64e92906d65eea6d4330d00b4",
"sha256:74b0dcf9a79188948f61f456bd1bf67ffa676a5d388aba1c76bff516566d7084"
],
"version": "==6.6.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==6.7.0"
},
"sentry-sdk": {
"hashes": [
@ -660,20 +688,23 @@
"sha256:58b46ce1cc4d43af0aac3ac9a047bdb0f44e05f0b2fa2eec755863331700c865",
"sha256:85c97f94c8957fa4e6dab113156c182fb346d56d059af78aad710bced15f16fb"
],
"markers": "python_version >= '2.6'",
"version": "==3.6.1"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.15.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
},
"starkbank-ecdsa": {
@ -694,6 +725,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"traitlets": {
@ -701,6 +733,7 @@
"sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396",
"sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"
],
"markers": "python_version >= '3.7'",
"version": "==5.0.5"
},
"typing": {
@ -708,6 +741,7 @@
"sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9",
"sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.7.4.3"
},
"unidecode": {
@ -727,10 +761,11 @@
},
"urllib3": {
"hashes": [
"sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
"sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
"sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
"sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
],
"version": "==1.26.3"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.4"
},
"wagtail": {
"hashes": [
@ -742,10 +777,10 @@
},
"wagtail-autocomplete": {
"hashes": [
"sha256:5f8ddf16d3ca365af4ad0f09e8f45d9e982ca854dc1b084a8a30f66b00a7739d"
"sha256:73602db77eb3eee27f17a0b22aa88badf9fb5079fb1822c921f06a17224ce4db"
],
"index": "pypi",
"version": "==0.6"
"version": "==0.6.3"
},
"wagtail-factories": {
"hashes": [
@ -788,25 +823,26 @@
"develop": {
"asgiref": {
"hashes": [
"sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17",
"sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"
"sha256:92906c611ce6c967347bbfea733f13d6313901d54dcca88195eaeb52b2a8e8ee",
"sha256:d1216dfbdfb63826470995d31caed36225dcaf34f182e0fa257a4dd9e86f1b78"
],
"version": "==3.3.1"
"markers": "python_version >= '3.6'",
"version": "==3.3.4"
},
"autopep8": {
"hashes": [
"sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea",
"sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"
"sha256:276ced7e9e3cb22e5d7c14748384a5cf5d9002257c0ed50c0e075b68011bb6d0",
"sha256:aa213493c30dcdac99537249ee65b24af0b2c29f2e83cd8b3f68760441ed0db9"
],
"version": "==1.5.5"
"version": "==1.5.7"
},
"awscli": {
"hashes": [
"sha256:191c07061bf227775437bf0b6e6209e0db50affcd8d5a37ba9fe5d87b6b80585",
"sha256:426e2eb8412b59e677ac28603cb6a038bea50ae8fab60ee9edf3756b28d4f419"
"sha256:57ae60a3f59cac265a9e5321c618b8768fdee89565089ada271e24489be5110d",
"sha256:a26b5e24f70cb2c542128ccc11e9d38e43cded687d60cd2ca18b3d28cd902509"
],
"index": "pypi",
"version": "==1.19.23"
"version": "==1.19.68"
},
"backcall": {
"hashes": [
@ -817,10 +853,11 @@
},
"botocore": {
"hashes": [
"sha256:d5ea913331e93cecde25773841397c5d2abbc04f38b1c96c636444e8bb3478f8",
"sha256:f3ae5cae250c3fb200d33abacbee3f0c6513fc6f1d17a851b04a3998de68c829"
"sha256:0f693f5ad6348ec1a62b3a66fee2840d3b722d66b44896022d644275ff8b143d",
"sha256:eb3544911cb0316a33b328a27d137130af278a9c0006be0c95e5e402b01d9865"
],
"version": "==1.20.23"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.68"
},
"certifi": {
"hashes": [
@ -834,6 +871,7 @@
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"colorama": {
@ -841,6 +879,7 @@
"sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
"sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.4.3"
},
"coverage": {
@ -903,18 +942,19 @@
},
"decorator": {
"hashes": [
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760",
"sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"
"sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060",
"sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"
],
"version": "==4.4.2"
"markers": "python_version >= '3.5'",
"version": "==5.0.7"
},
"django": {
"hashes": [
"sha256:30c235dec87e05667597e339f194c9fed6c855bda637266ceee891bf9093da43",
"sha256:e319a7164d6d30cb177b3fd74d02c52f1185c37304057bb76d74047889c605d9"
"sha256:db2214db1c99017cbd971e58824e6f424375154fe358afc30e976f5b99fc6060",
"sha256:e831105edb153af1324de44d06091ca75520a227456387dda4a47d2f1cc2731a"
],
"index": "pypi",
"version": "==2.2.19"
"version": "==2.2.22"
},
"django-silk": {
"hashes": [
@ -929,6 +969,7 @@
"sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
"sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.15.2"
},
"gprof2dot": {
@ -942,22 +983,23 @@
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"ipdb": {
"hashes": [
"sha256:1aa37e19e5b3b96c930fe7fe97193a54202c23a8d1b1c7763f76553f5b275731"
"sha256:178c367a61c1039e44e17c56fcc4a6e7dc11b33561261382d419b6ddb4401810"
],
"index": "pypi",
"version": "==0.13.6"
"version": "==0.13.7"
},
"ipython": {
"hashes": [
"sha256:04323f72d5b85b606330b6d7e2dc8d2683ad46c3905e955aa96ecc7a99388e70",
"sha256:34207ffb2f653bced2bc8e3756c1db86e7d93e44ed049daae9814fed66d408ec"
"sha256:714810a5c74f512b69d5f3b944c86e592cee0a5fb9c728e582f074610f6cf038",
"sha256:f78c6a3972dde1cc9e4041cbf4de583546314ba52d3c97208e5b6b2221a9cb7d"
],
"index": "pypi",
"version": "==7.21.0"
"version": "==7.23.1"
},
"ipython-genutils": {
"hashes": [
@ -979,6 +1021,7 @@
"sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
"sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.3"
},
"jmespath": {
@ -986,6 +1029,7 @@
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.0"
},
"markupsafe": {
@ -1043,13 +1087,23 @@
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
"sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"matplotlib-inline": {
"hashes": [
"sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811",
"sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"
],
"markers": "python_version >= '3.5'",
"version": "==0.1.2"
},
"parso": {
"hashes": [
"sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea",
"sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.7.1"
},
"pexpect": {
@ -1069,10 +1123,11 @@
},
"prompt-toolkit": {
"hashes": [
"sha256:0fa02fa80363844a4ab4b8d6891f62dd0645ba672723130423ca4037b80c1974",
"sha256:62c811e46bd09130fb11ab759012a4ae385ce4fb2073442d1898867a824183bd"
"sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04",
"sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"
],
"version": "==3.0.16"
"markers": "python_full_version >= '3.6.1'",
"version": "==3.0.18"
},
"ptyprocess": {
"hashes": [
@ -1101,23 +1156,26 @@
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
],
"version": "==2.6.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.7.0"
},
"pygments": {
"hashes": [
"sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94",
"sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
],
"version": "==2.8.1"
"markers": "python_version >= '3.5'",
"version": "==2.9.0"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
},
"pytz": {
@ -1159,6 +1217,7 @@
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==5.4.1"
},
"requests": {
@ -1174,27 +1233,30 @@
"sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2",
"sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"
],
"markers": "python_version >= '3.5' and python_version < '4'",
"version": "==4.7.2"
},
"s3transfer": {
"hashes": [
"sha256:1e28620e5b444652ed752cf87c7e0cb15b0e578972568c6609f0f18212f259ed",
"sha256:7fdddb4f22275cf1d32129e21f056337fd2a80b6ccef1664528145b72c49e6d2"
"sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc",
"sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2"
],
"version": "==0.3.4"
"version": "==0.4.2"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"version": "==1.15.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
},
"toml": {
@ -1202,6 +1264,7 @@
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
},
"traitlets": {
@ -1209,14 +1272,16 @@
"sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396",
"sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"
],
"markers": "python_version >= '3.7'",
"version": "==5.0.5"
},
"urllib3": {
"hashes": [
"sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
"sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
"sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df",
"sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"
],
"version": "==1.26.3"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.4"
},
"wcwidth": {
"hashes": [

View File

@ -42,6 +42,7 @@ module.exports = {
alias: {
'@': resolve('src'),
styles: resolve('src/styles'),
gql: resolve('src/graphql/gql')
},
},
module: {

View File

@ -0,0 +1,10 @@
export default {
username: '',
firstName: '',
lastName: '',
avatarUrl: '',
email: '',
onboardingVisited: true,
schoolClasses: {},
id: '',
};

View File

@ -15,6 +15,7 @@
},
"onboardingVisited": false,
"__typename": "UserNode",
"permissions": []
"permissions": [],
"team": null
}
}

View File

@ -0,0 +1,25 @@
export default {
UUID: () => '123-456-789',
GenericStreamFieldType: () => 'GenericStreamFieldType',
DateTime: () => '2021-01-01Z10:01:23',
SnapshotNode: () => ({
// id: ID!
// module: ModuleNode!
chapters: [],
// chapters: [SnapshotChapterNode]
// hiddenContentBlocks(offset: Int, before: String, after: String, first: Int, last: Int, slug: String, title: String): ContentBlockNodeConnection!
// created: DateTime!
// creator: String!
// shared: Boolean!
// objectiveGroups: [SnapshotObjectiveGroupNode]
// hiddenObjectives(offset: Int, before: String, after: String, first: Int, last: Int, text: String): ObjectiveNodeConnection!
title: 'MockSnapshotTitle',
metaTitle: 'MockSnapshotMetaTitle'
// heroImage: String
// changes: SnapshotChangesNode
// mine: Boolean
}),
ContentBlockNode: () => ({
contents: [],
}),
};

View File

@ -0,0 +1,11 @@
export default {
title: 'title',
metaTitle: 'metaTitle',
heroImage: 'heroImage',
teaser: 'teaser',
intro: 'intro',
assignments: {},
objectiveGroups: [],
id: '',
chapters: [],
};

View File

@ -7,8 +7,7 @@
"length": 3,
"sentenceOffset": 0,
"affected": "Hir",
"corrected": "Dir",
"__typename": "SpellCheckStepNode"
"corrected": "Dir"
},
{
"sentence": "Hir ist ein Feler gewesen",
@ -16,8 +15,7 @@
"length": 5,
"sentenceOffset": 0,
"affected": "Feler",
"corrected": "Fehler",
"__typename": "SpellCheckStepNode"
"corrected": "Fehler"
},
{
"sentence": "Hir ist ein Feler gewesen",
@ -25,9 +23,7 @@
"length": 7,
"sentenceOffset": 0,
"affected": "gewesen",
"corrected": "gewesen.",
"__typename": "SpellCheckStepNode"
"corrected": "gewesen."
}
],
"__typename": "SpellCheckPayload"
]
}

View File

@ -1,16 +1,18 @@
import {mockUpdateOnboardingProgress} from '../../support/helpers';
const schema = require('../../fixtures/schema.json');
const me = require('../../fixtures/me.new-student.json');
describe('New student', () => {
it('shows "Enter Code" page and adds the user to a class', () => {
before(() => {
cy.server();
cy.mockGraphql({
schema: schema,
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
});
});
});
it('shows "Enter Code" page and adds the user to a class', () => {
cy.apolloLogin('hansli', 'test');
const __typename = 'SchoolClassNode';
@ -26,9 +28,9 @@ describe('New student', () => {
schoolClass: {
id,
name,
__typename
}
}
__typename,
},
},
},
MySchoolClassQuery: {
me: {
@ -37,21 +39,21 @@ describe('New student', () => {
__typename,
name,
id,
members: []
}
}
members: [],
},
},
},
...mockUpdateOnboardingProgress()
}
...mockUpdateOnboardingProgress(),
},
});
cy.visit('/');
cy.get('[data-cy=join-class-title]').should('contain', 'Einer Klasse beitreten');
cy.get('[data-cy=input-class-code]').type('XXXX');
cy.get('[data-cy=join-class]').click();
cy.get('[data-cy=join-form-title]').should('contain', 'Einer Klasse beitreten');
cy.get('[data-cy=input-form-code]').type('XXXX');
cy.get('[data-cy=join-form-confirm]').click();
cy.skipOnboarding();
cy.get('[data-cy=user-widget-avatar]').click();
cy.get('[data-cy=class-list-link]').click();
cy.get('[data-cy=class-list-title]').should('contain', 'Klassenliste');
cy.get('[data-cy=group-list-title]').should('contain', 'Klassenliste');
});
});

View File

@ -1,10 +1,6 @@
// import * as schema from '../fixtures/schema.json';
import {getModules, getMe} from '../../support/helpers';
const mocks = {
UUID: () => 'Whatever',
GenericStreamFieldType: () => [],
};
import {getModules, getMe} from '../../support/helpers';
import mocks from '../../fixtures/mocks';
const operations = {
MeQuery() {

View File

@ -1,4 +1,3 @@
// const schema = require('../../fixtures/schema.json');
const assignments = require('../../fixtures/assignments.json');
const mePayload = require('../../fixtures/me.join-class.json');
const topics = require('../../fixtures/topics.json');

View File

@ -1,5 +1,3 @@
// const schema = require('../../fixtures/schema.json');
const operations = {
ProjectsQuery: {
projects: {

View File

@ -0,0 +1,87 @@
import {getMe} from '../../support/helpers';
import mocks from '../../fixtures/mocks';
const modules = {
'lohn-und-budget': {
'objectiveGroups': {
'edges': [
{
'node': {
'title': 'LANGUAGE_COMMUNICATION',
'objectives': {
'edges': [
{
'node': {
'text': 'i-am-an-objective',
'hiddenFor': {
'edges': [],
},
},
},
],
},
},
},
],
},
},
};
const operations = {
MeQuery() {
return getMe({
schoolClasses: ['FLID2018a'],
teacher: false,
});
},
ModulesQuery: modules,
MySchoolClassQuery: {
me: {},
},
UpdateLastModule: {
updateLastModule: {
success: true,
},
},
SyncModuleVisibility: {
syncModuleVisibility: {
success: true,
},
},
};
// const mocks = {
// UUID: () => 'Whatever',
// GenericStreamFieldType: () => [],
// ObjectiveGroup: () => ({}),
// Module: () => ({
// title: 'title',
// slug: 'slug',
// metaTitle: 'metaTitle',
// teaser: 'teaser',
// intro: 'intro',
// assignments: {edges: []},
// objectiveGroups: {edges: []},
// id: 'ID',
// }),
// };
describe('Objective Visibility', () => {
beforeEach(() => {
cy.server();
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
// endpoint: '/api/graphql'
mocks,
operations,
});
});
});
it('should display the correct objectives', () => {
cy.fakeLogin('rahel.cueni', 'test');
cy.visit('/module/lohn-und-budget');
});
});

View File

@ -1,14 +1,15 @@
import {mockUpdateOnboardingProgress} from '../../support/helpers';
const schema = require('../../fixtures/schema.json');
const me = require('../../fixtures/me.join-class.json');
describe('Onboarding', () => {
beforeEach(() => {
cy.server();
cy.mockGraphql({
schema: schema,
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
});
});
});

View File

@ -1,5 +1,96 @@
const schema = require('../../fixtures/schema.json');
const me = require('../../fixtures/me.new-student.json');
const operations = {
MeQuery: {
me: {
id: 'VXNlck5vZGU6NQ==',
onboardingVisited: true,
permissions: [],
},
},
ProjectsQuery: {
projects: {
edges: [{
node: {
id: 'UHJvamVjdE5vZGU6MzM=',
title: 'Groot',
appearance: 'red',
'description': 'I am Groot',
'slug': 'groot',
'objectives': 'Be Groot\nBe awesome',
'final': false,
'student': {
'firstName': 'Rahel',
'lastName': 'Cueni',
'id': 'VXNlck5vZGU6NQ==',
'avatarUrl': '',
'__typename': 'UserNode',
},
'entriesCount': 2,
'__typename': 'ProjectNode',
},
'__typename': 'ProjectNodeEdge',
}],
'__typename': 'ProjectNodeConnection',
},
},
ProjectQuery: {
'project': {
'id': 'UHJvamVjdE5vZGU6MzY=',
'title': 'Groot',
'appearance': 'yellow',
'description': 'I am Groot',
'slug': 'groot',
'objectives': 'Be Groot\nBe awesome',
'final': false,
'student': {
'firstName': 'Rahel',
'lastName': 'Cueni',
'id': 'VXNlck5vZGU6NQ==',
'avatarUrl': '',
'__typename': 'UserNode',
},
'entriesCount': 1,
'__typename': 'ProjectNode',
'entries': {
'edges': [{
'node': {
'id': 'UHJvamVjdEVudHJ5Tm9kZTo2NQ==',
'activity': 'Kill Thanos',
'reflection': 'He sucks',
'nextSteps': 'Go for the head',
'documentUrl': '',
'__typename': 'ProjectEntryNode',
'created': '2020-01-20T15:20:31.262510+00:00',
},
'__typename': 'ProjectEntryNodeEdge',
}],
'__typename': 'ProjectEntryNodeConnection',
},
},
},
AddProjectEntry: variables => ({
addProjectEntry: {
projectEntry: Object.assign({}, variables.input.projectEntry, {
created: '2020-01-20T15:26:58.722773+00:00',
}),
errors: null,
__typename: 'AddProjectEntryPayload',
},
}),
UpdateProjectEntry: variables => ({
updateProjectEntry: {
projectEntry: variables.input.projectEntry,
errors: null,
__typename: 'UpdateProjectEntryPayload',
},
}),
DeleteProjectEntry: {
deleteProjectEntry: {
success: true,
__typename: 'DeleteProjectEntryPayload',
},
},
};
describe('Project Entry', () => {
beforeEach(() => {
@ -7,100 +98,11 @@ describe('Project Entry', () => {
cy.fakeLogin('rahel.cueni', 'test');
cy.server();
cy.mockGraphql({
schema: schema,
operations: {
MeQuery: {
me: {
id: 'VXNlck5vZGU6NQ==',
onboardingVisited: true,
permissions: []
}
},
ProjectsQuery: {
projects: {
edges: [{
node: {
id: 'UHJvamVjdE5vZGU6MzM=',
title: 'Groot',
appearance: 'red',
'description': 'I am Groot',
'slug': 'groot',
'objectives': 'Be Groot\nBe awesome',
'final': false,
'student': {
'firstName': 'Rahel',
'lastName': 'Cueni',
'id': 'VXNlck5vZGU6NQ==',
'avatarUrl': '',
'__typename': 'UserNode'
},
'entriesCount': 2,
'__typename': 'ProjectNode'
},
'__typename': 'ProjectNodeEdge'
}],
'__typename': 'ProjectNodeConnection'
}
},
ProjectQuery: {
'project': {
'id': 'UHJvamVjdE5vZGU6MzY=',
'title': 'Groot',
'appearance': 'yellow',
'description': 'I am Groot',
'slug': 'groot',
'objectives': 'Be Groot\nBe awesome',
'final': false,
'student': {
'firstName': 'Rahel',
'lastName': 'Cueni',
'id': 'VXNlck5vZGU6NQ==',
'avatarUrl': '',
'__typename': 'UserNode'
},
'entriesCount': 1,
'__typename': 'ProjectNode',
'entries': {
'edges': [{
'node': {
'id': 'UHJvamVjdEVudHJ5Tm9kZTo2NQ==',
'activity': 'Kill Thanos',
'reflection': 'He sucks',
'nextSteps': 'Go for the head',
'documentUrl': '',
'__typename': 'ProjectEntryNode',
'created': '2020-01-20T15:20:31.262510+00:00'
},
'__typename': 'ProjectEntryNodeEdge'
}],
'__typename': 'ProjectEntryNodeConnection'
}
}
},
AddProjectEntry: variables => ({
addProjectEntry: {
projectEntry: Object.assign({}, variables.input.projectEntry, {
created: '2020-01-20T15:26:58.722773+00:00'
}),
errors: null,
__typename: 'AddProjectEntryPayload'
}
}),
UpdateProjectEntry: variables => ({
updateProjectEntry: {
projectEntry: variables.input.projectEntry,
errors: null,
__typename: 'UpdateProjectEntryPayload'
}
}),
DeleteProjectEntry: {
deleteProjectEntry: {
success: true,
__typename: 'DeleteProjectEntryPayload'
}
}
}
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
operations,
});
});
});

View File

@ -1,4 +1,3 @@
const schema = require('../../fixtures/schema.json');
const me = require('../../fixtures/me.join-class.json');
const selectedClass = require('../../fixtures/selected-school-class.json');
@ -6,8 +5,10 @@ describe('Class Management', () => {
beforeEach(() => {
cy.server();
cy.mockGraphql({
schema: schema,
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
});
});
cy.viewport('macbook-15');
@ -104,7 +105,7 @@ describe('Class Management', () => {
});
cy.visit('/me/my-class');
cy.get('[data-cy=school-class-member]').should('have.length', 2);
cy.get('[data-cy=group-list-member]').should('have.length', 2);
cy.get('[data-cy=remove-from-class]').should('have.length', 0);
cy.get('[data-cy=add-to-class]').should('have.length', 0);
});
@ -187,8 +188,10 @@ describe('Teacher Class Management', () => {
beforeEach(() => {
cy.server();
cy.mockGraphql({
schema: schema,
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
});
});
cy.viewport('macbook-15');
@ -229,10 +232,10 @@ describe('Teacher Class Management', () => {
cy.visit('/me/my-class');
cy.get('[data-cy=edit-class-name-link]').click();
cy.get('[data-cy=edit-class-name-input] input').type('{selectall}{backspace}').type(className);
cy.get('[data-cy=edit-group-name-link]').click();
cy.get('[data-cy=edit-name-input] input').type('{selectall}{backspace}').type(className);
cy.get('[data-cy=modal-save-button]').click();
cy.get('[data-cy=school-class-name]').should('contain', className);
cy.get('[data-cy=group-list-name]').should('contain', className);
});
// // fixme: cache misbehaves with mequery, but only for test

View File

@ -0,0 +1,114 @@
import minimalMe from '../../fixtures/me.minimal';
import module from '../../fixtures/module.minimal';
import mocks from '../../fixtures/mocks';
const me = isTeacher => ({
...minimalMe,
isTeacher,
});
const operations = isTeacher => ({
operations: {
MeQuery: {
me: me(isTeacher),
},
ModuleDetailsQuery: {
module,
},
CreateSnapshot: {
createSnapshot: {
snapshot: {
id: '',
title: '',
created: '',
creator: '',
},
success: true,
},
},
ModuleSnapshotsQuery: {
module: {
...module,
snapshots: [
{
id: 'snapshot-id',
title: 'title',
created: '2020-01-01',
mine: true,
shared: false,
creator: 'me',
},
],
},
},
SnapshotDetail: {
snapshot: {
chapters: [],
module: {}
}
},
ApplySnapshot: {
applySnapshot: {
success: true
}
}
},
});
describe('Snapshot', () => {
beforeEach(() => {
cy.server();
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
mocks,
});
});
cy.viewport('macbook-15');
});
it('Menu is visible for teacher', () => {
cy.fakeLogin('nico.zickgraf', 'test');
cy.mockGraphqlOps(operations(true));
cy.visit('module/miteinander-reden/');
cy.getByDataCy('snapshot-menu').should('be.visible');
});
it('Menu is not visible for student', () => {
cy.fakeLogin('rahel.cueni', 'test');
cy.mockGraphqlOps(operations(false));
cy.visit('module/miteinander-reden/');
cy.getByDataCy('module-title').should('be.visible');
cy.getByDataCy('snapshot-menu').should('not.exist');
});
it('Creates Snapshot', () => {
cy.fakeLogin('nico.zickgraf', 'test');
cy.mockGraphqlOps(operations(true));
cy.visit('module/miteinander-reden/');
cy.getByDataCy('module-snapshots-button').click();
cy.getByDataCy('create-snapshot-button').click();
cy.getByDataCy('show-all-snapshots-button').click();
cy.getByDataCy('snapshot-list').should('exist').within(() => {
cy.get('.snapshots__snapshot').should('have.length', 1);
});
});
it('Applies Snapshot', () => {
cy.fakeLogin('nico.zickgraf', 'test');
cy.mockGraphqlOps(operations(true));
cy.visit('module/miteinander-reden/snapshots');
cy.getByDataCy('snapshot-link').click();
cy.getByDataCy('apply-checkbox').click();
cy.getByDataCy('apply-button').click();
cy.getByDataCy('module-title').should('exist');
cy.getByDataCy('snapshot-header').should('not.exist');
});
});

View File

@ -1,33 +1,142 @@
import {mockUpdateLastModule} from '../../support/helpers';
import module from '../../fixtures/module.minimal';
const schema = require('../../fixtures/schema.json');
const assignments = require('../../fixtures/assignments.json');
const module = require('../../fixtures/module.json');
const spellCheck = require('../../fixtures/spell-check.json');
const operations = {
MeQuery: {
me: {
permissions: [],
onboardingVisited: true,
},
},
AssignmentQuery: {
assignment: {
id: '',
title: 'Ein Auftragstitel',
assignment: 'Ein Auftrag',
submission: {
id: 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=',
text: 'Hir ist ein Feler gewesen',
final: false,
document: '',
submissionFeedback: {
'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox',
'text': '\ud83d\ude42\ud83d\ude10\ud83e\udd2c\ud83d\udc4d\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83d\ude2e\ud83e\udd17',
'teacher': {
'firstName': 'Nico',
'lastName': 'Zickgraf',
'__typename': 'UserNode',
},
},
},
},
},
ModuleDetailsQuery: {
module: {
...module,
assignments: [
{
'id': 'QXNzaWdubWVudE5vZGU6MQ==',
'title': 'Ein Auftragstitel',
'assignment': 'Ein Auftrag',
'solution': null,
'submission': {
'id': 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=',
'text': 'Hir ist ein Feler gewesen',
'final': false,
'document': '',
'submissionFeedback': {
'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox',
'text': '🙂😐🤬👍🤢🤢🤢🤢😮🤗',
'teacher': {
'firstName': 'Nico',
'lastName': 'Zickgraf',
'__typename': 'UserNode',
},
'__typename': 'SubmissionFeedbackNode',
},
'__typename': 'StudentSubmissionNode',
},
'__typename': 'AssignmentNode',
},
],
chapters: [
{
'id': 'Q2hhcHRlck5vZGU6MTg=',
'title': '1.1 Lehrbeginn',
'description': 'Wie sieht Ihr Konsumverhalten aus?',
'bookmark': {
'note': {
'id': 'Tm90ZU5vZGU6Mg==',
'text': 'Chapter Chapter',
'__typename': 'NoteNode',
},
'__typename': 'ChapterBookmarkNode',
},
contentBlocks: [
{
'id': 'Q29udGVudEJsb2NrTm9kZToxOQ==',
'slug': 'assignment',
'title': 'Assignment',
'type': 'NORMAL',
'contents': [
{
'type': 'assignment',
'value': {
'title': 'Ein Auftragstitel',
'assignment': 'Ein Auftrag',
'id': 'QXNzaWdubWVudE5vZGU6MQ==',
},
'id': 'df8212ee-3e82-49fa-977e-c4b60789163e',
},
],
'userCreated': false,
'mine': false,
'bookmarks': [
{
'uuid': 'df8212ee-3e82-49fa-977e-c4b60789163e',
'note': {
'id': 'Tm90ZU5vZGU6Mw==',
'text': 'Noch eine Notiz',
'__typename': 'NoteNode',
},
'__typename': 'ContentBlockBookmarkNode',
},
],
'hiddenFor': [],
'visibleFor': [],
'__typename': 'ContentBlockNode',
},
],
},
],
},
},
SpellCheck: {
spellCheck,
},
UpdateAssignment: {
updateAssignment: {
assignment: {
id: '',
title: 'title',
assignment: '',
solution: '',
},
},
},
...mockUpdateLastModule(),
};
describe('Spellcheck', () => {
before(() => {
cy.server();
cy.mockGraphql({
schema: schema,
operations: {
MeQuery: {
me: {
permissions: [],
onboardingVisited: true
}
},
AssignmentsQuery: {
assignments
},
ModulesQuery: {
module
},
SpellCheck: {
spellCheck
},
...mockUpdateLastModule()
}
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
operations,
});
});
});
@ -36,9 +145,7 @@ describe('Spellcheck', () => {
cy.visit('/module/lohn-und-budget/');
cy.get('.spellcheck__correction').should('have.length', 0);
cy.get('.submission-form-container__spellcheck').click();
cy.get('.spellcheck__correction').should('have.length', 3);
});
});

View File

@ -1,12 +1,13 @@
const schema = require('../../fixtures/schema.json');
const module = require('../../fixtures/module.json');
describe('Survey', () => {
beforeEach(() => {
cy.server();
cy.mockGraphql({
schema: schema,
cy.task('getSchema').then(schema => {
cy.mockGraphql({
schema,
});
});
cy.viewport('macbook-15');

View File

@ -20,7 +20,7 @@ module.exports = (on, config) => {
on('task', {
getSchema() {
return readFileSync(
resolve(__dirname, '../../../schema.graphql'),
resolve(__dirname, '../../../server/schema.graphql'),
'utf8'
);
}

View File

@ -22,7 +22,7 @@ export const getMe = ({schoolClasses, teacher}) => {
'firstName': 'Rahel',
'lastName': 'Cueni',
'avatarUrl': '',
'isTeacher': false,
'isTeacher': teacher,
'lastModule': {
'id': 'TW9kdWxlTm9kZToxNw==',
'slug': 'lohn-und-budget',

View File

@ -20,7 +20,8 @@ declare namespace Cypress {
selectClass(schoolClass: string): void
login(username:string, password:string, visitLogin?: boolean): void
fakeLogin(username:string, password:string): void
login(username: string, password: string, visitLogin?: boolean): void
fakeLogin(username: string, password: string): void
}
}

View File

@ -3,6 +3,8 @@
"allowJs": true,
"checkJs": true,
"resolveJsonModule": true,
"lib": ["es2015", "dom"],
"target": "ES5",
"types": [
"cypress"
]

View File

@ -19,7 +19,8 @@ module.exports = {
'/node_modules/'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
'^@/(.*)$': '<rootDir>/src/$1',
'^gql/(.*)$': '<rootDir>/src/graphql/gql/$1',
},
snapshotSerializers: [
'<rootDir>/node_modules/jest-serializer-vue'

View File

@ -8336,6 +8336,11 @@
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=",
"dev": true
},
"dayjs": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz",
"integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw=="
},
"de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",

View File

@ -41,6 +41,7 @@
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"dayjs": "^1.10.4",
"debounce": "^1.2.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",

View File

@ -11,7 +11,6 @@
:is="showModal"
v-if="showModal"/>
<component :is="layout"/>
</div>
</template>
@ -32,10 +31,12 @@
import NewNoteWizard from '@/components/notes/NewNoteWizard';
import EditNoteWizard from '@/components/notes/EditNoteWizard';
import EditClassNameWizard from '@/components/school-class/EditClassNameWizard';
import EditTeamNameWizard from '@/components/profile/EditTeamNameWizard';
import FullscreenImage from '@/components/FullscreenImage';
import FullscreenInfographic from '@/components/FullscreenInfographic';
import FullscreenVideo from '@/components/FullscreenVideo';
import DeactivatePerson from '@/components/profile/DeactivatePerson';
import SnapshotCreated from '@/components/modules/SnapshotCreated';
import {mapGetters} from 'vuex';
import ScrollUp from '@/components/ScrollUp';
@ -60,10 +61,12 @@
NewNoteWizard,
EditNoteWizard,
EditClassNameWizard,
EditTeamNameWizard,
FullscreenImage,
FullscreenInfographic,
FullscreenVideo,
DeactivatePerson
DeactivatePerson,
SnapshotCreated
},
computed: {

View File

@ -93,8 +93,7 @@
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_functions.scss";
@import "~styles/helpers";
.assignment-with-submissions {
&__title {

View File

@ -55,154 +55,160 @@
</template>
<script>
import ContentBlock from '@/components/ContentBlock';
import AddContentButton from '@/components/AddContentButton';
import BookmarkActions from '@/components/notes/BookmarkActions';
import VisibilityAction from '@/components/visibility/VisibilityAction';
import ContentBlock from '@/components/ContentBlock';
import AddContentButton from '@/components/AddContentButton';
import BookmarkActions from '@/components/notes/BookmarkActions';
import VisibilityAction from '@/components/visibility/VisibilityAction';
import {mapState} from 'vuex';
import {hidden} from '@/helpers/visibility';
import {CONTENT_TYPE, CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE} from '@/consts/types';
import {mapState} from 'vuex';
import {hidden} from '@/helpers/visibility';
import {CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE, CONTENT_TYPE} from '@/consts/types';
import UPDATE_CHAPTER_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateChapterBookmark.gql';
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
import UPDATE_CHAPTER_BOOKMARK_MUTATION from '@/graphql/gql/mutations/updateChapterBookmark.gql';
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
import me from '@/mixins/me';
import me from '@/mixins/me';
export default {
props: ['chapter', 'index'],
export default {
props: ['chapter', 'index'],
mixins: [me],
mixins: [me],
components: {
BookmarkActions,
VisibilityAction,
ContentBlock,
AddContentButton
},
computed: {
...mapState(['editModule']),
filteredContentBlocks() {
if (!(this.chapter && this.chapter.contentBlocks)) {
return [];
}
if (this.editModule) {
return this.chapter.contentBlocks;
}
return this.chapter.contentBlocks.filter(contentBlock => !hidden({
block: contentBlock,
schoolClass: this.schoolClass,
type: CONTENT_TYPE
}));
components: {
BookmarkActions,
VisibilityAction,
ContentBlock,
AddContentButton,
},
note() {
if (this.chapter && this.chapter.bookmark) {
return this.chapter.bookmark.note;
}
},
titleGreyedOut() {
return this.textHidden(CHAPTER_TITLE_TYPE) && this.editModule;
},
// never hidden when editing the module
titleHidden() {
return this.textHidden(CHAPTER_TITLE_TYPE) && !this.editModule;
},
descriptionGreyedOut() {
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && this.editModule;
},
// never hidden when editing the module
descriptionHidden() {
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && !this.editModule;
}
},
methods: {
bookmark(bookmarked) {
const id = this.chapter.id;
this.$apollo.mutate({
mutation: UPDATE_CHAPTER_BOOKMARK_MUTATION,
variables: {
input: {
chapter: id,
bookmarked
}
},
update: (store, response) => {
const query = CHAPTER_QUERY;
const variables = {id};
const data = store.readQuery({
query,
variables
});
const chapter = data.chapter;
if (bookmarked) {
chapter.bookmark = {
__typename: 'ChapterBookmarkNode',
note: null
};
} else {
chapter.bookmark = null;
}
data.chapter = chapter;
store.writeQuery({
data,
query,
variables
});
},
optimisticResponse: {
__typename: 'Mutation',
updateChapterBookmark: {
__typename: 'UpdateChapterBookmarkPayload',
success: true
}
computed: {
...mapState(['editModule']),
filteredContentBlocks() {
if (!(this.chapter && this.chapter.contentBlocks)) {
return [];
}
});
if (this.editModule) {
return this.chapter.contentBlocks;
}
return this.chapter.contentBlocks.filter(contentBlock => !hidden({
block: contentBlock,
schoolClass: this.schoolClass,
type: CONTENT_TYPE,
}));
},
note() {
if (this.chapter && this.chapter.bookmark) {
return this.chapter.bookmark.note;
}
},
titleGreyedOut() {
return this.textHidden(CHAPTER_TITLE_TYPE) && this.editModule;
},
// never hidden when editing the module
titleHidden() {
if (this.chapter.titleHidden === true) {
return true;
}
return this.textHidden(CHAPTER_TITLE_TYPE) && !this.editModule;
},
descriptionGreyedOut() {
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && this.editModule;
},
// never hidden when editing the module
descriptionHidden() {
if (this.chapter.descriptionHidden === true) {
return true;
}
return this.textHidden(CHAPTER_DESCRIPTION_TYPE) && !this.editModule;
},
},
addNote(id) {
this.$store.dispatch('addNote', {
content: id,
parent: this.chapter.id
});
},
editNote() {
this.$store.dispatch('editNote', this.chapter.bookmark.note);
},
textHidden(type) {
return hidden({
block: this.chapter,
schoolClass: this.schoolClass,
type
});
}
},
};
methods: {
bookmark(bookmarked) {
const id = this.chapter.id;
this.$apollo.mutate({
mutation: UPDATE_CHAPTER_BOOKMARK_MUTATION,
variables: {
input: {
chapter: id,
bookmarked,
},
},
update: (store, response) => {
const query = CHAPTER_QUERY;
const variables = {id};
const data = store.readQuery({
query,
variables,
});
const chapter = data.chapter;
if (bookmarked) {
chapter.bookmark = {
__typename: 'ChapterBookmarkNode',
note: null,
};
} else {
chapter.bookmark = null;
}
data.chapter = chapter;
store.writeQuery({
data,
query,
variables,
});
},
optimisticResponse: {
__typename: 'Mutation',
updateChapterBookmark: {
__typename: 'UpdateChapterBookmarkPayload',
success: true,
},
},
});
},
addNote(id) {
this.$store.dispatch('addNote', {
content: id,
parent: this.chapter.id,
});
},
editNote() {
this.$store.dispatch('editNote', this.chapter.bookmark.note);
},
textHidden(type) {
return hidden({
block: this.chapter,
schoolClass: this.schoolClass,
type,
});
},
},
};
</script>
<style scoped lang="scss">
@import "@/styles/_mixins.scss";
@import "@/styles/_mixins.scss";
.chapter {
position: relative;
&__bookmark-actions {
margin-top: 3px;
}
&__intro {
.chapter {
position: relative;
}
&__description {
@include lead-paragraph;
&__bookmark-actions {
margin-top: 3px;
}
margin-bottom: $large-spacing;
&__intro {
position: relative;
}
&__description {
@include lead-paragraph;
margin-bottom: $large-spacing;
}
}
}
</style>

View File

@ -57,7 +57,7 @@
import VisibilityAction from '@/components/visibility/VisibilityAction';
import ContentComponent from '@/components/content-blocks/ContentComponent';
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
import CHAPTER_QUERY from '@/graphql/gql/queries/chapterQuery.gql';
import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql';
import {mapState} from 'vuex';

View File

@ -57,10 +57,10 @@
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_functions.scss";
@import "~styles/helpers";
.student-submission {
@include table-row;
&__student-name {
font-size: toRem(17px);

View File

@ -16,7 +16,7 @@
</template>
<script>
import ALL_TOPICS_QUERY from '@/graphql/gql/allTopicsQuery.gql';
import ALL_TOPICS_QUERY from '@/graphql/gql/queries/allTopicsQuery.gql';
import sidebarMixin from '@/mixins/sidebar';
export default {

View File

@ -134,7 +134,7 @@
title: this.contentBlock.title,
contents: [...this.contentBlock.contents],
id: this.contentBlock.id || undefined,
isAssignment: this.contentBlock.type && this.contentBlock.type === 'TASK'
isAssignment: this.contentBlock.type && this.contentBlock.type.toLowerCase() === 'task'
}),
me: {}
};

View File

@ -13,8 +13,8 @@
import store from '@/store/index';
import EDIT_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/mutateContentBlock.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import CONTENT_BLOCK_QUERY from '@/graphql/gql/contentBlockQuery.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import CONTENT_BLOCK_QUERY from '@/graphql/gql/queries/contentBlockQuery.gql';
import { setUserBlockType } from '@/helpers/content-block';
export default {

View File

@ -12,7 +12,7 @@
import ContentsForm from '@/components/content-block-form/ContentsForm';
import NEW_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/addContentBlock.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import {setUserBlockType} from '@/helpers/content-block';
export default {

View File

@ -47,9 +47,9 @@
</template>
<script>
import {mapGetters, mapActions} from 'vuex';
import ASSIGNMENT_QUERY from '@/graphql/gql/assignmentQuery.gql';
import ME_QUERY from '@/graphql/gql/meQuery.gql';
import {mapActions, mapGetters} from 'vuex';
import ASSIGNMENT_QUERY from '@/graphql/gql/queries/assignmentQuery.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import UPDATE_ASSIGNMENT_MUTATION from '@/graphql/gql/mutations/updateAssignmentMutation.gql';
import UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS from '@/graphql/gql/mutations/updateAssignmentMutationWithSuccess.gql';
import SPELL_CHECK_MUTATION from '@/graphql/gql/mutations/spellCheck.gql';
@ -66,7 +66,7 @@
components: {
Solution,
SubmissionForm,
SpellCheck
SpellCheck,
},
data() {
@ -75,13 +75,13 @@
submission: this.initialSubmission(),
},
me: {
permissions: []
permissions: [],
},
inputType: 'text',
unsaved: false,
saving: 0,
corrections: '',
spellcheckLoading: false
spellcheckLoading: false,
};
},
@ -98,7 +98,7 @@
},
solution() {
return {
text: this.assignment.solution
text: this.assignment.solution,
};
},
id() {
@ -107,7 +107,7 @@
feedbackText() {
let feedback = this.assignment.submission.submissionFeedback;
return `<span class="inline-title">Feedback von ${feedback.teacher.firstName} ${feedback.teacher.lastName}:</span> ${feedback.text}`;
}
},
},
methods: {
@ -122,20 +122,20 @@
id: this.assignment.id,
answer: this.assignment.submission.text,
document: this.assignment.submission.document,
}
}
},
},
},
update(store, {data: {updateAssignment: {successful, updatedAssignment}}}) {
try {
if (successful) {
const query = ASSIGNMENT_QUERY;
const variables = {
id: updatedAssignment.id
id: updatedAssignment.id,
};
const data = store.readQuery({query, variables});
data.assignment = Object.assign({}, updatedAssignment, {
submission
submission,
});
store.writeQuery({query, variables, data});
}
@ -143,7 +143,7 @@
console.error(e);
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
}
},
}).then(() => {
this.saving--;
if (this.saving === 0) {
@ -177,10 +177,10 @@
id: this.assignment.id,
answer: this.assignment.submission.text,
document: this.assignment.submission.document,
final: true
}
}
}
final: true,
},
},
},
});
},
reopen() {
@ -192,10 +192,10 @@
id: this.assignment.id,
answer: this.assignment.submission.text,
document: this.assignment.submission.document,
final: false
}
}
}
final: false,
},
},
},
});
},
initialSubmission() {
@ -213,16 +213,16 @@
variables: {
input: {
assignment: this.assignment.id,
text: this.assignment.submission.text
}
text: this.assignment.submission.text,
},
},
update(store, {data: {spellCheck: {results}}}) {
self.corrections = results;
}
},
}).then(() => {
this.spellcheckLoading = false;
});
}
},
},
apollo: {
@ -230,7 +230,7 @@
query: ASSIGNMENT_QUERY,
variables() {
return {
id: this.value.id
id: this.value.id,
};
},
result(response) {
@ -240,12 +240,12 @@
if (this.assignment.id === this.scrollToAssignmentId && 'stale' in response) {
this.$nextTick(() => this.scrollToAssignmentReady(true));
}
}
},
},
me: {
query: ME_QUERY
}
}
query: ME_QUERY,
},
},
};
</script>

View File

@ -0,0 +1,141 @@
<template>
<div class="module">
<h2
class="module__meta-title"
id="meta-title">{{ module.metaTitle }}</h2>
<h1
class="module__title"
data-cy="module-title">{{ module.title }}</h1>
<img
:src="module.heroImage"
alt=""
class="module__hero">
<div class="module__intro-wrapper">
<bookmark-actions
:bookmarked="module.bookmark"
:note="note"
class="module__bookmark-actions"
@add-note="$emit('addNote')"
@edit-note="$emit('editNote')"
@bookmark="$emit('bookmark', !module.bookmark)"/>
<div
class="module__intro intro"
v-html="module.intro"/>
</div>
<h3 id="objectives">Lernziele</h3>
<div class="module__objective-groups">
<objective-groups :groups="languageCommunicationObjectiveGroups"/>
<objective-groups :groups="societyObjectiveGroups"/>
<objective-groups :groups="interdisciplinaryObjectiveGroups"/>
</div>
<chapter
:chapter="chapter"
:index="index"
:key="chapter.id"
v-for="(chapter, index) in module.chapters"/>
</div>
</template>
<script>
import ObjectiveGroups from '@/components/objective-groups/ObjectiveGroups.vue';
import Chapter from '@/components/Chapter.vue';
import BookmarkActions from '@/components/notes/BookmarkActions';
export default {
props: {
module: {
type: Object,
default: () => ({}),
},
},
components: {
BookmarkActions,
ObjectiveGroups,
Chapter,
},
computed: {
languageCommunicationObjectiveGroups() {
return this.module.objectiveGroups ? this.module.objectiveGroups
.filter(group => group.title.toLowerCase() === 'language_communication') : [];
},
societyObjectiveGroups() {
return this.module.objectiveGroups ? this.module.objectiveGroups
.filter(group => group.title.toLowerCase() === 'society') : [];
},
interdisciplinaryObjectiveGroups() {
return this.module.objectiveGroups ? this.module.objectiveGroups
.filter(group => group.title.toLowerCase() === 'interdisciplinary') : [];
},
isStudent() {
return !this.me.permissions.includes('users.can_manage_school_class_content');
},
note() {
if (!(this.module && this.module.bookmark)) {
return;
}
return this.module.bookmark.note;
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.module {
display: flex;
justify-self: center;
max-width: 100vw;
@include desktop {
width: 800px;
}
flex-direction: column;
padding: $large-spacing 15px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
&__hero {
margin-bottom: 35px;
border-radius: 12px;
max-width: 100%;
}
&__meta-title {
@include meta-title;
}
&__intro-wrapper {
position: relative;
}
&__intro {
> /deep/ p {
font-size: toRem(25px);
margin-bottom: $large-spacing;
&:last-child {
margin-bottom: 0;
}
}
}
&__bookmark-actions {
margin-top: 3px;
}
&__objective-groups {
margin-bottom: $large-spacing;
}
}
</style>

View File

@ -44,10 +44,7 @@
class="module-navigation__toggle-menu"
v-if="canManageContent"
>
<!-- <a-->
<!-- class="module-navigation__actions"-->
<!-- data-cy="module-snapshots-button">Snapshots</a>-->
<snapshot-menu class="module-navigation__actions"/>
<router-link
:to="{name: 'module-settings'}"
class="module-navigation__actions"
@ -66,6 +63,7 @@
import SubNavigationItem from '@/components/book-navigation/SubNavigationItem';
import ToggleEditing from '@/components/toggle-menu/ToggleEditing';
import me from '@/mixins/me';
import SnapshotMenu from '@/components/modules/SnapshotMenu';
export default {
apollo: {
@ -75,6 +73,7 @@
mixins: [me],
components: {
SnapshotMenu,
BackLink,
SubNavigationItem,
ToggleEditing,

View File

@ -0,0 +1,96 @@
<template>
<modal
:hide-header="false"
:small="true"
class="snapshot-created">
<div slot="header">
<h1 class="snapshot-created__heading">Ein neuer Snapshot wurde erstellt</h1>
</div>
<div class="snapshot-created__content">
<div class="snapshot-created__entry">
<span class="snapshot-created__title">{{ snapshot.title }}</span>
<span class="snapshot-created__meta">{{ created }} - {{ snapshot.creator }}</span>
</div>
</div>
<div slot="footer">
<a
class="button button--primary"
data-cy="show-all-snapshots-button"
@click="toList">Alle Snapshots anzeigen</a>
<a
class="button"
@click="close">Zurück zum Modul</a>
</div>
</modal>
</template>
<script>
import Modal from '@/components/Modal';
import {SNAPSHOT_LIST} from '@/router/module.names';
import dateformat from '@/helpers/date-format';
export default {
components: {
Modal,
},
data() {
return {
snapshotList: {
name: SNAPSHOT_LIST,
},
};
},
computed: {
snapshot() {
return this.$modal.state.payload.snapshot;
},
created() {
return dateformat(this.snapshot.created);
}
},
methods: {
toList() {
this.$router.push(this.snapshotList);
this.$modal.confirm();
},
close() {
this.$modal.cancel();
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.snapshot-created {
&__heading {
@include heading-2;
margin-bottom: 0;
line-height: 1.5;
}
&__content {
min-height: 200px;
}
&__entry {
padding: $medium-spacing;
background-color: $color-brand-light;
display: flex;
align-items: center;
}
&__title {
@include heading-4;
margin-right: $medium-spacing;
}
&__meta {
@include small-text;
}
}
</style>

View File

@ -0,0 +1,176 @@
<template>
<div
class="snapshot-header"
data-cy="snapshot-header">
<h1>Snapshot {{ id }}</h1>
<div class="snapshot-header__meta">
{{ created }} {{ snapshot.creator }}
</div>
<section class="snapshot-header__section">
<h2 class="snapshot-header__subtitle">
In diesem Snapshot sind {{ changesCount }} Anpassungen gespeichert:
</h2>
<ul class="snapshot-header__list">
<li class="snapshot-header__list-item">{{ hiddenObjectives }} Lernziele wurden ausgeblendet
</li>
<li class="snapshot-header__list-item">{{ newObjectives }} Lernziele wurde erfasst</li>
<li class="snapshot-header__list-item">{{ hiddenContentBlocks }} Inhaltsblöcke wurden
ausgeblendet
</li>
<li class="snapshot-header__list-item">{{ newContentBlocks }} Inhaltsblock wurde erfasst</li>
</ul>
</section>
<section class="snapshot-header__section">
<h2 class="snapshot-header__subtitle">
Willst du diesen Snapshot anwenden?
</h2>
<div>
<checkbox
:checked="agreement"
data-cy="apply-checkbox"
label="Ich will die Anpassungen aus diesem Snapshot in das Modul kopieren."
@input="agreement = $event"/>
</div>
</section>
<section class="snapshot-header__buttons snapshot-header__section">
<button
:disabled="!agreement"
:class="{'button--disabled-alt': !agreement}"
class="button button--primary"
data-cy="apply-button"
@click="apply">Snapshot anwenden
</button>
<button
class="button button--secondary"
@click="back">Abbrechen
</button>
</section>
</div>
</template>
<script>
import dateformat from '@/helpers/date-format';
import Checkbox from '@/components/ui/Checkbox';
import me from '@/mixins/me';
import APPLY_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/applySnapshot.gql';
import {MODULE_PAGE} from '@/router/module.names';
const _getChange = (snapshot, index) => {
try {
return snapshot.changes[index];
} catch (e) {
return 0;
}
};
export default {
props: {
snapshot: {
type: Object,
default: () => ({}),
},
},
mixins: [me],
components: {
Checkbox,
},
data: () => ({
agreement: false,
}),
computed: {
created() {
return dateformat(this.snapshot.created);
},
hiddenObjectives() {
return _getChange(this.snapshot, 'hiddenObjectives');
},
newObjectives() {
return _getChange(this.snapshot, 'newObjectives');
},
hiddenContentBlocks() {
return _getChange(this.snapshot, 'hiddenContentBlocks');
},
newContentBlocks() {
return _getChange(this.snapshot, 'newContentBlocks');
},
changesCount() {
return this.hiddenObjectives + this.newObjectives + this.hiddenContentBlocks + this.newContentBlocks;
},
id() {
try {
return atob(this.snapshot.id).split(':')[1];
} catch (e) {
return '';
}
},
},
methods: {
apply() {
this.$apollo.mutate({
mutation: APPLY_SNAPSHOT_MUTATION,
variables: {
input: {
snapshot: this.snapshot.id,
selectedClass: this.me.selectedClass.id,
},
},
}).then(({data: {applySnapshot: {module: {slug}}}}) => {
this.$router.push({
name: MODULE_PAGE,
params: {
slug: slug,
},
});
});
},
back() {
this.$router.go(-1);
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.snapshot-header {
&__subtitle {
@include heading-3;
margin-bottom: $small-spacing;
}
&__meta {
@include regular-text;
margin-bottom: $large-spacing;
}
&__list {
padding-left: $small-spacing;
}
&__list-item {
@include regular-text;
line-height: 1.5;
list-style-type: '';
padding-left: $small-spacing;
}
&__section {
margin-bottom: $large-spacing;
}
&__buttons {
}
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<div class="snapshot-list-item">
<router-link
:to="snapshotRoute"
class="snapshot-list-item__title"
data-cy="snapshot-link"
v-html="snapshot.title"/>
<span
class="snapshot-list-item__date"
v-html="meta"/>
<a
class="snapshot-list-item__link"
v-if="snapshot.mine"
@click="share"
>
<template v-if="snapshot.shared">Nicht mehr teilen</template>
<template v-else>Mit Team teilen</template>
</a>
</div>
</template>
<script>
import dateformat from '@/helpers/date-format';
import {SNAPSHOT_DETAIL} from '@/router/module.names';
import SHARE_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/share.gql';
import gql from 'graphql-tag';
export default {
props: {
snapshot: {
type: Object,
default: () => ({}),
},
},
computed: {
meta() {
const created = dateformat(this.snapshot.created);
if (this.snapshot.mine) {
return created;
}
return `${created} - ${this.snapshot.creator}`;
},
snapshotRoute() {
return {
name: SNAPSHOT_DETAIL,
params: {
id: this.snapshot.id,
},
};
},
},
methods: {
share() {
this.$apollo.mutate({
mutation: SHARE_SNAPSHOT_MUTATION,
variables: {
input: {
snapshot: this.snapshot.id,
shared: !this.snapshot.shared,
},
},
update(store, {data: {shareSnapshot: {snapshot: {id, shared}}}}) {
store.writeFragment({
id,
fragment: gql`fragment SnapshotFragment on SnapshotNode { shared }`,
data: {
shared,
__typename: 'SnapshotNode'
}
});
}
});
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.snapshot-list-item {
display: flex;
align-items: center;
justify-content: flex-start;
&__title {
@include heading-4;
width: 180px;
}
&__date {
@include regular-text;
}
&__link {
@include default-link;
color: $color-brand;
margin-left: auto;
}
}
</style>

View File

@ -0,0 +1,102 @@
<template>
<div
class="snapshot-menu"
data-cy="snapshot-menu">
<a
data-cy="module-snapshots-button"
class="snapshot-menu__toggle"
@click="showPopover = true">Snapshots</a>
<widget-popover
class="snapshot-menu__popover"
v-if="showPopover"
@hide-me="showPopover = false">
<a
class="popover-links__link snapshot-menu__link snapshot-menu__button snapshot-menu__button--primary"
data-cy="create-snapshot-button"
@click="createSnapshot">Neuen
Snapshot erstellen</a>
<router-link
:to="snapshotListRoute"
class="popover-links__link snapshot-menu__link">Alle Snapshots anzeigen
</router-link>
<a class="popover-links__link snapshot-menu__link">Was ist ein Snapshot?</a>
</widget-popover>
</div>
</template>
<script>
import WidgetPopover from '@/components/WidgetPopover';
import {SNAPSHOT_LIST} from '@/router/module.names';
import me from '@/mixins/me';
import CREATE_SNAPSHOT_MUTATION from '@/graphql/gql/mutations/createSnapshot.gql';
export default {
mixins: [me],
components: {
WidgetPopover,
},
data() {
return {
snapshotListRoute: {
name: SNAPSHOT_LIST,
},
showPopover: false,
};
},
methods: {
createSnapshot() {
this.$apollo.mutate({
mutation: CREATE_SNAPSHOT_MUTATION,
variables: {
input: {
module: this.$route.params.slug,
selectedClass: this.me.selectedClass.id,
},
},
}).then(({data: {createSnapshot: {snapshot}}}) => {
this.showPopover = false;
this.$modal.open('snapshot-created', {
snapshot
});
});
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.snapshot-menu {
position: relative;
&__toggle {
@include regular-text;
cursor: pointer;
}
&__popover {
box-sizing: border-box;
width: 300px;
height: 170px;
left: 0;
top: 35px;
padding: $medium-spacing;
}
&__link {
@include large-link;
line-height: 27px;
padding: $small-spacing 0;
}
&__button {
@include button-border;
padding: 5px $small-spacing;
}
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<ul class="snapshot-team-menu">
<li class="snapshot-team-menu__item">
<a
:class="{'snapshot-team-menu__link--active': selected === 'mine'}"
class="snapshot-team-menu__link"
@click="$emit('select', 'mine')">Meine Snapshots</a></li>
<li
class="snapshot-team-menu__item"
>
<a
:class="{'snapshot-team-menu__link--active': selected === 'team'}"
class="snapshot-team-menu__link"
@click="$emit('select', 'team')">Team Snapshots</a></li>
</ul>
</template>
<script>
export default {
props: {
selected: {
type: String,
default: 'mine',
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.snapshot-team-menu {
display: flex;
&__item {
margin-right: $large-spacing;
padding: $medium-spacing 0;
}
&__link {
@include default-link;
color: $color-silver-dark;
cursor: pointer;
&--active {
color: $color-brand;
}
}
}
</style>

View File

@ -9,8 +9,8 @@
import NoteForm from '@/components/notes/NoteForm';
import UPDATE_NOTE_MUTATION from '@/graphql/gql/mutations/updateNote.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import INSTRUMENT_QUERY from '@/graphql/gql/instrumentQuery.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import INSTRUMENT_QUERY from '@/graphql/gql/queries/instrumentQuery.gql';
import {mapGetters} from 'vuex';

View File

@ -24,7 +24,7 @@
import ModalInput from '@/components/ModalInput';
import NEW_OBJECTIVE_MUTATION from '@/graphql/gql/mutations/addObjective.gql';
import OBJECTIVE_GROUP_QUERY from '@/graphql/gql/objectiveGroupQuery.gql';
import OBJECTIVE_GROUP_QUERY from '@/graphql/gql/queries/objectiveGroupQuery.gql';
import {mapGetters} from 'vuex';

View File

@ -30,7 +30,7 @@
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
import DELETE_OBJECTIVE_MUTATION from '@/graphql/gql/mutations/deleteObjective.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/moduleDetailsQuery.gql';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import {hidden} from '@/helpers/visibility';
import {OBJECTIVE_TYPE} from '@/consts/types';

View File

@ -62,7 +62,7 @@
return hidden({
block: this.group,
schoolClass: this.schoolClass,
type: this.type
type: this.type,
});
},
},
@ -70,7 +70,7 @@
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "~styles/helpers";
.objective-group {
position: relative;

View File

@ -3,7 +3,9 @@
<objective-group
:key="group.id"
:group="group"
v-for="group in objectiveGroups"/>
v-for="group in objectiveGroups"
v-if="!group.hidden"
/>
</div>
</template>
@ -58,7 +60,7 @@
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "~styles/helpers";
.objective-groups {
margin-bottom: $large-spacing;

View File

@ -10,7 +10,7 @@
import {mapGetters} from 'vuex';
import PROJECT_ENTRY_QUERY from '@/graphql/gql/projectEntryQuery.gql';
import PROJECT_ENTRY_QUERY from '@/graphql/gql/queries/projectEntryQuery.gql';
import UPDATE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/updateProjectEntry.gql';
export default {

View File

@ -9,7 +9,7 @@
import ProjectEntryForm from './ProjectEntryForm';
import NEW_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/addProjectEntry.gql';
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
export default {
components: {

View File

@ -28,8 +28,8 @@
import WidgetPopover from '@/components/WidgetPopover';
import DELETE_PROJECT_MUTATION from '@/graphql/gql/mutations/deleteProject.gql';
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
import PROJECTS_QUERY from '@/graphql/gql/allProjects.gql';
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
import PROJECTS_QUERY from '@/graphql/gql/queries/allProjects.gql';
import UPDATE_PROJECT_SHARED_STATE_MUTATION from '@/graphql/gql/mutations/updateProjectSharedState.gql';
export default {

View File

@ -45,7 +45,7 @@
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
import DELETE_PROJECT_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteProjectEntry.gql';
import PROJECT_QUERY from '@/graphql/gql/projectQuery.gql';
import PROJECT_QUERY from '@/graphql/gql/queries/projectQuery.gql';
export default {
props: ['activity', 'reflection', 'nextSteps', 'documentUrl', 'created', 'id'],

View File

@ -1,115 +0,0 @@
<template>
<div class="school-class">
<h2 class="school-class__heading"><span
class="school-class__name"
data-cy="school-class-name">{{ name }}</span>
<edit-class-name
v-if="teacher"
@edit="editClassName"/>
</h2>
<div class="school-class__members school-class-members">
<ul
class="school-class-members__list simple-list simple-list--active"
data-cy="active-class-members-list">
<li
:key="member.id"
class="simple-list__item member-item"
data-cy="school-class-member"
v-for="member in activeMembers">
<span class="member-item__name">{{ fullName(member) }}</span>
<span class="member-item__role">{{ role(member) }}</span>
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="remove-from-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('remove', member)">Deaktivieren</a>-->
</li>
</ul>
<!-- <template v-if="inactiveMembers.length">-->
<!-- <h3 class="school-class__inactive-heading">Deaktivierte Personen</h3>-->
<!-- <ul data-cy="inactive-class-members-list" class="simple-list simple-list&#45;&#45;inactive">-->
<!-- <li-->
<!-- class="simple-list__item member-item"-->
<!-- data-cy="school-class-member"-->
<!-- v-for="member in inactiveMembers"-->
<!-- :key="member.id">-->
<!-- <span class="member-item__name">{{fullName(member)}}</span>-->
<!-- <span class="member-item__role">{{role(member)}}</span>-->
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="add-to-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('add', member)">Aktivieren</a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </template>-->
</div>
</div>
</template>
<script>
import EditClassName from '@/components/school-class/EditClassName';
export default {
props: ['members', 'name', 'teacher', 'id'],
components: {
EditClassName
},
computed: {
activeMembers() {
return this.members.filter(member => member.active);
},
inactiveMembers() {
return this.members.filter(member => !member.active);
}
},
methods: {
fullName(member) {
return `${member.firstName} ${member.lastName}`;
},
role({isTeacher}) {
return isTeacher ? 'Lehrperson' : 'Schüler';
},
editClassName() {
this.$store.dispatch('editClassName');
}
},
};
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.school-class {
&__inactive-heading {
@include heading-4;
margin-bottom: $small-spacing;
}
&__name {
@include heading-2;
}
}
.member-item {
&__name {
font-family: $sans-serif-font-family;
font-weight: $font-weight-bold;
flex: 2 1 auto;
}
&__role {
flex: 0 1 110px;
text-align: right;
}
&__action {
flex: 0 1 110px;
padding-left: $large-spacing;
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<a
class="edit-class-name"
data-cy="edit-class-name-link"
class="edit-group-name"
data-cy="edit-group-name-link"
@click="$emit('edit')">
<pen-icon class="edit-class-name__icon"/>
<pen-icon class="edit-group-name__icon"/>
</a>
</template>
@ -18,9 +18,9 @@
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "~styles/_variables.scss";
.edit-class-name {
.edit-group-name {
&__icon {
width: 20px;
height: 20px;

View File

@ -0,0 +1,46 @@
<template>
<modal
:hide-header="false"
:small="true">
<h4 slot="header">{{ type }} bearbeiten</h4>
<modal-input
:value="name"
placeholder="Klassenname"
data-cy="edit-name-input"
@input="$emit('input', $event)"
/>
<div slot="footer">
<a
class="button button--primary"
data-cy="modal-save-button"
@click="$emit('save')">Speichern</a>
<a
class="button"
@click="$emit('cancel')">Abbrechen</a>
</div>
</modal>
</template>
<script>
import Modal from '@/components/Modal';
import ModalInput from '@/components/ModalInput';
export default {
props: {
name: {
type: String,
default: ''
},
type: {
type: String,
default: ''
}
},
components: {
Modal,
ModalInput
},
};
</script>

View File

@ -0,0 +1,69 @@
<template>
<edit-name-wizard
:name="name"
type="Team"
@input="name = $event"
@cancel="hideModal"
@save="save"
/>
</template>
<script>
import me from '@/mixins/me';
import EditNameWizard from '@/components/profile/EditNameWizard';
import UPDATE_TEAM_MUTATION from '@/graphql/gql/mutations/updateTeam.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
export default {
mixins: [me],
components: {
EditNameWizard,
},
data() {
return {
name: ''
};
},
computed: {
team() {
return this.me.team;
}
},
mounted() {
this.name = this.team ? this.team.name : '';
},
methods: {
save() {
this.$apollo.mutate({
mutation: UPDATE_TEAM_MUTATION,
variables: {
input: {
name: this.name,
id: this.team.id
}
},
update(store, {data: {updateTeam: {team: {name}}}}) {
const query = ME_QUERY;
const data = store.readQuery({query});
data.me.team.name = name;
store.writeQuery({query, data});
}
});
this.hideModal();
},
hideModal() {
this.$store.dispatch('hideModal');
}
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
</style>

View File

@ -0,0 +1,175 @@
<template>
<div class="group-list">
<h1
class="group-list__header"
data-cy="group-list-title">{{ title }}</h1>
<router-link
:to="showCodeRoute"
class="group-list__code-link button button--primary"
v-if="showCode">Zugangscode anzeigen
</router-link>
<div class="group-list__content">
<h2 class="group-list__heading"><span
class="group-list__name"
data-cy="group-list-name">{{ name }}</span>
<edit-group-name
v-if="canEdit"
@edit="$emit('edit')"/>
</h2>
<div class="group-list__members group-list-members">
<ul
class="group-list-members__list simple-list simple-list--active"
data-cy="active-class-members-list">
<li
:key="member.id"
class="simple-list__item member-item"
data-cy="group-list-member"
v-for="member in activeMembers">
<span class="member-item__name">{{ fullName(member) }}</span>
<span class="member-item__role">{{ role(member) }}</span>
<a
class="member-item__action simple-list__action"
data-cy="leave-group"
v-if="member.isMe"
@click="$emit('leave')">Verlassen</a>
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="remove-from-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('remove', member)">Deaktivieren</a>-->
</li>
</ul>
<!-- <template v-if="inactiveMembers.length">-->
<!-- <h3 class="group-list__inactive-heading">Deaktivierte Personen</h3>-->
<!-- <ul data-cy="inactive-class-members-list" class="simple-list simple-list&#45;&#45;inactive">-->
<!-- <li-->
<!-- class="simple-list__item member-item"-->
<!-- data-cy="group-list-member"-->
<!-- v-for="member in inactiveMembers"-->
<!-- :key="member.id">-->
<!-- <span class="member-item__name">{{fullName(member)}}</span>-->
<!-- <span class="member-item__role">{{role(member)}}</span>-->
<!-- <a-->
<!-- class="member-item__action simple-list__action"-->
<!-- data-cy="add-to-class"-->
<!-- v-if="teacher"-->
<!-- @click="$emit('add', member)">Aktivieren</a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </template>-->
</div>
</div>
</div>
</template>
<script>
import EditGroupName from '@/components/profile/EditGroupName';
export default {
// props: ['active-members', 'inactive-members', 'name', 'canEdit'],
props: {
title: {
type: String,
default: '',
},
showCode: {
type: Boolean,
default: false,
},
showCodeRoute: {
type: Object,
default: () => ({}),
},
activeMembers: {
type: Array,
default: () => [],
},
inactiveMembers: {
type: Array,
default: () => [],
},
name: {
type: String,
default: '',
},
canEdit: {
type: Boolean,
default: false,
},
},
components: {
EditGroupName,
},
methods: {
fullName(member) {
return `${member.firstName} ${member.lastName}`;
},
role({isTeacher}) {
if (isTeacher === undefined) {
return '';
}
return isTeacher ? 'Lehrperson' : 'Schüler';
},
},
};
</script>
<style scoped lang="scss">
@import "~styles/helpers";
.group-list {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
grid-template-areas: "h b" "c c";
&__header {
grid-area: h;
}
&__code-link {
grid-area: b;
justify-self: end;
align-self: center;
}
&__content {
grid-area: c;
width: 100%;
margin-bottom: $large-spacing;
}
&__inactive-heading {
@include heading-4;
margin-bottom: $small-spacing;
}
&__name {
@include heading-2;
}
}
.member-item {
&__name {
font-family: $sans-serif-font-family;
font-weight: $font-weight-bold;
flex: 2 1 auto;
}
&__role {
flex: 0 1 110px;
text-align: right;
}
&__action {
flex: 0 1 110px;
padding-left: $large-spacing;
}
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<div>
<h1 data-cy="join-form-title">{{ title }}</h1>
<div>
<div class="skillboxform-input">
<label
for="join-code"
class="skillboxform-input__label">{{ labelText }}</label>
<input
:class="{'skillboxform-input__input--error': error}"
:value="value"
class="skillbox-input skillboxform-input__input"
data-cy="input-form-code"
id="join-code"
@input="$emit('input', $event)">
<small
class="skillboxform-input__error"
v-if="error"
>{{ error }}
</small>
</div>
<div>
<a
class="button button--primary button--big"
data-cy="join-form-confirm"
@click="$emit('confirm', value)">{{ okText }}</a>
<button
class="button button--big"
data-cy="join-form-cancel"
@click="$emit('cancel')">{{ cancelText }}
</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: ''
},
value: {
type: String,
default: ''
},
error: {
type: String,
default: ''
},
okText: {
type: String,
default: 'Klasse beitreten'
},
labelText: {
type: String,
default: 'Zugangscode eingeben'
},
cancelText: {
type: String,
default: 'Abmelden'
}
}
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
</style>

View File

@ -21,7 +21,7 @@
<script>
import UPDATE_AVATAR_QUERY from '@/graphql/gql/mutations/updateAvatarUrl.gql';
import ME_QUERY from '@/graphql/gql/meQuery.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import AvatarUploadForm from '@/components/profile/AvatarUploadForm';
import Avatar from '@/components/profile/Avatar';
import TrashIcon from '@/components/icons/TrashIcon';

View File

@ -19,6 +19,15 @@
class="profile-sidebar__link">Meine Aktivitäten
</router-link>
</div>
<div
class="profile-sidebar__item"
v-if="me.isTeacher"
@click="close">
<router-link
:to="myTeamPage"
class="profile-sidebar__link">Mein Team
</router-link>
</div>
</div>
<div class="profile-sidebar__section">
<div class="profile-sidebar__item">
@ -56,31 +65,40 @@
import ClassSelectionWidget from '@/components/school-class/ClassSelectionWidget';
import sidebarMixin from '@/mixins/sidebar';
import sidebar from '@/mixins/sidebar';
import me from '@/mixins/me';
import LogoutWidget from '@/components/LogoutWidget';
import {MY_TEAM} from '@/router/me.names';
export default {
mixins: [sidebarMixin],
mixins: [sidebar, me],
components: {
LogoutWidget,
ClassSelectionWidget,
ProfileWidget,
Cross
Cross,
},
computed: {
myTeamPage() {
return {
name: MY_TEAM,
};
},
},
methods: {
close() {
this.closeSidebar('profile');
}
},
},
};
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
@import "~styles/helpers";
$desktop-width: 333px;

View File

@ -0,0 +1,42 @@
<template>
<div class="show-code">
<h2 class="show-code__title">Zugangscode {{ type }} {{ name }}</h2>
<h1 class="show-code__code">{{ code }}</h1>
</div>
</template>
<script>
export default {
props: {
code: {
type: String,
default: '',
},
type: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
},
};
</script>
<style scoped lang="scss">
@import '~styles/helpers';
.show-code {
&__title {
@include regular-text;
margin-bottom: 2*$large-spacing;
}
&__code {
font-size: toRem(120px);
letter-spacing: toRem(20px);
font-weight: 600;
}
}
</style>

View File

@ -20,7 +20,7 @@
import WidgetPopover from '@/components/WidgetPopover';
import DELETE_ROOM_MUTATION from '@/graphql/gql/mutations/deleteRoom.gql';
import ROOMS_QUERY from '@/graphql/gql/roomsQuery.gql';
import ROOMS_QUERY from '@/graphql/gql/queries/roomsQuery.gql';
export default {
props: ['id'],

View File

@ -34,8 +34,8 @@
<script>
import DELETE_ROOM_ENTRY_MUTATION from '@/graphql/gql/mutations/deleteRoomEntry.gql';
import ROOM_ENTRIES_QUERY from '@/graphql/gql/roomEntriesQuery.gql';
import ME_QUERY from '@/graphql/gql/meQuery.gql';
import ROOM_ENTRIES_QUERY from '@/graphql/gql/queries/roomEntriesQuery.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import UserMetaWidget from '@/components/UserMetaWidget';
import MoreOptionsWidget from '@/components/MoreOptionsWidget';

View File

@ -57,7 +57,7 @@
import PageForm from '@/components/page-form/PageForm';
import PageFormInput from '@/components/page-form/PageFormInput';
import ME_QUERY from '@/graphql/gql/meQuery.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
export default {
props: ['room'],

View File

@ -11,7 +11,7 @@
<script>
import UPDATE_ROOM_ENTRY_MUTATION from '@/graphql/gql/mutations/updateRoomEntry.gql';
import ROOM_ENTRY_QUERY from '@/graphql/gql/roomEntryByIdQuery.gql';
import ROOM_ENTRY_QUERY from '@/graphql/gql/queries/roomEntryByIdQuery.gql';
import ContentsForm from '@/components/content-block-form/ContentsForm';

View File

@ -11,7 +11,7 @@
<script>
import NEW_ROOM_ENTRY_MUTATION from '@/graphql/gql/mutations/addRoomEntry.gql';
import ROOM_ENTRIES_QUERY from '@/graphql/gql/roomEntriesQuery.gql';
import ROOM_ENTRIES_QUERY from '@/graphql/gql/queries/roomEntriesQuery.gql';
import ContentsForm from '@/components/content-block-form/ContentsForm';

View File

@ -1,39 +1,20 @@
<template>
<modal
:hide-header="false"
:small="true"
title="Hello">
<h4 slot="header">Klasse bearbeiten</h4>
<modal-input
:value="name"
placeholder="Klassenname"
data-cy="edit-class-name-input"
@input="name = $event"
/>
<div slot="footer">
<a
class="button button--primary"
data-cy="modal-save-button"
@click="save">Speichern</a>
<a
class="button"
@click="hide">Abbrechen</a>
</div>
</modal>
<edit-name-wizard
:name="name"
type="Klasse"
@input="name = $event"
@cancel="hideModal"
@save="save" />
</template>
<script>
import Modal from '@/components/Modal';
import ModalInput from '@/components/ModalInput';
import MY_SCHOOL_CLASS_QUERY from '@/graphql/gql/mySchoolClass.gql';
import MY_SCHOOL_CLASS_QUERY from '@/graphql/gql/queries/mySchoolClass.gql';
import UPDATE_SCHOOL_CLASS_MUTATION from '@/graphql/gql/mutations/updateSchoolClass.gql';
import EditNameWizard from '@/components/profile/EditNameWizard';
export default {
components: {
Modal,
ModalInput
EditNameWizard,
},
data() {
@ -63,9 +44,9 @@
store.writeQuery({query, data});
}
});
this.hide();
this.hideModal();
},
hide() {
hideModal() {
this.$store.dispatch('hideModal');
}
},

View File

@ -1,7 +0,0 @@
# to be read from cache only
#import "./fragments/contentBlockParts.gql"
query ContentBlockQuery {
contentBlocks {
...ContentBlockParts
}
}

View File

@ -1,6 +0,0 @@
#import "./fragments/chapterParts.gql"
query ChapterQuery($id: ID!) {
chapter(id: $id) {
...ChapterParts
}
}

View File

@ -1,6 +0,0 @@
#import "./fragments/contentBlockParts.gql"
query ContentBlockQuery($id: ID!) {
contentBlock(id: $id) {
...ContentBlockParts
}
}

View File

@ -1,4 +1,3 @@
#import "./contentBlockParts.gql"
fragment ChapterParts on ChapterNode {
id
title
@ -9,27 +8,12 @@ fragment ChapterParts on ChapterNode {
text
}
}
contentBlocks {
edges {
node {
...ContentBlockParts
}
}
}
titleHiddenFor {
edges {
node {
id
name
}
}
id
name
}
descriptionHiddenFor {
edges {
node {
id
name
}
}
id
name
}
}

View File

@ -0,0 +1,6 @@
fragment ContentBlockInterfaceParts on ContentBlockInterface {
title
type
contents
__typename
}

View File

@ -1,9 +1,6 @@
fragment ContentBlockParts on ContentBlockNode {
id
slug
title
type
contents
userCreated
mine
bookmarks {
@ -14,19 +11,12 @@ fragment ContentBlockParts on ContentBlockNode {
}
}
hiddenFor {
edges {
node {
id
name
}
}
id
name
}
visibleFor {
edges {
node {
id
name
}
}
id
name
}
}

View File

@ -3,11 +3,7 @@ fragment ObjectiveGroupParts on ObjectiveGroupNode {
title
displayTitle
hiddenFor {
edges {
node {
id
name
}
}
id
name
}
}

View File

@ -4,19 +4,11 @@ fragment ObjectiveParts on ObjectiveNode {
mine
userCreated
hiddenFor {
edges {
node {
id
name
}
}
id
name
}
visibleFor {
edges {
node {
id
name
}
}
id
name
}
}

View File

@ -0,0 +1,11 @@
fragment TeamParts on TeamNode {
name
code
id
members {
firstName
lastName
id
isMe
}
}

View File

@ -1,6 +1,6 @@
#import "./schoolClassParts.gql"
#import "./moduleParts.gql"
fragment UserParts on UserNode {
fragment UserParts on PrivateUserNode {
id
pk
username

View File

@ -1,9 +0,0 @@
#import "./fragments/userParts.gql"
query MeQuery {
me {
...UserParts
isTeacher
permissions
onboardingVisited
}
}

View File

@ -1,24 +0,0 @@
#import "./fragments/moduleParts.gql"
query ModuleQuery($id: ID!) {
module(id: $id) {
...ModuleParts
chapters {
edges {
node {
id
contentBlocks {
edges {
node {
id
slug
title
type
contents
}
}
}
}
}
}
}
}

View File

@ -1,38 +0,0 @@
#import "./fragments/chapterParts.gql"
#import "./fragments/assignmentParts.gql"
#import "./fragments/objectiveGroupParts.gql"
#import "./fragments/objectiveParts.gql"
#import "./fragments/moduleParts.gql"
query ModulesQuery($slug: String!) {
module(slug: $slug) {
...ModuleParts
assignments {
edges {
node {
...AssignmentParts
}
}
}
objectiveGroups {
edges {
node {
...ObjectiveGroupParts
objectives {
edges {
node {
...ObjectiveParts
}
}
}
}
}
}
chapters {
edges {
node {
...ChapterParts
}
}
}
}
}

View File

@ -1,9 +1,11 @@
#import "../fragments/contentBlockInterfaceParts.gql"
#import "../fragments/contentBlockParts.gql"
mutation AddContentBlock($input: AddContentBlockInput!) {
addContentBlock(input: $input) {
newContentBlock {
...ContentBlockParts
...ContentBlockInterfaceParts
}
errors
clientMutationId

View File

@ -0,0 +1,11 @@
mutation CreateSnapshot($input: CreateSnapshotInput!) {
createSnapshot(input: $input) {
snapshot {
id
title
created
creator
}
success
}
}

View File

@ -0,0 +1,9 @@
#import "gql/fragments/teamParts.gql"
mutation CreateTeamMutation($input: CreateTeamInput!) {
createTeam(input: $input) {
success
team {
...TeamParts
}
}
}

View File

@ -0,0 +1,9 @@
#import "gql/fragments/teamParts.gql"
mutation JoinTeamMutation($input: JoinTeamInput!) {
joinTeam(input: $input) {
success
team {
...TeamParts
}
}
}

View File

@ -0,0 +1,5 @@
mutation LeaveTeam {
leaveTeam {
success
}
}

View File

@ -1,9 +1,11 @@
#import "../fragments/contentBlockInterfaceParts.gql"
#import "../fragments/contentBlockParts.gql"
mutation MutateContentBlock($input: MutateContentBlockInput!) {
mutateContentBlock(input: $input) {
contentBlock {
...ContentBlockParts
...ContentBlockInterfaceParts
}
}
}

View File

@ -0,0 +1,28 @@
#import "gql/fragments/objectiveGroupParts.gql"
#import "gql/fragments/objectiveParts.gql"
#import "gql/fragments/contentBlockInterfaceParts.gql"
#import "gql/fragments/contentBlockParts.gql"
#import "gql/fragments/moduleParts.gql"
#import "gql/fragments/chapterParts.gql"
mutation ApplySnapshot($input: ApplySnapshotInput!) {
applySnapshot(input: $input) {
success
module {
...ModuleParts
objectiveGroups {
...ObjectiveGroupParts
objectives {
...ObjectiveParts
}
}
chapters {
...ChapterParts
contentBlocks {
...ContentBlockInterfaceParts
...ContentBlockParts
}
}
}
}
}

View File

@ -0,0 +1,9 @@
mutation ShareSnapshot($input: ShareSnapshotInput!) {
shareSnapshot(input: $input) {
success
snapshot {
id
shared
}
}
}

View File

@ -1,8 +1,15 @@
#import "../fragments/chapterParts.gql"
#import "../fragments/contentBlockInterfaceParts.gql"
#import "../fragments/contentBlockParts.gql"
mutation UpdateChapterVisibility($input: UpdateChapterVisibilityInput!) {
updateChapterVisibility(input: $input) {
chapter {
...ChapterParts
contentBlocks {
...ContentBlockParts
...ContentBlockInterfaceParts
}
}
}
}

View File

@ -0,0 +1,9 @@
mutation UpdateTeam($input: UpdateTeamInput!) {
updateTeam(input: $input) {
success
team {
id
name
}
}
}

View File

@ -1,4 +1,4 @@
#import "./fragments/moduleParts.gql"
#import "../fragments/moduleParts.gql"
query ModulesQuery {
modules {
edges {

View File

@ -1,4 +1,4 @@
#import "./fragments/projectParts.gql"
#import "../fragments/projectParts.gql"
query ProjectsQuery {
projects {
edges {

View File

@ -1,4 +1,4 @@
#import "./fragments/assignmentParts.gql"
#import "../fragments/assignmentParts.gql"
query AssignmentQuery($id: ID!) {
assignment(id: $id) {
...AssignmentParts

View File

@ -1,4 +1,4 @@
#import "./fragments/schoolClassParts.gql"
#import "../fragments/schoolClassParts.gql"
query AssignmentWithSubmissions($id: ID!) {
assignment(id: $id) {
title

Some files were not shown because too many files have changed in this diff Show More