Merged in develop (pull request #68)

Develop
This commit is contained in:
Ramon Wenger 2020-06-22 13:42:06 +00:00
commit 13fe4d589e
360 changed files with 9499 additions and 3735 deletions

View File

@ -19,7 +19,7 @@ django = "<3"
whitenoise = "==4.0b4"
psycopg2 = "==2.7.3.2"
gunicorn = "==19.7.1"
python-dotenv = "==0.7.1"
python-dotenv = "==0.13.0"
dj-database-url = "==0.4.1"
raven = "==6.9.0"
django-extensions = "==1.9.8"
@ -27,7 +27,7 @@ graphene-django = "==2.2.0"
django-filter = "==2.0.0"
djangorestframework = "==3.8.2"
pillow = "==5.0.0"
wagtail = "==2.4"
wagtail = "==2.5"
django-cors-headers = "==2.2.0"
django-storages = "*"
boto3 = "*"

322
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "f7bfa448c4413cfd5d7517793e7095aa2f0e072f570b32281c5f573aba34552d"
"sha256": "886f06e25f5fffc2690a1937d3e7994ad68438a416347568831ebbbfb965387c"
},
"pipfile-spec": 6,
"requires": {
@ -40,33 +40,33 @@
},
"bleach": {
"hashes": [
"sha256:cc8da25076a1fe56c3ac63671e2194458e0c4d9c7becfd52ca251650d517903c",
"sha256:e78e426105ac07026ba098f04de8abe9b6e3e98b5befbf89b51a5ef0a4292b03"
"sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f",
"sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"
],
"index": "pypi",
"version": "==3.1.4"
"version": "==3.1.5"
},
"boto3": {
"hashes": [
"sha256:5246caf509baa4716065e6bb78bdc516fdd6b0dfbd9098cc2a0f779fad789c6c",
"sha256:52b8de35f6647e3b8ce81f6a745a67812623b5c4acc2d6bd5e814fddfa488321"
"sha256:009d0b483513e4c8639895c2b8dc451b41c9b863116d0234506b8b88b30a3d1b",
"sha256:f5218afc43c4c21ae2e8a1d4620d27ab6765250fe1f913ab234fbb686de87ae1"
],
"index": "pypi",
"version": "==1.12.34"
"version": "==1.13.17"
},
"botocore": {
"hashes": [
"sha256:c799623598d04c66b0be4cb990c01a24bd3c06023f0c7221adead38a7431c994",
"sha256:db0fba3f4adfb9bf3976aae10efa9acb59e3efe52ce8feac71ecac0b7be2fc2e"
"sha256:cca04cd4bdb092a727772c38808f97e15f07dc609f6fbd7d7ba09c7734058794",
"sha256:f9627c718d480225cbfeeeb7b4a694b9cea3cae67940a4b673770cfaca328a81"
],
"version": "==1.15.34"
"version": "==1.16.17"
},
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
],
"version": "==2019.11.28"
"version": "==2020.4.5.1"
},
"chardet": {
"hashes": [
@ -75,13 +75,6 @@
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
],
"version": "==7.1.1"
},
"decorator": {
"hashes": [
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760",
@ -98,11 +91,11 @@
},
"django": {
"hashes": [
"sha256:48522428f4a285cf265af969f4744c5ebb027c7f41958ba48b639ace2068ffe7",
"sha256:a794f7a2f4b7c928eecfbc4ebad03712ff27fb545abe269bf01aa8500781eb1c"
"sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a",
"sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916"
],
"index": "pypi",
"version": "==2.1.15"
"version": "==2.2.12"
},
"django-appconf": {
"hashes": [
@ -176,10 +169,10 @@
},
"django-taggit": {
"hashes": [
"sha256:a21cbe7e0879f1364eef1c88a2eda89d593bf000ebf51c3f00423c6927075dce",
"sha256:db4430ec99265341e05d0274edb0279163bd74357241f7b4d9274bdcb3338b17"
"sha256:710b4d15ec1996550cc68a0abbc41903ca7d832540e52b1336e6858737e410d8",
"sha256:bb8f27684814cd1414b2af75b857b5e26a40912631904038a7ecacd2bfafc3ac"
],
"version": "==0.23.0"
"version": "==0.24.0"
},
"django-treebeard": {
"hashes": [
@ -219,10 +212,10 @@
},
"faker": {
"hashes": [
"sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f",
"sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976"
"sha256:103c46b9701a151299c5bffe6fefcd4fb5fb04c3b5d06bee4952d36255d44ea2",
"sha256:34ae397aef03a0a17910452f1e8430d57fa59e2d67b20e9b637218e8f7dd22b3"
],
"version": "==4.0.2"
"version": "==4.1.0"
},
"future": {
"hashes": [
@ -247,10 +240,10 @@
},
"graphql-core": {
"hashes": [
"sha256:74a8f509ae8c4a58271f5d6b46d5c75b4aed116821ab62dea252d8041bfe057a",
"sha256:c06e59153246dd48ddbf86ca94a8f045c1b71daf8154b4635e1a0e2e11d9b60b"
"sha256:44c9bac4514e5e30c5a595fac8e3c76c1975cae14db215e8174c7fe995825bad",
"sha256:aac46a9ac524c9855910c14c48fc5d60474def7f99fd10245e76608eba7af746"
],
"version": "==2.3.1"
"version": "==2.3.2"
},
"graphql-relay": {
"hashes": [
@ -283,11 +276,11 @@
},
"ipython": {
"hashes": [
"sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a",
"sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333"
"sha256:5b241b84bbf0eb085d43ae9d46adf38a13b45929ca7774a740990c2c242534bb",
"sha256:f0126781d0f959da852fb3089e170ed807388e986a8dd4e6ac44855845b0fb1c"
],
"index": "pypi",
"version": "==7.13.0"
"version": "==7.14.0"
},
"ipython-genutils": {
"hashes": [
@ -298,54 +291,56 @@
},
"jedi": {
"hashes": [
"sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2",
"sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"
"sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798",
"sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030"
],
"version": "==0.16.0"
"version": "==0.17.0"
},
"jmespath": {
"hashes": [
"sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec",
"sha256:cca55c8d153173e21baa59983015ad0daf603f9cb799904ff057bfb8ff8dc2d9"
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"version": "==0.9.5"
"version": "==0.10.0"
},
"libsass": {
"hashes": [
"sha256:003a65b4facb4c5dbace53fb0f70f61c5aae056a04b4d112a198c3c9674b31f2",
"sha256:0fb4399f7bbecab7b181f2c2d82c3a0ba2916bf9169714b96e425355a5b23b9f",
"sha256:0fd8b4337b3b101c6e6afda9112cc0dc4bacb9133b59d75d65968c7317aa3272",
"sha256:338e9ae066bf1fde874e335324d5355c52d2081d978b4f74fc59536564b35b08",
"sha256:4dcfd561fb100250b89496e1362b96f2cc804f689a59731eb0f94f9a9e144f4a",
"sha256:50778d4be269a021ba2bf42b5b8f6ff3704ab96a82175a052680bddf3ba7cc9f",
"sha256:53f87116e7441827878bd79bbad8debac23e1930423f61ab8d837ec4a4c36e0c",
"sha256:6a51393d75f6e3c812785b0fa0b7d67c54258c28011921f204643b55f7355ec0",
"sha256:74acd9adf506142699dfa292f0e569fdccbd9e7cf619e8226f7117de73566e32",
"sha256:81a013a4c2a614927fd1ef7a386eddabbba695cbb02defe8f31cf495106e974c",
"sha256:845a9573b25c141164972d498855f4ad29367c09e6d76fad12955ad0e1c83013",
"sha256:8b5b6d1a7c4ea1d954e0982b04474cc076286493f6af2d0a13c2e950fbe0be95",
"sha256:9b59afa0d755089c4165516400a39a289b796b5612eeef5736ab7a1ebf96a67c",
"sha256:a7e685466448c9b1bf98243339793978f654a1151eb5c975f09b83c7a226f4c1",
"sha256:c93df526eeef90b1ea4799c1d33b6cd5aea3e9f4633738fb95c1287c13e6b404",
"sha256:e318f06f06847ff49b1f8d086ac9ebce1e63404f7ea329adab92f4f16ba0e00e",
"sha256:fc5f8336750f76f1bfae82f7e9e89ae71438d26fc4597e3ab4c05ca8fcd41d8a",
"sha256:fcb7ab4dc81889e5fc99cafbc2017bc76996f9992fc6b175f7a80edac61d71df"
"sha256:107c409524c6a4ed14410fa9dafa9ee59c6bd3ecae75d73af749ab2b75685726",
"sha256:3bc0d68778b30b5fa83199e18795314f64b26ca5871e026343e63934f616f7f7",
"sha256:5c8ff562b233734fbc72b23bb862cc6a6f70b1e9bf85a58422aa75108b94783b",
"sha256:74f6fb8da58179b5d86586bc045c16d93d55074bc7bb48b6354a4da7ac9f9dfd",
"sha256:7555d9b24e79943cfafac44dbb4ca7e62105c038de7c6b999838c9ff7b88645d",
"sha256:794f4f4661667263e7feafe5cc866e3746c7c8a9192b2aa9afffdadcbc91c687",
"sha256:8cf72552b39e78a1852132e16b706406bc76029fe3001583284ece8d8752a60a",
"sha256:98f6dee9850b29e62977a963e3beb3cfeb98b128a267d59d2c3d675e298c8d57",
"sha256:a43f3830d83ad9a7f5013c05ce239ca71744d0780dad906587302ac5257bce60",
"sha256:b077261a04ba1c213e932943208471972c5230222acb7fa97373e55a40872cbb",
"sha256:b7452f1df274b166dc22ee2e9154c4adca619bcbbdf8041a7aa05f372a1dacbc",
"sha256:e6a547c0aa731dcb4ed71f198e814bee0400ce04d553f3f12a53bc3a17f2a481",
"sha256:fd19c8f73f70ffc6cbcca8139da08ea9a71fc48e7dfc4bb236ad88ab2d6558f1"
],
"version": "==0.19.4"
"version": "==0.20.0"
},
"newrelic": {
"hashes": [
"sha256:5d83887781683975bd75ec624baa8de5e1e75c2bf89e7269ed2811b559da1504"
"sha256:43dbaf5227b647c1b4b206064fc3367ac14f7b99ce38d11d50e82d49d21ccb76"
],
"index": "pypi",
"version": "==5.10.0.138"
"version": "==5.12.1.141"
},
"packaging": {
"hashes": [
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
"version": "==20.4"
},
"parso": {
"hashes": [
"sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157",
"sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"
"sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0",
"sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"
],
"version": "==0.6.2"
"version": "==0.7.0"
},
"pexpect": {
"hashes": [
@ -473,6 +468,13 @@
],
"version": "==2.6.1"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"version": "==2.4.7"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
@ -482,11 +484,11 @@
},
"python-dotenv": {
"hashes": [
"sha256:45e927c34204c90f5faa35ea8709b894f6b1a7712d77eb50940601068040993b",
"sha256:dc7940052cfe170e881aea40feb4ea7776e6a97170ed038fd2ee7e26e47585f2"
"sha256:25c0ff1a3e12f4bde8d592cc254ab075cfe734fc5dd989036716fd17ee7e5ec7",
"sha256:3b9909bc96b0edc6b01586e1eed05e71174ef4e04c71da5786370cebea53ad74"
],
"index": "pypi",
"version": "==0.7.1"
"version": "==0.13.0"
},
"python-http-client": {
"hashes": [
@ -499,10 +501,10 @@
},
"pytz": {
"hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
"sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
],
"version": "==2019.3"
"version": "==2020.1"
},
"raven": {
"hashes": [
@ -560,11 +562,10 @@
},
"sendgrid": {
"hashes": [
"sha256:2954caf82c94c3566147e78ff7772f0883a7cc668de5a1c5ff1c49dcb9c1af31",
"sha256:7c606125c59d1df7354e20581ca645d109be1d026f5b01dd62112e07348b2d87",
"sha256:a58c8d3f99e1b4fed6c041e6bb0e30893a6c40ffc0ecd33430a08d044fe78832"
"sha256:38c0853494c7bfbef64f33934c25bf98bb7648cf0e66a0cfb22410927e4ef4c7",
"sha256:838e1f7b0f84d56714be6a18ef66fbcf8fba0bda782eee35c47c04d7a16efde8"
],
"version": "==6.2.1"
"version": "==6.3.1"
},
"sentry-sdk": {
"hashes": [
@ -583,10 +584,17 @@
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"version": "==1.14.0"
"version": "==1.15.0"
},
"sqlparse": {
"hashes": [
"sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e",
"sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548"
],
"version": "==0.3.1"
},
"text-unidecode": {
"hashes": [
@ -627,18 +635,18 @@
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
],
"version": "==1.25.8"
"version": "==1.25.9"
},
"wagtail": {
"hashes": [
"sha256:1dd069ef8b3a08728fa5304e45ee2a92ed49b8e235e9c8b677adfd2039cff1fd",
"sha256:fa588695932178667f58347fb77cc85a34be963ccceafa818e0d7f9998c2cdf3"
"sha256:0b1357bc66873d1c3403791e717a15c8b01d3d0e67083fe5f9eff60d7f7d9d77",
"sha256:19de8dd6390429c1c6e921487e997a64fa9334da0c6255f1e9988d30729fce1a"
],
"index": "pypi",
"version": "==2.4"
"version": "==2.5"
},
"wagtail-factories": {
"hashes": [
@ -688,17 +696,17 @@
},
"autopep8": {
"hashes": [
"sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43"
"sha256:152fd8fe47d02082be86e05001ec23d6f420086db56b17fc883f3f965fb34954"
],
"version": "==1.5"
"version": "==1.5.2"
},
"awscli": {
"hashes": [
"sha256:b713641674a228959e4030cab578d2720d1132f40bed15f0c5cd950525b559c9",
"sha256:c0b75242000ed03152e30cee50a61d571019da27a5862d520e758f03842c206d"
"sha256:23a65b27b732b00ff42162b03334771f54ff84ade91cff504f870cb931e67a95",
"sha256:d853d0d81ef17eb02f9703dca9f221beee0efe8edbb7acb4ec7e2ffdef792eb3"
],
"index": "pypi",
"version": "==1.18.34"
"version": "==1.18.67"
},
"backcall": {
"hashes": [
@ -709,17 +717,17 @@
},
"botocore": {
"hashes": [
"sha256:c799623598d04c66b0be4cb990c01a24bd3c06023f0c7221adead38a7431c994",
"sha256:db0fba3f4adfb9bf3976aae10efa9acb59e3efe52ce8feac71ecac0b7be2fc2e"
"sha256:cca04cd4bdb092a727772c38808f97e15f07dc609f6fbd7d7ba09c7734058794",
"sha256:f9627c718d480225cbfeeeb7b4a694b9cea3cae67940a4b673770cfaca328a81"
],
"version": "==1.15.34"
"version": "==1.16.17"
},
"certifi": {
"hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
],
"version": "==2019.11.28"
"version": "==2020.4.5.1"
},
"chardet": {
"hashes": [
@ -738,40 +746,40 @@
},
"coverage": {
"hashes": [
"sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0",
"sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30",
"sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b",
"sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0",
"sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823",
"sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe",
"sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037",
"sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6",
"sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31",
"sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd",
"sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892",
"sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1",
"sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78",
"sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac",
"sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006",
"sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014",
"sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2",
"sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7",
"sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8",
"sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7",
"sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9",
"sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1",
"sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307",
"sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a",
"sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435",
"sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0",
"sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5",
"sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441",
"sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732",
"sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de",
"sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"
"sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a",
"sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355",
"sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65",
"sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7",
"sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9",
"sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1",
"sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0",
"sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55",
"sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c",
"sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6",
"sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef",
"sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019",
"sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e",
"sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0",
"sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf",
"sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24",
"sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2",
"sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c",
"sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4",
"sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0",
"sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd",
"sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04",
"sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e",
"sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730",
"sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2",
"sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768",
"sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796",
"sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7",
"sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a",
"sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489",
"sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"
],
"index": "pypi",
"version": "==5.0.4"
"version": "==5.1"
},
"decorator": {
"hashes": [
@ -782,11 +790,11 @@
},
"django": {
"hashes": [
"sha256:48522428f4a285cf265af969f4744c5ebb027c7f41958ba48b639ace2068ffe7",
"sha256:a794f7a2f4b7c928eecfbc4ebad03712ff27fb545abe269bf01aa8500781eb1c"
"sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a",
"sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916"
],
"index": "pypi",
"version": "==2.1.15"
"version": "==2.2.12"
},
"django-silk": {
"hashes": [
@ -825,11 +833,11 @@
},
"ipython": {
"hashes": [
"sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a",
"sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333"
"sha256:5b241b84bbf0eb085d43ae9d46adf38a13b45929ca7774a740990c2c242534bb",
"sha256:f0126781d0f959da852fb3089e170ed807388e986a8dd4e6ac44855845b0fb1c"
],
"index": "pypi",
"version": "==7.13.0"
"version": "==7.14.0"
},
"ipython-genutils": {
"hashes": [
@ -840,24 +848,24 @@
},
"jedi": {
"hashes": [
"sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2",
"sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"
"sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798",
"sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030"
],
"version": "==0.16.0"
"version": "==0.17.0"
},
"jinja2": {
"hashes": [
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
"version": "==2.11.1"
"version": "==2.11.2"
},
"jmespath": {
"hashes": [
"sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec",
"sha256:cca55c8d153173e21baa59983015ad0daf603f9cb799904ff057bfb8ff8dc2d9"
"sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9",
"sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"
],
"version": "==0.9.5"
"version": "==0.10.0"
},
"markupsafe": {
"hashes": [
@ -899,10 +907,10 @@
},
"parso": {
"hashes": [
"sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157",
"sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"
"sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0",
"sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"
],
"version": "==0.6.2"
"version": "==0.7.0"
},
"pexpect": {
"hashes": [
@ -953,10 +961,10 @@
},
"pycodestyle": {
"hashes": [
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
"version": "==2.5.0"
"version": "==2.6.0"
},
"pygments": {
"hashes": [
@ -974,10 +982,10 @@
},
"pytz": {
"hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
"sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
],
"version": "==2019.3"
"version": "==2020.1"
},
"pyyaml": {
"hashes": [
@ -1020,10 +1028,10 @@
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
"version": "==1.14.0"
"version": "==1.15.0"
},
"sqlparse": {
"hashes": [
@ -1041,10 +1049,10 @@
},
"urllib3": {
"hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
],
"version": "==1.25.8"
"version": "==1.25.9"
},
"wcwidth": {
"hashes": [

View File

@ -18,6 +18,13 @@ definitions:
POSTGRES_HOST_AUTH_METHOD: trust
aliases:
- &lint
name: lint
caches:
- node
script:
- npm install --prefix client
- npm run lint --prefix client
- &unittest-python
name: run python unit tests
caches:
@ -78,6 +85,7 @@ aliases:
pipelines:
default:
- step: *lint
- step: *unittest-python
- step: *cypress-test
- step: *jest-test
@ -91,6 +99,7 @@ pipelines:
- step: *deploy-prod-manual
develop:
- step: *lint
- step: *unittest-python
- step: *cypress-test
- step: *jest-test

View File

@ -11,7 +11,8 @@ module.exports = {
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
'plugin:vue/recommended',
// 'plugin:vue/recommended',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
@ -29,5 +30,46 @@ module.exports = {
'semi': 0,
'space-before-function-paren': 'off',
'comma-dangle': 'off',
// vue rules
'vue/require-prop-types': 'off', //todo: should we do this?
'vue/require-default-prop': 'off', //todo: should we do this?
'vue/attributes-order': ['error', {
'order': [
'OTHER_ATTR',
'DEFINITION',
'LIST_RENDERING',
'CONDITIONALS',
'RENDER_MODIFIERS',
'GLOBAL',
'UNIQUE',
'TWO_WAY_BINDING',
'OTHER_DIRECTIVES',
'EVENTS',
'CONTENT'
]
}],
'vue/order-in-components': ['error', {
'order': [
'el',
'name',
'parent',
'functional',
['delimiters', 'comments'],
['props', 'propsData'],
'mixins',
['components', 'directives', 'filters'],
'data',
'extends',
'inheritAttrs',
'model',
'computed',
'watch',
'LIFECYCLE_HOOKS',
'methods',
['template', 'render'],
'renderError'
]
}]
}
};

View File

@ -4,6 +4,7 @@ const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
HEP_URL: JSON.stringify(process.env.HEP_URL),
MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST),
MATOMO_SITE_ID: JSON.stringify(process.env.MATOMO_SITE_ID),
});

View File

@ -1,6 +1,7 @@
'use strict'
module.exports = {
NODE_ENV: '"production"',
HEP_URL: JSON.stringify(process.env.HEP_URL),
MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST),
MATOMO_SITE_ID: JSON.stringify(process.env.MATOMO_SITE_ID),
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,8 +2,7 @@ describe('The Login Page', () => {
it('login and redirect to main page', () => {
const username = 'test';
const password = 'test';
cy.visit('/');
cy.visit('/beta-login');
cy.login(username, password, true);
cy.get('body').contains('Neues Wissen erwerben');
});
@ -12,7 +11,7 @@ describe('The Login Page', () => {
const username = '';
const password = 'test';
cy.visit('/');
cy.visit('/beta-login');
cy.login(username, password);
cy.get('[data-cy=email-local-errors]').contains('E-Mail ist ein Pflichtfeld');
});
@ -21,7 +20,7 @@ describe('The Login Page', () => {
const username = 'test';
const password = '';
cy.visit('/');
cy.visit('/beta-login');
cy.login(username, password);
cy.get('[data-cy=password-local-errors]').contains('Passwort ist ein Pflichtfeld');
});
@ -30,23 +29,18 @@ describe('The Login Page', () => {
const username = 'test';
const password = '12345';
cy.visit('/');
cy.visit('/beta-login');
cy.login(username, password);
cy.get('[data-cy=login-error]').contains('Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.');
});
it('redirect after login', () => {
const username = 'test';
const password = 'test';
it('logs out then logs in again', () => {
cy.visit('/book/topic/berufliche-grundbildung');
cy.login(username, password);
cy.get('body').contains('Berufliche Grundbildung');
});
const user = 'rahel.cueni';
const pw = 'test'
it.only('logs out then logs in again', () => {
cy.viewport('macbook-15');
cy.apolloLogin('rahel.cueni', 'test');
cy.apolloLogin(user, pw);
cy.visit('/me/my-class');
cy.get('[data-cy=header-user-widget]').should('exist').within(() => {
cy.get('[data-cy=user-widget-avatar]').should('exist').click();
@ -54,7 +48,11 @@ describe('The Login Page', () => {
cy.get('[data-cy=logout]').click();
cy.login('rahel.cueni', 'test');
cy.get('[data-cy=email-input]').should('exist').within(() => {
cy.visit('/beta-login');
});
cy.login(user, pw);
cy.get('[data-cy=header-user-widget]').should('exist').within(() => {
cy.get('[data-cy=user-widget-avatar]').should('exist').click();

View File

@ -0,0 +1,76 @@
import { GraphQLError } from "graphql";
const schema = require('../fixtures/schema.json');
describe('Email Verifcation', () => {
beforeEach(() => {
cy.server();
});
it('forwards to homepage if confirmation key is correct', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
operations: {
Coupon: {
coupon: {
success: true
}
},
}
});
cy.login('rahel.cueni', 'test', true)
cy.get('[data-cy="rooms-link"]').contains('Alle Räume anzeigen');
cy.visit('/license-activation');
cy.redeemCoupon('12345asfd');
cy.get('body').contains('Neues Wissen erwerben');
});
it('displays error if input is missing', () => {
cy.viewport('macbook-15');
cy.login('rahel.cueni', 'test', true)
cy.get('[data-cy="rooms-link"]').contains('Alle Räume anzeigen');
cy.visit('/license-activation');
cy.redeemCoupon('');
cy.get('[data-cy="coupon-local-errors"]').contains('Coupon ist ein Pflichtfeld.');
});
it('displays error if coupon input is wrong', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
operations: {
Coupon: new GraphQLError('invalid_coupon')
}
});
cy.login('rahel.cueni', 'test', true)
cy.get('[data-cy="rooms-link"]').contains('Alle Räume anzeigen');
cy.visit('/license-activation');
cy.redeemCoupon('12345asfd');
cy.get('[data-cy="coupon-remote-errors"]').contains('Der angegebene Coupon-Code ist ungültig.');
});
it('displays error if an error occures', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
operations: {
Coupon: new GraphQLError("unknown_error")
}
});
cy.login('rahel.cueni', 'test', true)
cy.get('[data-cy="rooms-link"]').contains('Alle Räume anzeigen');
cy.visit('/license-activation');
cy.redeemCoupon('12345asfd');
cy.get('[data-cy="coupon-remote-errors"]').contains('Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.');
});
});

View File

@ -19,6 +19,11 @@ describe('Current Module', () => {
'slug': 'lohn-und-budget',
'__typename': 'ModuleNode'
},
'lastTopic': {
'id': 'VG9waWNOb2RlOjU=',
'slug': 'geld-und-kauf',
'__typename': 'TopicNode'
},
'__typename': 'UserNode',
'permissions': []
}

View File

@ -0,0 +1,94 @@
const schema = require('../fixtures/schema_public.json');
describe('Email Verifcation', () => {
beforeEach(() => {
cy.server();
});
it('forwards to homepage if confirmation key is correct', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
operations: {
Registration: {
registration: {
message: "success",
success: true
}
},
}
});
cy.visit('/verify-email?confirmation=abcd1234&id=12');
// user should be logged in at that stage. As the cookie cannot be set at the right time
// we just check if the user gets redirected to the login page as we can't log her in
cy.url().should('include', 'hello?redirect=%2F');
});
it('displays error if key is incorrect', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
// endpoint: '/api/graphql'
operations: {
Registration: {
registration: {
message: "invalid_key",
success: false
}
},
}
});
cy.visit('/verify-email?confirmation=abcd1234&id=12');
cy.get('[data-cy="code-nok-msg"]').contains('Der angegebene Verifizierungscode ist ungültig oder abgelaufen.');
cy.get('[data-cy="code-ok-msg"]').should('not.exist');
});
it('displays error if an error occured', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
// endpoint: '/api/graphql'
operations: {
Registration: {
registration: {
message: "unkown_error",
success: false
}
},
}
});
cy.visit('/verify-email?confirmation=abcd1234&id=12');
cy.get('[data-cy="code-nok-msg"]').contains('Ein Fehler ist aufgetreten. Bitte kontaktieren Sie den Administrator.');
});
it('forwards to coupon page if user has no valid license', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
// endpoint: '/api/graphql'
operations: {
Registration: {
registration: {
message: "no_valid_license",
success: false
}
},
}
});
cy.visit('/verify-email?confirmation=abcd1234&id=12');
// user should be logged in at that stage. As the cookie cannot be set at the right time
// we just check if the user gets redirected to the coupon page as we can't log her in
cy.url().should('include', 'hello?redirect=%2Flicense-activation');
});
});

View File

@ -1,9 +0,0 @@
describe('The Logged In Home Page', () => {
it('successfully loads', () => {
// todo: use graphql login
cy.visit('/');
cy.login('test', 'test');
cy.get('.block-title__title').should('contain', 'Inhalte')
})
})

View File

@ -0,0 +1,73 @@
const schema = require('../fixtures/schema_public.json');
const isEmailAvailableUrl = '**/rest/deutsch/V1/customers/isEmailAvailable';
const checkPasswordUrl = '**/rest/deutsch/V1/integration/customer/token';
describe('Login', () => {
beforeEach(() => {
cy.server();
});
it('works with valid email and password', () => {
cy.viewport('macbook-15');
cy.mockGraphql({
schema: schema,
operations: {
Login: variables => {
return {
login: {
errors: [],
message: "success",
success: true
}
}
},
}
});
cy.route('POST', isEmailAvailableUrl, 'false');
cy.route({
method: 'POST',
url: checkPasswordUrl,
response: 'token12345ABCD+',
});
cy.visit('/hello');
cy.checkEmailAvailable('feuz@aebi.ch');
cy.get('[data-cy="login-title"]').contains('Bitte geben Sie das passende Passwort ein');
cy.enterPassword('abcd1234');
// As we cannot set the cookie in the right manner, we just check for the absence of errors.
// In real world the user gets redirect to another page
cy.get('[data-cy="email-local-errors"]').should('not.exist');
});
it('displays error message if password is wrong', () => {
cy.viewport('macbook-15');
cy.route('POST', isEmailAvailableUrl, 'false');
cy.route({
method: 'POST',
status: 401,
response: {
message: "Sie haben sich nicht korrekt eingeloggt oder Ihr Konto ist vor\u00fcbergehend deaktiviert."
},
url: checkPasswordUrl
});
cy.visit('/hello');
cy.checkEmailAvailable('feuz@aebi.ch');
cy.get('[data-cy="login-title"]').contains('Bitte geben Sie das passende Passwort ein');
cy.enterPassword('abcd1234');
cy.get('[data-cy="password-errors"]').contains('Sie haben sich nicht korrekt eingeloggt oder Ihr Konto ist vorübergehend deaktiviert.');
});
it('displays error message if input is not an email address', () => {
cy.viewport('macbook-15');
cy.visit('/hello');
cy.checkEmailAvailable('feuzaebi.ch');
cy.get('[data-cy="email-local-errors"]').contains('Bitte geben Sie eine gülitge E-Mail an');
})
});

View File

@ -1,77 +0,0 @@
describe('The Regstration Page', () => {
// works locally, but not in pipelines.
// it('register user', () => {
// let timestamp = Math.round((new Date()).getTime() / 1000);
// const firstname = 'pesche';
// const lastname = 'peschemann';
// const email = `skillboxtest${timestamp}@iterativ.ch`;
// const licenseKey = 'c1fa2e2a-2e27-480d-8469-2e88414c4ad8';
// cy.visit('/register');
// cy.register(firstname, lastname, email, licenseKey);
// cy.get('.reset__heading').contains('Schauen Sie in Ihr Postfach');
// });
it('user sees error message if firstname is omitted', () => {
let timestamp = Math.round((new Date()).getTime() / 1000);
const firstname = '';
const lastname = 'peschemann';
const email = `skillboxtest${timestamp}@iterativ.ch`;
const licenseKey = 'c1fa2e2a-2e27-480d-8469-2e88414c4ad8';
cy.visit('/register');
cy.register(firstname, lastname, email, licenseKey);
cy.get('[data-cy="firstname-local-errors"]').contains('Vorname ist ein Pflichtfeld.');
});
it('user sees error message if lastname is omitted', () => {
let timestamp = Math.round((new Date()).getTime() / 1000);
const firstname = 'pesche';
const lastname = '';
const email = `skillboxtest${timestamp}@iterativ.ch`;
const licenseKey = 'c1fa2e2a-2e27-480d-8469-2e88414c4ad8';
cy.visit('/register');
cy.register(firstname, lastname, email, licenseKey);
cy.get('[data-cy="lastname-local-errors"]').contains('Nachname ist ein Pflichtfeld.');
});
it('user sees error message if email is omitted', () => {
let timestamp = Math.round((new Date()).getTime() / 1000);
const firstname = 'pesche';
const lastname = 'peschemann';
const email = ``;
const licenseKey = 'c1fa2e2a-2e27-480d-8469-2e88414c4ad8';
cy.visit('/register');
cy.register(firstname, lastname, email, licenseKey);
cy.get('[data-cy="email-local-errors"]').contains('E-Mail ist ein Pflichtfeld.');
});
it('user sees error message if license is omitted', () => {
let timestamp = Math.round((new Date()).getTime() / 1000);
const firstname = 'pesche';
const lastname = 'peschemann';
const email = `skillboxtest${timestamp}@iterativ.ch`;
const licenseKey = '';
cy.visit('/register');
cy.register(firstname, lastname, email, licenseKey);
cy.get('[data-cy="licenseKey-local-errors"]').contains('Lizenz ist ein Pflichtfeld.');
});
it('user sees error message if license key is wrong', () => {
let timestamp = Math.round((new Date()).getTime() / 1000);
const firstname = 'pesche';
const lastname = 'peschemann';
const email = `skillboxtest${timestamp}@iterativ.ch`;
const licenseKey = 'asdsafsadfsadfasdf';
cy.visit('/register');
cy.register(firstname, lastname, email, licenseKey);
cy.get('[data-cy="licenseKey-remote-errors"]').contains('Die angegebenen Lizenz ist unglültig');
});
})

View File

@ -0,0 +1,164 @@
const isEmailAvailableUrl = 'https://stage.hep-verlag.ch/rest/deutsch/V1/customers/isEmailAvailable';
const registerUrl = '/api/proxy/registration/';
let registrationResponse = {
id: 84215,
group_id: 1,
confirmation: "91cf39007547feae7e33778d89fc71db",
created_at: "2020-02-06 13:56:54",
updated_at: "2020-02-06 13:56:54",
created_in: "hep verlag",
email: "feuz@aebi.ch",
firstname: "Kari",
lastname: "Feuz",
prefix: "Herr",
gender: 1,
store_id: 1,
website_id: 1,
addresses: []
};
describe('Registration', () => {
beforeEach(() => {
cy.viewport('macbook-15');
cy.server();
});
// it('works with valid data', () => {
// cy.route('POST', isEmailAvailableUrl, "true");
// cy.route('POST', registerUrl, registrationResponse);
// cy.visit('/hello');
// cy.checkEmailAvailable(registrationResponse.email);
// cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
// cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd1234!', 'Abcd1234!', true);
// cy.get('[data-cy="email-check"]').contains('Eine Email ist auf dem Weg, bitte überprüfen sie ihre E-mail Konto.');
// });
it('displays error if firstname is missing', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, '', registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd1234!', 'Abcd1234!', true);
cy.get('[data-cy="firstname-local-errors"]').contains('Vorname ist ein Pflichtfeld');
});
it('displays error if lastname is missing', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, '', 'Weg 1', 'Bern', '3001', 'Abcd1234!', 'Abcd1234!', true);
cy.get('[data-cy="lastname-local-errors"]').contains('Nachname ist ein Pflichtfeld');
});
it('displays error if street is missing', () => {
cy.route('POST', isEmailAvailableUrl, 'true');
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, '', 'Bern', '3001', 'Abcd1234!', 'Abcd1234!', true);
cy.get('[data-cy="street-local-errors"]').contains('Strasse ist ein Pflichtfeld');
});
it('displays error if city is missing', () => {
cy.route('POST', isEmailAvailableUrl, 'true');
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', '', '3001', 'Abcd1234!', 'Abcd1234!', true);
cy.get('[data-cy="city-local-errors"]').contains('Ort ist ein Pflichtfeld');
});
it('displays error if postcode is missing', () => {
cy.route('POST', isEmailAvailableUrl, 'true');
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '', 'Abcd1234!', 'Abcd1234!', true);
cy.get('[data-cy="postcode-local-errors"]').contains('Postleitzahl ist ein Pflichtfeld');
});
it('displays error if password is missing', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', '', 'Abcd1234!', true);
cy.get('[data-cy="password-local-errors"]').contains('Passwort ist ein Pflichtfeld');
});
it('displays error if passwords are not secure', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd1234', 'Abcd1234', true);
cy.get('[data-cy="password-local-errors"]').contains('Das Passwort muss Grossbuchstaben, Zahlen und Sonderzeichen beinhalten und mindestens 8 Zeichen lang sein');
});
it('displays error if passwords are too short', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd12!', 'Abcd12!', true);
cy.get('[data-cy="password-local-errors"]').contains('Das Passwort muss Grossbuchstaben, Zahlen und Sonderzeichen beinhalten und mindestens 8 Zeichen lang sein');
});
it('displays error if passwords are not matching', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd1234!', 'Abcd129999!', true);
cy.get('[data-cy="passwordConfirmation-local-errors"]').contains('Die Bestätigung von Passwort wiederholen stimmt nicht überein');
});
it('displays error if terms are not accepted', () => {
cy.route('POST', isEmailAvailableUrl, "true");
cy.route('POST', registerUrl, registrationResponse);
cy.visit('/hello');
cy.checkEmailAvailable(registrationResponse.email);
cy.get('[data-cy="registration-title"]').contains('Damit Sie mySkillbox verwenden können, müssen Sie ein Konto erstellen.');
cy.register(registrationResponse.gender, registrationResponse.firstname, registrationResponse.lastname, 'Weg 1', 'Bern', '3001', 'Abcd1234!', 'Abcd1234!', false);
cy.get('[data-cy="acceptedTerms-local-errors"]').contains('Sie müssen hier zustimmen, damit Sie sich registrieren können.');
});
it('redirects to hello if email is missing', () => {
cy.visit('/register');
cy.get('[data-cy="hello-title"]').contains('Wollen sie mySkillbox jetzt im Unterricht verwenden?');
});
});

View File

@ -3,8 +3,8 @@ describe('The Room Page', () => {
// todo: mock all the graphql queries and mutations
cy.viewport('macbook-15');
cy.apolloLogin('rahel.cueni', 'test');
cy.visit('/room/ein-historisches-festival');
cy.login('rahel.cueni', 'test');
cy.get('[data-cy=add-room-entry-button]').click();
cy.get('.add-content-element:first-of-type').click();

View File

@ -1,16 +1,16 @@
describe('The Rooms Page', () => {
// todo: mock all the graphql queries and mutations
it('goes to the rooms page', () => {
cy.apolloLogin('nico.zickgraf', 'test');
cy.visit('/rooms');
cy.login('nico.zickgraf', 'test');
cy.get('[data-cy=add-room]').should('exist');
});
it('add room should not exist for student', () => {
cy.apolloLogin('rahel.cueni', 'test');
cy.visit('/rooms');
cy.login('rahel.cueni', 'test');
cy.get('[data-cy=add-room]').should('not.exist');

View File

@ -8,7 +8,6 @@ describe('Spellcheck', () => {
cy.server();
cy.mockGraphql({
schema: schema,
// endpoint: '/api/graphql'
operations: {
MeQuery: {
me: {

View File

@ -29,16 +29,17 @@
// import 'cypress-graphql-mock';
import '@iam4x/cypress-graphql-mock';
Cypress.Commands.add('apolloLogin', (username, password) => {
const payload = {
'operationName': 'Login',
'operationName': 'BetaLogin',
'variables': {
'input': {
'usernameInput': username,
'passwordInput': password
}
},
'query': 'mutation Login($input: LoginInput!) {\n login(input: $input) {\n success\n errors {\n field\n __typename\n }\n __typename\n }\n}\n'
'query': 'mutation BetaLogin($input: BetaLoginInput!) {\n betaLogin(input: $input) {\n success\n __typename\n }\n}\n'
};
cy.request({
@ -53,7 +54,7 @@ Cypress.Commands.add('apolloLogin', (username, password) => {
// todo: replace with apollo call
Cypress.Commands.add("login", (username, password, visitLogin = false) => {
if (visitLogin) {
cy.visit('/login');
cy.visit('/beta-login');
}
if (username != '') {
@ -110,22 +111,59 @@ Cypress.Commands.add('changePassword', (oldPassword, newPassword) => {
cy.get('[data-cy=change-password-button]').click();
});
Cypress.Commands.add('register', (firstname, lastname, email, licenseKey) => {
if (firstname != '') {
cy.get('[data-cy=firstname-input]').type(firstname);
}
if (lastname != '') {
cy.get('[data-cy=lastname-input]').type(lastname);
}
if (email != '') {
cy.get('[data-cy=email-input]').type(email);
}
if (licenseKey != '') {
cy.get('[data-cy=licenseKey-input]').type(licenseKey);
}
cy.get('[data-cy=register-button]').click();
Cypress.Commands.add('checkEmailAvailable', (email) => {
cy.get('[data-cy="email-input"]').type(email);
cy.get('[data-cy="hello-button"]').click();
});
Cypress.Commands.add('enterPassword', (password) => {
cy.get('[data-cy="password-input"]').type(password);
cy.get('[data-cy="login-button"]').click();
});
Cypress.Commands.add('register', (prefix, firstname, lastname, street, city, postcode, password, passwordConfirmation, acceptTerms) => {
let selection = prefix === 1 ? 'Herr' : 'Frau';
cy.get('[data-cy="prefix-selection"]').select(selection);
if (firstname !== '') {
cy.get('[data-cy="firstname-input"]').type(firstname);
}
if (lastname !== '') {
cy.get('[data-cy="lastname-input"]').type(lastname);
}
if (street !== '') {
cy.get('[data-cy="street-input"]').type(street);
}
if (city !== '') {
cy.get('[data-cy="city-input"]').type(city);
}
if (postcode !== '') {
cy.get('[data-cy="postcode-input"]').type(postcode);
}
if (password !== '') {
cy.get('[data-cy="password-input"]').type(password);
}
if (acceptTerms) {
cy.get('[data-cy="acceptedTerms-input"] > input').first().check({force: true}).then(() => {
cy.get('[data-cy="acceptedTerms-input"] > input:checkbox').should('be.checked');
});;
}
cy.get('[data-cy="passwordConfirmation-input"]').type(passwordConfirmation);
cy.get('[data-cy="register-button"]').click();
});
Cypress.Commands.add('redeemCoupon', coupon => {
if (coupon !== '') {
cy.get('[data-cy="coupon-input"]').type(coupon);
}
cy.get('[data-cy="coupon-button"]').click();
})

255
client/package-lock.json generated
View File

@ -2460,7 +2460,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@ -2492,7 +2492,7 @@
},
"onetime": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true
},
@ -9130,7 +9130,8 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -9148,11 +9149,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"bundled": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -9165,15 +9168,18 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -9276,7 +9282,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -9286,6 +9293,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -9298,17 +9306,20 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"bundled": true,
"optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@ -9325,6 +9336,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -9397,7 +9409,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -9407,6 +9420,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -9482,7 +9496,8 @@
},
"safe-buffer": {
"version": "5.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -9512,6 +9527,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -9529,6 +9545,7 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -9567,18 +9584,20 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"bundled": true,
"optional": true
},
"yallist": {
"version": "3.0.2",
"bundled": true
"bundled": true,
"optional": true
}
}
},
"fstream": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
@ -9791,13 +9810,20 @@
}
},
"globule": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
"integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
"integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
"requires": {
"glob": "~7.1.1",
"lodash": "~4.17.10",
"lodash": "~4.17.12",
"minimatch": "~3.0.2"
},
"dependencies": {
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
}
}
},
"graceful-fs": {
@ -10406,9 +10432,9 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
"in-publish": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E="
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
"integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ=="
},
"indent-string": {
"version": "2.1.0",
@ -11617,7 +11643,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -11660,7 +11687,8 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
@ -11671,7 +11699,8 @@
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -11788,7 +11817,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -11800,6 +11830,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -11829,6 +11860,7 @@
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -11847,6 +11879,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -11940,6 +11973,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -12025,7 +12059,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -12061,6 +12096,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -12080,6 +12116,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -12123,12 +12160,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@ -13302,7 +13341,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
@ -13485,21 +13524,11 @@
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
"dev": true
},
"lodash.assign": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
},
"lodash.camelcase": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
},
"lodash.create": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
@ -13544,11 +13573,6 @@
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
},
"lodash.mergewith": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
},
"lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@ -14145,7 +14169,8 @@
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"optional": true
},
"nanomatch": {
"version": "1.2.13",
@ -14433,9 +14458,9 @@
}
},
"node-sass": {
"version": "4.9.2",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
"integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==",
"version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@ -14444,20 +14469,29 @@
"get-stdin": "^4.0.1",
"glob": "^7.0.3",
"in-publish": "^2.0.0",
"lodash.assign": "^4.2.0",
"lodash.clonedeep": "^4.3.2",
"lodash.mergewith": "^4.6.0",
"lodash": "^4.17.15",
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.10.0",
"node-gyp": "^3.3.1",
"nan": "^2.13.2",
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "2.87.0",
"request": "^2.88.0",
"sass-graph": "^2.2.4",
"stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2"
},
"dependencies": {
"ajv": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
@ -14484,10 +14518,80 @@
"which": "^1.2.9"
}
},
"fast-deep-equal": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
},
"har-validator": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
"ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
}
}
},
@ -17119,8 +17223,7 @@
"psl": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==",
"dev": true
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
},
"public-encrypt": {
"version": "4.0.2",
@ -18560,9 +18663,9 @@
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"stdout-stream": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz",
"integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
"integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
"requires": {
"readable-stream": "^2.0.1"
}
@ -18773,12 +18876,12 @@
"integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
},
"tar": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"requires": {
"block-stream": "*",
"fstream": "^1.0.2",
"fstream": "^1.0.12",
"inherits": "2"
}
},
@ -19066,25 +19169,11 @@
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
},
"true-case-path": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz",
"integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
"integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
"requires": {
"glob": "^6.0.4"
},
"dependencies": {
"glob": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
"integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
"requires": {
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "2 || 3",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
}
"glob": "^7.1.2"
}
},
"tryer": {

View File

@ -6,8 +6,9 @@
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"start": ". ../server/.env && npm run dev",
"lint": "eslint --ext .js,.vue src",
"fix-lint": "eslint --ext .js,.vue --fix src",
"build": "node build/build.js",
"open:cypress": "cypress open",
"test:cypress": "cypress run",
@ -57,7 +58,7 @@
"lodash": "^4.17.10",
"moment": "^2.24.0",
"node-notifier": "^5.1.2",
"node-sass": "^4.9.2",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",

View File

@ -1,9 +1,17 @@
<template>
<div :class="{'no-scroll': showModal || showMobileNavigation}" class="app" id="app">
<component :is="showModalDeprecated" v-if="showModalDeprecated"></component>
<component :is="showModal" v-if="showModal"></component>
<component :is="layout"></component>
<mobile-navigation v-if="showMobileNavigation"></mobile-navigation>
<div
:class="{'no-scroll': showModal || showMobileNavigation}"
class="app"
id="app">
<scroll-up/>
<component
:is="showModalDeprecated"
v-if="showModalDeprecated"/>
<component
:is="showModal"
v-if="showModal"/>
<component :is="layout"/>
</div>
</template>
@ -14,7 +22,6 @@
import FullScreenLayout from '@/layouts/FullScreenLayout';
import PublicLayout from '@/layouts/PublicLayout';
import Modal from '@/components/Modal';
import MobileNavigation from '@/components/book-navigation/MobileNavigation';
import NewContentBlockWizard from '@/components/content-block-form/NewContentBlockWizard';
import EditContentBlockWizard from '@/components/content-block-form/EditContentBlockWizard';
import NewRoomEntryWizard from '@/components/rooms/room-entries/NewRoomEntryWizard';
@ -31,18 +38,19 @@
import DeactivatePerson from '@/components/profile/DeactivatePerson';
import {mapGetters} from 'vuex';
import ScrollUp from '@/components/ScrollUp';
export default {
name: 'App',
components: {
ScrollUp,
DefaultLayout,
SimpleLayout,
BlankLayout,
FullScreenLayout,
PublicLayout,
Modal,
MobileNavigation,
NewContentBlockWizard,
EditContentBlockWizard,
NewRoomEntryWizard,
@ -71,9 +79,6 @@
return this.$modal.state.component;
}
},
mounted() {
}
}
</script>
@ -95,4 +100,5 @@
.no-scroll {
overflow-y: hidden;
}
</style>

View File

@ -1,7 +1,9 @@
<template>
<div class="add-content">
<a class="add-content__button" v-on:click="addContent">
<add-pointer class="add-content__icon"></add-pointer>
<a
class="add-content__button"
@click="addContent">
<add-pointer class="add-content__icon"/>
</a>
</div>
</template>

View File

@ -1,6 +1,8 @@
<template>
<div class="add-content-element" v-on:click="$emit('add-element', index)">
<add-icon class="add-content-element__icon"></add-icon>
<div
class="add-content-element"
@click="$emit('add-element', index)">
<add-icon class="add-content-element__icon"/>
</div>
</template>

View File

@ -1,7 +1,11 @@
<template>
<component :is="component" v-bind="properties" class="add-widget" @click="$emit('click')"
:class="{ 'add-widget--reverse': reverse }">
<add-icon class="add-widget__add"></add-icon>
<component
:is="component"
v-bind="properties"
:class="{ 'add-widget--reverse': reverse }"
class="add-widget"
@click="$emit('click')">
<add-icon class="add-widget__add"/>
</component>
</template>
@ -24,6 +28,10 @@
}
},
components: {
AddIcon
},
computed: {
component() {
// only use the router link if the route prop is provided, otherwise render a normal anchor tag
@ -36,10 +44,6 @@
} : {}
}
},
components: {
AddIcon
}
}
</script>

View File

@ -1,33 +1,41 @@
<template>
<div class="assignment-with-submissions">
<p class="assignment-with-submissions__text">{{assignment.assignment}}</p>
<p class="assignment-with-submissions__text">{{ assignment.assignment }}</p>
<div>
<a class="button button--primary submissions-page__back" @click="$emit('back')">Aufgabe im Modul anzeigen</a>
<a
class="button button--primary submissions-page__back"
@click="$emit('back')">Aufgabe im Modul anzeigen</a>
</div>
<div class="assignment-with-submissions__solution" v-if="assignment.solution">
<div
class="assignment-with-submissions__solution"
v-if="assignment.solution">
<h4 class="assignment-with-submissions__heading">Lösung</h4>
<p class="assignment-with-submissions__solution-text">{{assignment.solution}}</p>
<p class="assignment-with-submissions__solution-text">{{ assignment.solution }}</p>
</div>
<p class="assignment-with-submissions__no-submissions" v-if="!assignment.submissions.length">Zu diesem Auftrag sind noch keine Ergebnisse vorhanden</p>
<p
class="assignment-with-submissions__no-submissions"
v-if="!assignment.submissions.length">Zu diesem Auftrag sind noch keine Ergebnisse vorhanden</p>
<div v-if="assignment.submissions.length" class="assignment-with-submissions__submissions submissions">
<div
class="assignment-with-submissions__submissions submissions"
v-if="assignment.submissions.length">
<div class="submissions__header student-submission-row submission-header">
<p class="submission-header__title">Lernende</p>
<p class="submission-header__title">Ergebnisse</p>
<p class="submission-header__title">Feedback</p>
<p class="submission-header__title">Lernende</p>
<p class="submission-header__title">Ergebnisse</p>
<p class="submission-header__title">Feedback</p>
</div>
<router-link
:to="submissionLink(submission)"
v-for="submission in submissions"
class="assignment-with-submissions__link"
:key="submission.id">
<student-submission class="assignment-with-submissions__submission"
:submission="submission"
>
</student-submission>
</router-link>
<router-link
:to="submissionLink(submission)"
:key="submission.id"
class="assignment-with-submissions__link"
v-for="submission in submissions">
<student-submission
:submission="submission"
class="assignment-with-submissions__submission"
/>
</router-link>
</div>
</div>
@ -45,15 +53,9 @@
StudentSubmission
},
methods: {
submissionLink(submission) {
return `/submission/${submission.id}`
},
belongsToSchool(submission) {
if (this.currentFilter.id === '') {
return true;
}
return submission.student.schoolClasses.edges.some(edge => edge.node.id === this.currentFilter.id)
data() {
return {
me: {}
}
},
@ -68,9 +70,15 @@
},
},
data() {
return {
me: {}
methods: {
submissionLink(submission) {
return `/submission/${submission.id}`
},
belongsToSchool(submission) {
if (this.currentFilter.id === '') {
return true;
}
return submission.student.schoolClasses.edges.some(edge => edge.node.id === this.currentFilter.id)
}
},

View File

@ -1,25 +1,30 @@
<template>
<div class="chapter" :data-scrollto="chapter.id">
<h3 :id="'chapter-' + index">{{chapter.title}}</h3>
<div
:data-scrollto="chapter.id"
class="chapter">
<h3 :id="'chapter-' + index">{{ chapter.title }}</h3>
<bookmark-actions
:bookmarked="chapter.bookmark"
:note="note"
class="chapter__bookmark-actions"
@add-note="addNote"
@edit-note="editNote"
:bookmarked="chapter.bookmark"
@bookmark="bookmark(!chapter.bookmark)"
:note="note"
></bookmark-actions>
/>
<p class="chapter__description">
{{chapter.description}}
{{ chapter.description }}
</p>
<add-content-button :parent="chapter" v-if="editModule"></add-content-button>
<add-content-button
:parent="chapter"
v-if="editModule"/>
<content-block :contentBlock="contentBlock"
:parent="chapter.id"
:key="contentBlock.id" v-for="contentBlock in filteredContentBlocks">
</content-block>
<content-block
:content-block="contentBlock"
:parent="chapter.id"
:key="contentBlock.id"
v-for="contentBlock in filteredContentBlocks"/>
</div>
</template>
@ -44,6 +49,12 @@
AddContentButton
},
data() {
return {
me: {}
}
},
computed: {
...mapGetters(['editModule']),
filteredContentBlocks() {
@ -66,12 +77,6 @@
}
},
data() {
return {
me: {}
}
},
methods: {
bookmark(bookmarked) {
const id = this.chapter.id;

View File

@ -1,10 +1,13 @@
<template>
<base-input :label="label"
:checked="checked"
:item="item"
v-on:input="passOn"
:type="'checkbox'"
></base-input>
<base-input
:label="label"
:checked="checked"
:item="item"
:type="'checkbox'"
@input="passOn"
>
<slot/>
</base-input>
</template>
<script>

View File

@ -1,12 +1,17 @@
<template>
<div class="color-chooser">
<div v-for="(color, index) in colors"
:key="index"
class="color-chooser__color-wrapper"
@click="$emit('input', color.name)"
:class="{'color-chooser__color-wrapper--selected': selectedColor === color.name}">
<div class="color-chooser__color" :class="'color-chooser__color--' + color.name">
<tick class="color-chooser__selected-icon" v-if="selectedColor === color.name"></tick>
<div
:key="index"
:class="{'color-chooser__color-wrapper--selected': selectedColor === color.name}"
class="color-chooser__color-wrapper"
v-for="(color, index) in colors"
@click="$emit('input', color.name)">
<div
:class="'color-chooser__color--' + color.name"
class="color-chooser__color">
<tick
class="color-chooser__selected-icon"
v-if="selectedColor === color.name"/>
</div>
</div>
</div>
@ -18,6 +23,10 @@
export default {
props: ['selected-color'],
components: {
Tick
},
data() {
return {
colors: [
@ -36,10 +45,6 @@
]
}
},
components: {
Tick
}
}
</script>

View File

@ -1,9 +1,17 @@
<template>
<div class="content-block__container hideable-element" :class="{'hideable-element--hidden': hidden}">
<div class="content-block" :class="specialClass">
<div class="block-actions" v-if="canEditContentBlock && editModule">
<user-widget :show-menu="false" v-bind="me"
class="block-actions__user-widget content-block__user-widget"></user-widget>
<div
:class="{'hideable-element--hidden': hidden}"
class="content-block__container hideable-element">
<div
:class="specialClass"
class="content-block">
<div
class="block-actions"
v-if="canEditContentBlock && editModule">
<user-widget
:show-menu="false"
v-bind="me"
class="block-actions__user-widget content-block__user-widget"/>
<more-options-widget>
<li class="popover-links__link"><a @click="deleteContentBlock(contentBlock)">Löschen</a></li>
<li class="popover-links__link"><a @click="editContentBlock(contentBlock)">Bearbeiten</a></li>
@ -11,26 +19,32 @@
</div>
<div class="content-block__visibility">
<visibility-action
v-if="canEditModule"
:block="contentBlock"></visibility-action>
:block="contentBlock"
v-if="canEditModule"/>
</div>
<h3 v-if="instrumentLabel !== ''" class="content-block__instrument-label">{{instrumentLabel}}</h3>
<h4 class="content-block__title" v-if="!contentBlock.indent">{{contentBlock.title}}</h4>
<h3
class="content-block__instrument-label"
v-if="instrumentLabel !== ''">{{ instrumentLabel }}</h3>
<h4
class="content-block__title"
v-if="!contentBlock.indent">{{ contentBlock.title }}</h4>
<content-component v-for="component in contentBlocksWithContentLists.contents"
:key="component.id"
:component="component"
:root="root"
:parent="contentBlock"
:bookmarks="contentBlock.bookmarks"
:notes="contentBlock.notes"
>
</content-component>
<content-component
:key="component.id"
:component="component"
:root="root"
:parent="contentBlock"
:bookmarks="contentBlock.bookmarks"
:notes="contentBlock.notes"
v-for="component in contentBlocksWithContentLists.contents"
/>
</div>
<add-content-button :after="contentBlock" v-if="canEditModule"></add-content-button>
<add-content-button
:after="contentBlock"
v-if="canEditModule"/>
</div>
@ -41,9 +55,6 @@
import MoreOptionsWidget from '@/components/MoreOptionsWidget';
import UserWidget from '@/components/UserWidget';
import VisibilityAction from '@/components/visibility/VisibilityAction';
import EyeIcon from '@/components/icons/EyeIcon';
import PenIcon from '@/components/icons/PenIcon';
import TrashIcon from '@/components/icons/TrashIcon';
import ContentComponent from '@/components/content-blocks/ContentComponent';
import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql';
@ -63,20 +74,24 @@
};
export default {
name: 'ContentBlock',
props: ['contentBlock', 'parent'],
name: 'content-block',
components: {
ContentComponent,
AddContentButton,
VisibilityAction,
EyeIcon,
PenIcon,
TrashIcon,
MoreOptionsWidget,
UserWidget
},
data() {
return {
showVisibility: false,
me: {}
}
},
computed: {
...mapGetters(['editModule']),
canEditModule() {
@ -160,7 +175,6 @@
return this.contentBlock.root ? this.contentBlock.root : this.contentBlock.id;
}
},
methods: {
editContentBlock(contentBlock) {
this.$store.dispatch('editContentBlock', contentBlock.id);
@ -222,12 +236,6 @@
return [...content.slice(0, listIndex), ...content[listIndex].contents[0].value, ...content.slice(listIndex + 1)];
}
},
data() {
return {
showVisibility: false,
me: {}
}
},
apollo: {
me: meQuery

View File

@ -1,103 +0,0 @@
<template>
<nav class="top-navigation" :class="{'top-navigation--mobile': mobile}">
<div class="top-navigation__item">
<router-link to="/book/topic/berufliche-grundbildung" active-class="top-navigation__link--active"
:class="{'top-navigation__link--active': isActive('book')}"
@click.native="hideMobileNavigation"
class="top-navigation__link">Inhalte
</router-link>
<mobile-subnavigation v-if="mobile"></mobile-subnavigation>
</div>
<div class="top-navigation__item">
<router-link to="/rooms" active-class="top-navigation__link--active" @click.native="hideMobileNavigation"
class="top-navigation__link">Räume
</router-link>
</div>
<div class="top-navigation__item">
<router-link to="/portfolio" active-class="top-navigation__link--active" @click.native="hideMobileNavigation"
class="top-navigation__link">Portfolio
</router-link>
</div>
</nav>
</template>
<script>
import MobileSubnavigation from '@/components/book-navigation/MobileSubnavigation';
export default {
props: {
mobile: {
default: false
}
},
components: {
MobileSubnavigation
},
methods: {
isActive(linkName) {
return linkName === 'book' && this.$route.path.indexOf('module') > -1;
},
hideMobileNavigation() {
this.$store.dispatch('showMobileNavigation', false);
}
}
}
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.top-navigation {
display: flex;
&__link {
padding: 0 24px;
@include default-link;
}
$parent: &;
&--mobile {
flex-direction: column;
#{$parent}__link {
color: $color-white;
@include heading-4;
line-height: 2.5em;
padding: 0;
display: block;
margin-bottom: 0.5*$small-spacing;
&:only-child {
margin-bottom: 0;
}
}
#{$parent}__item {
border-bottom: 1px solid $color-white;
&:nth-child(1) {
order: 3;
border-bottom: 0;
}
&:nth-child(2) {
order: 1;
}
&:nth-child(3) {
order: 2;
}
}
}
}
</style>

View File

@ -1,6 +1,11 @@
<template>
<modal class="fullscreen-image" :hide-header="true" :fullscreen="true">
<img class="fullscreen-image__image" :src="imageUrl">
<modal
:hide-header="true"
:fullscreen="true"
class="fullscreen-image">
<img
:src="imageUrl"
class="fullscreen-image__image">
</modal>
</template>

View File

@ -1,6 +1,8 @@
<template>
<modal :fullscreen="true">
<component :is="type" :value="value"></component>
<component
:is="type"
:value="value"/>
</modal>
</template>

View File

@ -1,11 +1,17 @@
<template>
<modal class="fullscreen-video" :hide-header="true" :fullscreen="true">
<iframe :src="src"
width="2000"
height="1000"
class="fullscreen-video__embed"
frameborder="0" webkitallowfullscreen
mozallowfullscreen allowfullscreen></iframe>
<modal
:hide-header="true"
:fullscreen="true"
class="fullscreen-video">
<iframe
:src="src"
width="2000"
height="1000"
class="fullscreen-video__embed"
frameborder="0"
webkitallowfullscreen
mozallowfullscreen
allowfullscreen/>
</modal>
</template>

View File

@ -1,27 +1,31 @@
<template>
<header class="header-bar">
<content-navigation></content-navigation>
<router-link to="/" class="header-bar__logo" data-cy="home-link">
<logo class="header-bar__logo-icon"></logo>
</router-link>
<a
class="header-bar__sidebar-link"
@click="openSidebar('navigation')">
<hamburger class="header-bar__sidebar-icon"/>
</a>
<content-navigation class="header-bar__content-navigation"/>
<div class="user-header">
<a class="user-header__sidebar-link" @click="openSidebar()">
<a
class="user-header__sidebar-link"
@click="openSidebar('profile')">
<current-class class="user-header__current-class"/>
</a>
<user-widget data-cy="header-user-widget" v-bind="me"></user-widget>
<user-widget
v-bind="me"
data-cy="header-user-widget"/>
</div>
<book-navigation v-if="showSubnavigation">
</book-navigation>
</header>
</template>
<script>
import ContentNavigation from '@/components/ContentNavigation.vue';
import BookNavigation from '@/components/book-navigation/BookNavigation';
import ContentNavigation from '@/components/book-navigation/ContentNavigation.vue';
import UserWidget from '@/components/UserWidget.vue';
import Logo from '@/components/icons/Logo';
import CurrentClass from '@/components/school-class/CurrentClass';
import Hamburger from '@/components/icons/Hamburger';
import openSidebar from '@/mixins/open-sidebar';
import me from '@/mixins/me';
@ -32,15 +36,9 @@
components: {
ContentNavigation,
UserWidget,
BookNavigation,
Logo,
CurrentClass
},
computed: {
showSubnavigation() {
return this.$route.meta.subnavigation;
}
CurrentClass,
Hamburger
},
}
</script>
@ -53,20 +51,19 @@
display: flex;
flex-direction: row;
@supports (display: grid) {
display: none;
@include desktop {
display: grid;
}
display: grid;
}
align-items: center;
justify-content: space-between;
background-color: $color-white;
grid-auto-rows: 50px;
width: 100%;
max-width: 100vw;
grid-template-columns: 1fr 1fr 1fr;
@include desktop {
grid-template-columns: 1fr 1fr 1fr;
grid-template-columns: 50px 1fr 200px;
grid-template-rows: 50px;
grid-auto-rows: auto;
}
@ -85,6 +82,21 @@
-ms-grid-row-align: center;
}
&__content-navigation {
grid-column: 2;
justify-content: space-between;
}
&__sidebar-link {
padding: $small-spacing;
cursor: pointer;
}
&__sidebar-icon {
width: 30px;
height: 30px;
}
/*
* For IE10+
*/
@ -101,29 +113,6 @@
-ms-grid-column: 1;
-ms-grid-column-span: 3;
}
&__logo {
color: #17A887;
font-size: 36px;
font-weight: 800;
font-family: $sans-serif-font-family;
display: flex;
justify-self: center;
/*
* For IE10+
*/
-ms-grid-column: 2;
-ms-grid-row-align: center;
-ms-grid-column-align: center;
}
&__logo-icon {
width: 212px;
height: 31px;
}
}
.user-header {
@ -135,6 +124,11 @@
&__sidebar-link {
cursor: pointer;
display: none;
@include desktop {
display: flex;
}
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="helpful-tooltip">
<info-icon class="helpful-tooltip__icon"></info-icon>
<info-icon class="helpful-tooltip__icon"/>
<div class="helpful-tooltip__tooltip">
<div class="helpful-tooltip__text">
{{text}}
{{ text }}
</div>
</div>
</div>

View File

@ -1,41 +0,0 @@
<template>
<router-link tag="div" :to="{name: 'instrument', params: {slug: slug}}" class="instrument-teaser">
<h3 class="instrument-teaser__title">{{title}}</h3>
<p class="instrument-teaser__text" v-html="teaser">
</p>
</router-link>
</template>
<script>
import teaser from '@/helpers/teaser';
export default {
props: ['title', 'contents', 'slug'],
computed: {
teaser() {
return teaser(this.contents);
}
}
}
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.instrument-teaser {
height: 250px;
box-sizing: border-box;
padding: $medium-spacing;
@include widget-shadow;
width: 100%;
cursor: pointer;
&__title {
@include heading-3;
margin-bottom: $small-spacing;
line-height: 1.2;
}
}
</style>

View File

@ -1,6 +1,9 @@
<template>
<div class="logout-widget">
<a class="logout-widget__logout" data-cy="logout" @click="logout()">Logout</a>
<a
class="logout-widget__logout"
data-cy="logout"
@click="logout()">Abmelden</a>
</div>
</template>
@ -25,12 +28,11 @@
@import "@/styles/_mixins.scss";
.logout-widget {
color: $color-silver-dark;
display: flex;
align-items: center;
&__logout {
@include regular-text;
@include default-link;
cursor: pointer;
}
}

View File

@ -1,14 +1,16 @@
<template>
<div class="mobile-header">
<a @click="showMobileNavigation">
<hamburger class="mobile-header__hamburger"></hamburger>
<hamburger class="mobile-header__hamburger"/>
</a>
<router-link to="/" data-cy="mobile-home-link">
<logo></logo>
<router-link
to="/"
data-cy="mobile-home-link">
<logo/>
</router-link>
<user-widget v-bind="me"></user-widget>
<user-widget v-bind="me"/>
</div>
</template>

View File

@ -1,20 +1,25 @@
<template>
<div class="modal__backdrop">
<div class="modal"
:class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}">
<div
:class="{'modal--hide-header': hideHeader || fullscreen, 'modal--fullscreen': fullscreen, 'modal--small': small}"
class="modal">
<div class="modal__header">
<slot name="header"></slot>
<slot name="header"/>
</div>
<div class="modal__body">
<slot></slot>
<div class="modal__close-button" @click="hideModal">
<cross class="modal__close-icon"></cross>
<slot/>
<div
class="modal__close-button"
@click="hideModal">
<cross class="modal__close-icon"/>
</div>
</div>
<div class="modal__footer">
<slot name="footer">
<!--<a class="button button&#45;&#45;active">Speichern</a>-->
<a class="button" v-on:click="hideModal">Abbrechen</a>
<a
class="button"
@click="hideModal">Abbrechen</a>
</slot>
</div>
</div>
@ -116,11 +121,6 @@
align-content: center;
}
&__close-icon {
width: 40px;
height: 40px;
}
&__footer {
grid-area: footer;
-ms-grid-row: 3;

View File

@ -1,11 +1,14 @@
<template>
<div class="modal-input">
<input :placeholder="placeholder"
class="modal-input__inputfield skillbox-input"
:class="{'skillbox-input--error': error}"
:value="value"
v-on:input="$emit('input', $event.target.value)">
<div class="modal-input__error" v-if="error">
<input
:placeholder="placeholder"
:class="{'skillbox-input--error': error}"
:value="value"
class="modal-input__inputfield skillbox-input"
@input="$emit('input', $event.target.value)">
<div
class="modal-input__error"
v-if="error">
Für Inhaltsblöcke muss zwingend ein Titel erfasst werden.
</div>
</div>

View File

@ -1,12 +1,15 @@
<template>
<div class="more-options">
<a @click="showMenu = !showMenu" class="more-options__more-link">
<ellipses class="more-options__ellipses"></ellipses>
<a
class="more-options__more-link"
@click="showMenu = !showMenu">
<ellipses class="more-options__ellipses"/>
</a>
<widget-popover @hide-me="showMenu = false"
class="more-options__popover"
v-if="showMenu">
<slot></slot>
<widget-popover
class="more-options__popover"
v-if="showMenu"
@hide-me="showMenu = false">
<slot/>
</widget-popover>
</div>
</template>

View File

@ -1,10 +1,16 @@
<template>
<div class="news-teaser">
<h4 class="small-emph">
<a :href="url" target="_blank" class="teaser__title">{{title}}</a>
<a
:href="url"
target="_blank"
class="teaser__title">{{ title }}</a>
</h4>
<a :href="url" target="_blank" class="teaser__date">
{{date}}
<a
:href="url"
target="_blank"
class="teaser__date">
{{ date }}
</a>
</div>
</template>

View File

@ -0,0 +1,103 @@
<template>
<div class="news-teasers">
<div
:key="teaser.id"
class="news-teasers__teaser teaser"
v-for="teaser in newsTeasers">
<a :href="teaser.newsArticleUrl">
<img
:src="teaser.imageUrl"
class="teaser__image">
<p class="teaser__image-source">
<a
:href="teaser.imageSource"
class="tiny-text">Quelle {{ teaser.imageSource }}</a></p>
<h4 class="teaser__title">{{ teaser.title }}</h4>
<p class="teaser__description">{{ teaser.description }}</p>
<p class="teaser__date">{{ teaser.displayDate }}</p>
</a>
</div>
</div>
</template>
<script>
import NEWS_TEASER_QUERY from '@/graphql/gql/newsTeasersQuery.gql';
export default {
components: {},
data() {
return {
newsTeasers: []
};
},
apollo: {
$client: 'publicClient',
newsTeasers: {
query: NEWS_TEASER_QUERY,
update(data) {
return this.$getRidOfEdges(data).newsTeasers;
}
}
}
};
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_functions.scss";
@import "@/styles/_mixins.scss";
$news_width: 550px;
$image_height: 254px;
.teasers {
display: block;
@include desktop {
@supports (display: grid) {
display: grid;
display: -ms-grid;
}
grid-template-columns: repeat(auto-fit, minmax(320px, $news_width));
grid-gap: 40px;
grid-auto-rows: minmax(400px, auto);
grid-template-rows: auto auto;
-ms-grid-columns: $news_width $news_width;
}
}
.teaser {
margin-bottom: $large-spacing;
position: relative;
&__image {
display: block;
max-width: 100%;
height: auto;
@include desktop {
max-width: $news_width;
}
}
&__image-source {
line-height: 25px;
}
&__description {
margin-bottom: $large-spacing;
}
&__date {
font-family: $sans-serif-font-family;
font-weight: $font-weight-regular;
color: $color-silver-dark;
position: absolute;
bottom: 0;
left: 0;
}
}
</style>

View File

@ -1,10 +1,11 @@
<template>
<base-input :label="label"
:checked="checked"
:item="item"
v-on:input="passOn"
:type="'radiobutton'"
></base-input>
<base-input
:label="label"
:checked="checked"
:item="item"
:type="'radiobutton'"
@input="passOn"
/>
</template>
<script>

View File

@ -0,0 +1,76 @@
<template>
<transition name="fade">
<a
class="scroll-up"
v-if="scroll>200"
@click="scrollTop">
<arrow-up class="scroll-up__icon"/>
</a>
</transition>
</template>
<script>
import ArrowUp from '@/components/icons/ArrowUp';
export default {
components: {
ArrowUp
},
data() {
return {
scroll: 0
}
},
mounted() {
let html = document.scrollingElement;
document.body.onscroll = () => {
this.scroll = html.scrollTop;
}
},
destroyed() {
document.body.onscroll = null;
},
methods: {
scrollTop() {
document.scrollingElement.scrollTop = 0;
}
},
}
</script>
<style scoped lang="scss">
@import '@/styles/_variables.scss';
@import '@/styles/_mixins.scss';
.scroll-up {
position: fixed;
right: $large-spacing;
bottom: $large-spacing;
padding: $medium-spacing;
border-radius: 100px;
@include default-box-shadow;
cursor: pointer;
background-color: $color-white;
border: 1px solid $color-silver;
&__icon {
width: 50px;
height: 50px;
fill: $color-brand;
}
}
.fade-enter-active, .fade-leave-active {
transition: opacity .3s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
{
opacity: 0;
}
</style>

View File

@ -1,20 +1,31 @@
<template>
<div class="section-block">
<div class="section-block__illustration" @click="navigate()" :class="{'section-block--navigatable': route}">
<slot></slot>
<div
:class="{'section-block--navigatable': route}"
class="section-block__illustration"
@click="navigate()">
<slot/>
</div>
<div class="section-block__title block-title" @click="navigate()" :class="{'section-block--navigatable': route}">
<h2 class="block-title__title">{{title}}</h2>
<h3 class="block-title__subtitle small-emph">{{subtitle}}</h3>
<div
:class="{'section-block--navigatable': route}"
class="section-block__title block-title"
@click="navigate()">
<h2 class="block-title__title">{{ title }}</h2>
<h3 class="block-title__subtitle small-emph">{{ subtitle }}</h3>
</div>
<div class="section-block__content section-content">
<div class="section-content__subsection subsection">
<a class="subsection__content button button--primary" v-if="route" @click="navigate()"
:class="{'section-block--navigatable': route}">
{{linkText}}
<a
:class="{'section-block--navigatable': route}"
class="subsection__content button button--primary"
v-if="route"
@click="navigate()">
{{ linkText }}
</a>
<span class="subsection__content subsection__content--disabled" v-if="!route">Noch nicht verfügbar</span>
<span
class="subsection__content subsection__content--disabled"
v-if="!route">Noch nicht verfügbar</span>
</div>
</div>
</div>

View File

@ -1,9 +1,17 @@
<template>
<div class="simple-file-upload">
<a class="simple-file-upload__link" @click="clickUploadCare" v-if="!value">
<document-icon class="simple-file-upload__icon"></document-icon>
<a
class="simple-file-upload__link"
v-if="!value"
@click="clickUploadCare">
<document-icon class="simple-file-upload__icon"/>
</a>
<input type="hidden" ref="uploadcare-filedialog" role="uploadcare-uploader" name="file-upload" data-system-dialog/>
<input
type="hidden"
role="uploadcare-uploader"
name="file-upload"
data-system-dialog
ref="uploadcare-filedialog">
</div>
</template>
@ -19,14 +27,6 @@
DocumentIcon
},
methods: {
clickUploadCare() {
// workaround for styling the uploadcare widget
let button = this.$el.querySelector('.uploadcare--widget__button');
button.click();
}
},
mounted() {
let widget = uploadcareWidget.Widget('[role=uploadcare-uploader]');
@ -36,7 +36,15 @@
this.$emit('link-change-url', urlWithFilename);
});
});
}
},
methods: {
clickUploadCare() {
// workaround for styling the uploadcare widget
let button = this.$el.querySelector('.uploadcare--widget__button');
button.click();
}
},
}
</script>

View File

@ -1,34 +1,35 @@
<template>
<div class="student-submission student-submission-row">
<div class="student-submission__student-name">
{{name}}
{{ name }}
</div>
<div class="student-submission__entry entry">
<p>{{submission.text | trimToLength(50)}}</p>
<p v-if="submission.document && submission.document.length > 0" class="entry__document">
<student-submission-document :document="submission.document" class="entry-document"></student-submission-document>
<p>{{ submission.text | trimToLength(50) }}</p>
<p
class="entry__document"
v-if="submission.document && submission.document.length > 0">
<student-submission-document
:document="submission.document"
class="entry-document"/>
</p>
</div>
<div class="student-submission__feedback entry" v-if="submission.submissionFeedback">
<p class="entry__text" :class="{'entry__text--final': submission.submissionFeedback.final}">{{submission.submissionFeedback.text | trimToLength(50)}}</p>
<div
class="student-submission__feedback entry"
v-if="submission.submissionFeedback">
<p
:class="{'entry__text--final': submission.submissionFeedback.final}"
class="entry__text">{{ submission.submissionFeedback.text | trimToLength(50) }}</p>
</div>
</div>
</template>
<script>
import DocumentIcon from '@/components/icons/DocumentIcon'
import StudentSubmissionDocument from '@/components/StudentSubmissionDocument';
export default {
props: ['submission'],
components: { DocumentIcon, StudentSubmissionDocument },
computed: {
name() {
return this.submission && this.submission.student
? `${this.submission.student.firstName} ${this.submission.student.lastName}` : '';
},
components: {
StudentSubmissionDocument
},
filters: {
trimToLength: function(text, numberOfChars) {
@ -44,7 +45,14 @@
}
return `${text.substring(0, index)}`;
}
}
},
computed: {
name() {
return this.submission && this.submission.student
? `${this.submission.student.firstName} ${this.submission.student.lastName}` : '';
},
},
}
</script>

View File

@ -1,8 +1,10 @@
<template>
<div class="submission-document">
<p v-if="document && document.length > 0" class="submission-document__content content">
<document-icon class="content__icon"></document-icon><span class="content__text">{{filename}}</span>
</p>
<p
class="submission-document__content content"
v-if="document && document.length > 0">
<document-icon class="content__icon"/><span class="content__text">{{ filename }}</span>
</p>
</div>
</template>
@ -12,8 +14,8 @@
import filenameFromUrl from '@/helpers/urls';
export default {
name: 'StudentSubmissionDocument',
props: ['document'],
name: 'student-submission-document',
components: { DocumentIcon },
computed: {

View File

@ -1,11 +1,13 @@
<template>
<div class="user-widget">
<div class="user-widget__avatar">
<avatar :avatar-url="avatarUrl" />
</div>
<span class="user-widget__name">{{firstName}} {{lastName}}</span>
<span class="user-widget__date" v-if="date">{{date}}</span>
<div class="user-widget">
<div class="user-widget__avatar">
<avatar :avatar-url="avatarUrl" />
</div>
<span class="user-widget__name">{{ firstName }} {{ lastName }}</span>
<span
class="user-widget__date"
v-if="date">{{ date }}</span>
</div>
</template>
<script>

View File

@ -1,7 +1,14 @@
<template>
<div class="user-widget" :class="{'user-widget--is-profile': isProfile}">
<div class="user-widget__avatar" data-cy="user-widget-avatar" @click="openSidebar()">
<avatar :avatar-url="avatarUrl" :icon-highlighted="isProfile"/>
<div
:class="{'user-widget--is-profile': isProfile}"
class="user-widget">
<div
class="user-widget__avatar"
data-cy="user-widget-avatar"
@click="openSidebar('profile')">
<avatar
:avatar-url="avatarUrl"
:icon-highlighted="isProfile"/>
</div>
</div>
</template>

View File

@ -1,6 +1,6 @@
<template>
<div class="widget-footer">
<slot></slot>
<slot/>
</div>
</template>

View File

@ -1,7 +1,10 @@
<template>
<div class="widget-popover" v-click-outside="hidePopover" :class="{'widget-popover--mobile': mobile}">
<div
v-click-outside="hidePopover"
:class="{'widget-popover--mobile': mobile}"
class="widget-popover">
<ul class="widget-popover__links popover-links">
<slot></slot>
<slot/>
</ul>
</div>
</template>

View File

@ -1,29 +0,0 @@
<template>
<aside class="sub-navigation">
<sub-navigation-item title="Themen">
<book-topic-navigation></book-topic-navigation>
</sub-navigation-item>
<sub-navigation-item title="Instrument">
<instrument-navigation></instrument-navigation>
</sub-navigation-item>
<!--<sub-navigation-item title="News">-->
<!--<template slot="title">-->
<!--<h2 class="sub-navigation__title" slot="title">ABU News</h2>-->
<!--</template>-->
<!--</sub-navigation-item>-->
</aside>
</template>
<script>
import SubNavigationItem from '@/components/book-navigation/SubNavigationItem';
import BookTopicNavigation from '@/components/book-navigation/BookTopicNavigation';
import InstrumentNavigation from '@/components/book-navigation/InstrumentNavigation';
export default {
components: {
SubNavigationItem,
BookTopicNavigation,
InstrumentNavigation
}
}
</script>

View File

@ -1,20 +1,23 @@
<template>
<nav class="book-topics">
<router-link :to="{name: 'topic', params: {topicSlug: topic.slug}}"
@click.native="hideMobileNavigation"
tag="div"
class="book-topics__topic book-subnavigation__item"
:class="{'book-topics__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile}"
v-for="topic in topics"
:key="topic.id">
{{topic.order}}.
{{topic.title}}
<router-link
:to="{name: 'topic', params: {topicSlug: topic.slug}}"
:class="{'book-topics__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile}"
:key="topic.id"
tag="div"
active-class="book-subnavigation__item--active"
class="book-topics__topic book-subnavigation__item"
v-for="topic in topics"
@click.native="closeSidebar('navigation')">
{{ topic.order }}.
{{ topic.title }}
</router-link>
</nav>
</template>
<script>
import ALL_TOPICS_QUERY from '@/graphql/gql/allTopicsQuery.gql';
import sidebarMixin from '@/mixins/sidebar';
export default {
props: {
@ -23,6 +26,8 @@
}
},
mixins: [sidebarMixin],
data() {
return {
topics: []
@ -32,9 +37,6 @@
methods: {
topicId(id) {
return atob(id)
},
hideMobileNavigation() {
this.$store.dispatch('showMobileNavigation', false);
}
},

View File

@ -0,0 +1,217 @@
<template>
<nav
:class="{'content-navigation--sidebar': isSidebar}"
class="content-navigation">
<div class="content-navigation__primary">
<div class="content-navigation__item">
<router-link
:class="{'content-navigation__link--active': isActive('book')}"
:to="topicRoute"
active-class="content-navigation__link--active"
class="content-navigation__link"
@click.native="close">Themen
</router-link>
<book-topic-navigation
v-if="isSidebar"
/>
</div>
<div class="content-navigation__item">
<router-link
to="/instruments"
active-class="content-navigation__link--active"
class="content-navigation__link"
@click.native="close">Instrumente
</router-link>
</div>
<div class="content-navigation__item">
<router-link
:to="{name: 'news'}"
active-class="content-navigation__link--active"
class="content-navigation__link"
@click.native="close">News
</router-link>
</div>
</div>
<router-link
to="/"
class="content-navigation__logo"
data-cy="home-link"
v-if="!isSidebar"
>
<logo class="content-navigation__logo-icon"/>
</router-link>
<div class="content-navigation__secondary">
<div class="content-navigation__item content-navigation__item--secondary">
<router-link
:class="{'content-navigation__link--active': isRoomUrl()}"
to="/rooms"
active-class="content-navigation__link--active"
class="content-navigation__link content-navigation__link--secondary"
@click.native="close">Räume
</router-link>
</div>
<div class="content-navigation__item content-navigation__item--secondary">
<router-link
to="/portfolio"
active-class="content-navigation__link--active"
class="content-navigation__link content-navigation__link--secondary"
@click.native="close">Portfolio
</router-link>
</div>
</div>
</nav>
</template>
<script>
import Logo from '@/components/icons/Logo';
import BookTopicNavigation from '@/components/book-navigation/BookTopicNavigation';
import sidebarMixin from '@/mixins/sidebar';
import meMixin from '@/mixins/me';
export default {
props: {
isSidebar: {
default: false
}
},
mixins: [sidebarMixin, meMixin],
components: {
BookTopicNavigation,
Logo
},
computed: {
topicRoute() {
if (this.me.lastTopic && this.me.lastTopic.slug) {
return {
name: 'topic',
params: {
topicSlug: this.me.lastTopic.slug
}
}
}
return '/book/topic/berufliche-grundbildung'
}
},
methods: {
isActive(linkName) {
return linkName === 'book' && this.$route.path.indexOf('module') > -1;
},
isRoomUrl() {
return this.$route.path.indexOf('room') > -1;
},
close() {
this.closeSidebar('navigation');
}
}
}
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.content-navigation {
display: flex;
align-items: center;
&__link {
padding: 0 24px;
@include navigation-link;
}
&__primary, &__secondary {
display: none;
flex-direction: row;
@include desktop {
display: flex;
}
}
&__logo {
color: #17A887;
font-size: 36px;
font-weight: 800;
font-family: $sans-serif-font-family;
display: flex;
justify-self: center;
/*
* For IE10+
*/
-ms-grid-column: 2;
-ms-grid-row-align: center;
-ms-grid-column-align: center;
}
&__logo-icon {
width: 212px;
height: 31px;
}
&__link {
&--secondary {
@include regular-text;
}
&--active {
color: $color-brand;
}
}
$parent: &;
&--sidebar {
flex-direction: column;
#{$parent}__primary, #{$parent}__secondary {
display: flex;
flex-direction: column;
width: 100%;
}
#{$parent}__link {
@include heading-4;
line-height: 2.5em;
padding: 0;
display: block;
margin-bottom: 0.5*$small-spacing;
&:only-child {
margin-bottom: 0;
}
}
#{$parent}__item {
width: 100%;
//border-bottom: 1px solid $color-white;
/*&:nth-child(1) {*/
/* order: 3;*/
/* border-bottom: 0;*/
/*}*/
/*&:nth-child(2) {*/
/* order: 1;*/
/*}*/
/*&:nth-child(3) {*/
/* order: 2;*/
/*}*/
}
}
}
</style>

View File

@ -1,31 +0,0 @@
<template>
<div>
<router-link tag="div" class="book-subnavigation__item"
:class="{'book-subnavigation__item--mobile': mobile}"
@click.native="hideMobileNavigation"
to="/instruments/sprache-kommunikation">Sprache und
Kommunikation
</router-link>
<router-link tag="div" class="book-subnavigation__item"
:class="{'book-subnavigation__item--mobile': mobile}"
@click.native="hideMobileNavigation"
to="/instruments/gesellschaft">Gesellschaft
</router-link>
</div>
</template>
<script>
export default {
props: {
mobile: {
default: false
}
},
methods: {
hideMobileNavigation() {
this.$store.dispatch('showMobileNavigation', false);
}
}
}
</script>

View File

@ -1,44 +0,0 @@
<template>
<div class="mobile-subnavigation">
<div class="mobile-subnavigation__section">
<h3 class="mobile-subnavigation__title">Themen</h3>
<book-topic-navigation :mobile="true"></book-topic-navigation>
</div>
<div class="mobile-subnavigation__section">
<h3 class="mobile-subnavigation__title">Instrumente</h3>
<instrument-navigation :mobile="true"></instrument-navigation>
</div>
</div>
</template>
<script>
import BookTopicNavigation from '@/components/book-navigation/BookTopicNavigation';
import InstrumentNavigation from '@/components/book-navigation/InstrumentNavigation';
export default {
components: {
BookTopicNavigation,
InstrumentNavigation
},
}
</script>
<style scoped lang="scss">
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.mobile-subnavigation {
&__title {
@include small-text;
color: rgba($color-white, 0.6);
margin-bottom: $small-spacing;
}
&__section {
margin-bottom: $medium-spacing;
}
}
</style>

View File

@ -1,43 +1,53 @@
<template>
<div class="mobile-navigation">
<content-navigation class="mobile-navigation__main" :mobile="true"></content-navigation>
<div class="mobile-navigation__close-button" @click="hideMobileNavigation">
<cross class="mobile-navigation__close-icon"></cross>
<transition name="slide">
<div
v-click-outside="close"
class="navigation-sidebar"
v-if="sidebar.navigation"
>
<content-navigation
:is-sidebar="true"
class="navigation-sidebar__main"/>
<div
class="navigation-sidebar__close-button"
@click="close">
<cross class="navigation-sidebar__close-icon"/>
</div>
</div>
</div>
</transition>
</template>
<script>
import Cross from '@/components/icons/Cross';
import UserWidget from '@/components/UserWidget';
import ContentNavigation from '@/components/ContentNavigation';
import ClassSelectionWidget from '@/components/school-class/ClassSelectionWidget';
import ContentNavigation from '@/components/book-navigation/ContentNavigation';
import sidebarMixin from '@/mixins/sidebar';
import {meQuery} from '@/graphql/queries';
export default {
mixins: [sidebarMixin],
components: {
ContentNavigation,
Cross,
UserWidget,
ClassSelectionWidget
},
methods: {
hideMobileNavigation() {
this.$store.dispatch('showMobileNavigation', false);
}
},
apollo: {
me: meQuery
Cross
},
data() {
return {
me: {}
}
}
},
methods: {
close() {
this.closeSidebar('navigation');
}
},
apollo: {
me: meQuery
},
}
</script>
@ -45,7 +55,9 @@
@import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss";
.mobile-navigation {
$desktop-width: 285px;
.navigation-sidebar {
position: fixed;
left: 0;
right: 0;
@ -54,6 +66,10 @@
background-color: white;
z-index: 20;
@include desktop {
box-shadow: 0px 2px 9px rgba(0, 0, 0, 0.12);
}
display: grid;
grid-template-columns: 1fr 50px;
@ -69,11 +85,10 @@
overflow-y: auto;
@include desktop {
display: none;
width: $desktop-width;
}
&__main {
background-color: $color-brand;
padding: $medium-spacing;
grid-area: m;
}
@ -86,13 +101,20 @@
grid-column: 2;
align-self: center;
justify-self: center;
cursor: pointer;
}
}
.slide {
&-enter-active, &-leave-active {
transition: left 0.2s;
}
&__close-icon {
width: 30px;
height: 30px;
opacity: 0.5;
fill: $color-white;
&-enter, &-leave-to {
left: -100vw;
@include desktop {
left: -$desktop-width;
}
}
}
</style>

View File

@ -1,12 +1,19 @@
<template>
<div class="sub-navigation-item" :class="{ 'sub-navigation-item--active': show}" v-click-outside="close">
<div class="sub-navigation-item__title" @click="show = !show">
{{title}}
<chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down"></chevron-down>
<chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up"></chevron-up>
<div
:class="{ 'sub-navigation-item--active': show}"
v-click-outside="close"
class="sub-navigation-item">
<div
class="sub-navigation-item__title"
@click="show = !show">
{{ title }}
<chevron-down class="sub-navigation-item__icon sub-navigation-item__chevron-down"/>
<chevron-up class="sub-navigation-item__icon sub-navigation-item__chevron-up"/>
</div>
<div class="sub-navigation-item__nav-items book-subnavigation" v-if="show">
<slot></slot>
<div
class="sub-navigation-item__nav-items book-subnavigation"
v-if="show">
<slot/>
</div>
</div>
</template>

View File

@ -1,65 +1,80 @@
<template>
<modal>
<template slot="header">
<modal-input v-on:input="updateTitle"
:placeholder="titlePlaceholder"
:value="localContentBlock.title"
:error="error"
data-cy="modal-title-input"
></modal-input>
<checkbox v-if="taskSelection"
:checked="localContentBlock.isAssignment"
:item="localContentBlock"
:label="'Auftrag'"
@input="setContentBlockType"
class="contents-form__task"
></checkbox>
<modal-input
:placeholder="titlePlaceholder"
:value="localContentBlock.title"
:error="error"
data-cy="modal-title-input"
@input="updateTitle"
/>
<checkbox
:checked="localContentBlock.isAssignment"
:item="localContentBlock"
:label="'Auftrag'"
class="contents-form__task"
v-if="taskSelection"
@input="setContentBlockType"
/>
</template>
<add-content-element class="contents-form__add"
v-on:add-element="addElement"
:index="-1"
></add-content-element>
<div v-for="(element, index) in localContentBlock.contents" :key="index" class="contents-form__element">
<add-content-element
:index="-1"
class="contents-form__add"
@add-element="addElement"
/>
<div
:key="index"
class="contents-form__element"
v-for="(element, index) in localContentBlock.contents">
<component
class="contents-form__element-component"
:is="type(element)"
:class="{'contents-form__chooser': type(element) === 'content-block-element-chooser-widget'}"
v-bind="element" :index="index"
v-on:change-type="changeType"
v-bind="element"
:index="index"
class="contents-form__element-component"
@change-type="changeType"
v-on:link-change-url="changeLinkUrl"
v-on:link-change-text="changeLinkText"
@link-change-url="changeLinkUrl"
@link-change-text="changeLinkText"
v-on:text-change-value="changeTextValue"
@text-change-value="changeTextValue"
v-on:document-change-url="changeDocumentUrl"
@document-change-url="changeDocumentUrl"
v-on:image-change-url="changeImageUrl"
@image-change-url="changeImageUrl"
v-on:video-change-url="changeVideoUrl"
@video-change-url="changeVideoUrl"
@switch-to-document="switchToDocument"
v-on:assignment-change-title="changeAssignmentTitle"
v-on:assignment-change-assignment="changeAssignmentAssignment"
>
</component>
<a class="contents-form__remove icon-button" @click="removeElement(index)">
<trash-icon v-if="type(element) !== 'content-block-element-chooser-widget'"
class="contents-form__trash-icon icon-button__icon"></trash-icon>
@assignment-change-title="changeAssignmentTitle"
@assignment-change-assignment="changeAssignmentAssignment"
/>
<a
class="contents-form__remove icon-button"
@click="removeElement(index)">
<trash-icon
class="contents-form__trash-icon icon-button__icon"
v-if="type(element) !== 'content-block-element-chooser-widget'"/>
</a>
<add-content-element class="contents-form__add"
v-on:add-element="addElement"
:index="index"
></add-content-element>
<add-content-element
:index="index"
class="contents-form__add"
@add-element="addElement"
/>
</div>
<div slot="footer">
<a class="button button--primary" data-cy="modal-save-button" :class="{'button--disabled': disableSave}"
v-on:click="save">Speichern</a>
<a class="button" v-on:click="$emit('hide')">Abbrechen</a>
<a
:class="{'button--disabled': disableSave}"
class="button button--primary"
data-cy="modal-save-button"
@click="save">Speichern</a>
<a
class="button"
@click="$emit('hide')">Abbrechen</a>
</div>
</modal>
</template>

View File

@ -4,7 +4,7 @@
:show-task-selection="true"
@save="saveContentBlock"
@hide="hideModal"
></contents-form>
/>
</template>
<script>
@ -22,6 +22,16 @@
ContentsForm
},
data() {
return {
contentBlock: {}
}
},
created() {
// debugger;
},
methods: {
hideModal() {
this.$store.dispatch('resetCurrentNoteBlock');
@ -52,16 +62,6 @@
}
},
created() {
// debugger;
},
data() {
return {
contentBlock: {}
}
},
apollo: {
contentBlock() {
return {

View File

@ -2,10 +2,10 @@
<contents-form
:content-block="contentBlock"
:show-task-selection="true"
:disable-save="saving"
@save="saveContentBlock"
@hide="hideModal"
:disable-save="saving"
></contents-form>
/>
</template>
<script>
@ -20,6 +20,18 @@
ContentsForm
},
data() {
return {
contentBlock: {
title: '',
contents: [
{}
]
},
saving: false
}
},
methods: {
hideModal() {
this.$store.dispatch('resetContentBlockPosition');
@ -52,17 +64,5 @@
});
}
},
data() {
return {
contentBlock: {
title: '',
contents: [
{}
]
},
saving: false
}
}
}
</script>

View File

@ -1,20 +1,20 @@
<template>
<div class="content-component"
:class="{'content-component--bookmarked': bookmarked}"
:data-scrollto="component.id">
<div
:class="{'content-component--bookmarked': bookmarked}"
:data-scrollto="component.id"
class="content-component">
<bookmark-actions
:bookmarked="bookmarked"
:note="note"
v-if="showBookmarkActions"
@add-note="addNote(component.id)"
@edit-note="editNote"
@bookmark="bookmarkContent(component.id, !bookmarked)"
:bookmarked="bookmarked"
:note="note"></bookmark-actions>
@bookmark="bookmarkContent(component.id, !bookmarked)"/>
<component
:is="component.type"
v-bind="component"
:parent="parent"
>
</component>
/>
</div>
</template>

View File

@ -2,14 +2,15 @@
<div class="content-list-block__container">
<div class="content-list-wrapper">
<ol class="content-list">
<li class="content-list__item contentlist-item"
:key="contentBlock.id"
v-for="(contentBlock, index) in contentBlocks">
<p class="content-list__numbering">{{alphaIndex(index)}})</p>
<li
:key="contentBlock.id"
class="content-list__item contentlist-item"
v-for="(contentBlock, index) in contentBlocks">
<p class="content-list__numbering">{{ alphaIndex(index) }})</p>
<content-block
:contentBlock="contentBlock"
:content-block="contentBlock"
:parent="parent"
></content-block>
/>
</li>
</ol>
</div>
@ -22,20 +23,14 @@
const lowerAsciiA = 97;
export default {
name: 'ContentBlockList',
props: ['contents', 'parent', 'startingIndex'],
name: 'content-block-list',
components: {
// https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
ContentBlock: () => import('@/components/ContentBlock')
},
methods: {
alphaIndex(index) {
return String.fromCharCode(lowerAsciiA + this.startingIndex + index);
}
},
computed: {
contentBlocks() {
return this.contents.map(contentBlock => {
@ -48,7 +43,13 @@
})
});
}
}
},
methods: {
alphaIndex(index) {
return String.fromCharCode(lowerAsciiA + this.startingIndex + index);
}
},
}
</script>

View File

@ -1,9 +1,15 @@
<template>
<div class="document-block">
<document-icon class="document-block__icon"></document-icon>
<a :href="value.url" class="document-block__link" target="_blank">{{urlName}}</a>
<a v-if="showTrashIcon" class="document-block__remove" v-on:click="$emit('trash')">
<trash-icon class="document-block__trash-icon"></trash-icon>
<document-icon class="document-block__icon"/>
<a
:href="value.url"
class="document-block__link"
target="_blank">{{ urlName }}</a>
<a
class="document-block__remove"
v-if="showTrashIcon"
@click="$emit('trash')">
<trash-icon class="document-block__trash-icon"/>
</a>
</div>
</template>

View File

@ -1,16 +1,17 @@
<template>
<div class="genially-block">
<div class="genially-block__wrapper">
<iframe class="genially-block__iframe"
frameborder="0"
width="800px"
height="600px"
:src="src"
type="text/html"
allowscriptaccess="always"
allowfullscreen="true"
scrolling="yes"
allownetworking="all"></iframe>
<iframe
:src="src"
class="genially-block__iframe"
frameborder="0"
width="800px"
height="600px"
type="text/html"
allowscriptaccess="always"
allowfullscreen="true"
scrolling="yes"
allownetworking="all"/>
</div>
</div>
</template>

View File

@ -1,5 +1,9 @@
<template>
<img :src="value.path" alt="" class="image-block" @click="openFullscreen">
<img
:src="value.path"
alt=""
class="image-block"
@click="openFullscreen">
</template>
<script>

View File

@ -1,5 +1,9 @@
<template>
<img :src="value.url" alt="" class="image-block" @click="openFullscreen">
<img
:src="value.url"
alt=""
class="image-block"
@click="openFullscreen">
</template>
<script>

View File

@ -1,8 +1,13 @@
<template>
<div class="infogram-block">
<iframe class="infogram-block__iframe" :src="src"
:title="title"
:height="height" scrolling="no" frameborder="0" style="border:none;"></iframe>
<iframe
:src="src"
:title="title"
:height="height"
class="infogram-block__iframe"
scrolling="no"
frameborder="0"
style="border:none;"/>
</div>
</template>
@ -10,26 +15,9 @@
export default {
props: ['value'],
mounted() {
// from https://developers.infogr.am/oembed/
window.addEventListener('message', event => {
try {
const data = JSON.parse(event.data);
if (data.context === 'iframe.resize') {
this.height = data.height;
}
} catch (e) {
return false;
}
});
},
methods: {
openFullscreen() {
this.$store.dispatch('showFullscreenInfographic', {
id: this.value.id,
type: 'infogram-block'
});
data() {
return {
height: 0
}
},
@ -48,11 +36,33 @@
}
},
data() {
return {
height: 0
mounted() {
// from https://developers.infogr.am/oembed/
window.addEventListener('message', event => {
try {
const data = JSON.parse(event.data);
if (data.context === 'iframe.resize' && this.parseId(data.src) === this.id) {
this.height = data.height;
}
} catch (e) {
return false;
}
});
},
methods: {
parseId(src) {
// src will be in the format of something like https://e.infogram.com/0ccf86bc-1afe-4026-b313-1f1b5992452b?src=embed
let last = src.split('/').pop();
return last.substring(0, last.indexOf('?')); // we're only interested in the id part before the '?'
},
openFullscreen() {
this.$store.dispatch('showFullscreenInfographic', {
id: this.value.id,
type: 'infogram-block'
});
}
}
},
}
</script>

View File

@ -1,7 +1,11 @@
<template>
<div class="instruction" v-if="me.isTeacher">
<bulb-icon class="instruction__icon"></bulb-icon>
<a class="instruction__link" :href="value.url">{{text}}</a>
<div
class="instruction"
v-if="me.isTeacher">
<bulb-icon class="instruction__icon"/>
<a
:href="value.url"
class="instruction__link">{{ text }}</a>
</div>
</template>

View File

@ -1,8 +1,12 @@
<template>
<div class="instrument-widget">
<div class="instrument-widget__description" v-html="value.description"></div>
<router-link class="instrument-widget__button button" tag="button"
:to="{name: 'instrument', params: { slug: value.slug }}">Instrument anzeigen
<div
class="instrument-widget__description"
v-html="value.description"/>
<router-link
:to="{name: 'instrument', params: { slug: value.slug }}"
class="instrument-widget__button button"
tag="button">Instrument anzeigen
</router-link>
</div>
</template>

View File

@ -1,7 +1,12 @@
<template>
<div class="link-block" :class="{ 'link-block--no-margin': noMargin}">
<link-icon class="link-block__icon"></link-icon>
<a :href="href" class="link-block__link" target="_blank">{{value.text}}</a>
<div
:class="{ 'link-block--no-margin': noMargin}"
class="link-block">
<link-icon class="link-block__icon"/>
<a
:href="href"
class="link-block__link"
target="_blank">{{ value.text }}</a>
</div>
</template>

View File

@ -1,6 +1,8 @@
<template>
<div class="module-slug">
<router-link class="button button--primary" :to="{name: 'moduleRoom', params: { slug: value.slug }}">Raum anzeigen
<router-link
:to="{name: 'moduleRoom', params: { slug: value.slug }}"
class="button button--primary">Raum anzeigen
</router-link>
</div>
</template>

View File

@ -1,5 +1,7 @@
<template>
<h4 class="section-title" v-html="value.text"></h4>
<h4
class="section-title"
v-html="value.text"/>
</template>
<script>

View File

@ -1,12 +1,20 @@
<template>
<div class="solution" data-cy="solution">
<a class="solution__toggle" data-cy="show-solution" @click="toggle">Lösung
<div
class="solution"
data-cy="solution">
<a
class="solution__toggle"
data-cy="show-solution"
@click="toggle">Lösung
<template v-if="!visible">anzeigen</template>
<template v-else>ausblenden</template>
</a>
<transition name="fade">
<p class="solution__text solution-text fade" data-cy="solution-text" v-if="visible" v-html="value.text">
</p>
<p
class="solution__text solution-text fade"
data-cy="solution-text"
v-if="visible"
v-html="value.text"/>
</transition>
</div>
</template>
@ -15,17 +23,17 @@
export default {
props: ['value'],
data() {
return {
visible: false
}
},
methods: {
toggle() {
this.visible = !this.visible;
}
},
data() {
return {
visible: false
}
}
}
</script>

View File

@ -1,5 +1,7 @@
<template>
<h5 class="subtitle" v-html="value.text"></h5>
<h5
class="subtitle"
v-html="value.text"/>
</template>
<script>

View File

@ -1,7 +1,10 @@
<template>
<div class="survey-block" :data-scrollto="value.id">
<router-link class="button button--primary"
:to="{name: 'survey', params: {id:value.id}}">Übung anzeigen
<div
:data-scrollto="value.id"
class="survey-block">
<router-link
:to="{name: 'survey', params: {id:value.id}}"
class="button button--primary">Übung anzeigen
</router-link>
</div>
</template>

View File

@ -1,6 +1,8 @@
<template>
<div class="task">
<div class="task__text" v-html="value.text"></div>
<div
class="task__text"
v-html="value.text"/>
</div>
</template>

View File

@ -1,5 +1,7 @@
<template>
<div class="text-block" v-html="value.text"></div>
<div
class="text-block"
v-html="value.text"/>
</template>
<script>

View File

@ -1,18 +1,19 @@
<template>
<div class="thinglink-block">
<div class="thinglink-block__wrapper">
<iframe class="thinglink-block__iframe"
frameborder="0"
width="800px"
height="600px"
:src="src"
type="text/html"
webkitallowfullscreen
mozallowfullscreen
scrolling="no"
allowscriptaccess="always"
allowfullscreen="true"
allownetworking="all"></iframe>
<iframe
:src="src"
class="thinglink-block__iframe"
frameborder="0"
width="800px"
height="600px"
type="text/html"
webkitallowfullscreen
mozallowfullscreen
scrolling="no"
allowscriptaccess="always"
allowfullscreen="true"
allownetworking="all"/>
</div>
</div>
</template>

View File

@ -1,8 +1,14 @@
<template>
<div class="video-block">
<youtube-embed v-if="isYoutube" :url="value.url"></youtube-embed>
<vimeo-embed v-if="isVimeo" :url="value.url"></vimeo-embed>
<srf-embed v-if="isSrf" :url="value.url"></srf-embed>
<youtube-embed
:url="value.url"
v-if="isYoutube"/>
<vimeo-embed
:url="value.url"
v-if="isVimeo"/>
<srf-embed
:url="value.url"
v-if="isSrf"/>
</div>
</template>

View File

@ -1,36 +1,45 @@
<template>
<div class="assignment" :data-scrollto="value.id">
<div
:data-scrollto="value.id"
class="assignment">
<p class="assignment__assignment-text">
{{assignment.assignment}}
{{ assignment.assignment }}
</p>
<solution :value="solution" v-if="assignment.solution"></solution>
<solution
:value="solution"
v-if="assignment.solution"/>
<template v-if="isStudent">
<submission-form
:user-input="submission"
:spellcheck-loading="spellcheckLoading"
:saved="!unsaved"
:spellcheck="true"
placeholder="Ergebnis erfassen"
action="Ergebnis mit Lehrperson teilen"
shared-msg="Das Ergebnis wurde mit der Lehrperson geteilt."
v-if="isStudent"
@turnIn="turnIn"
@saveInput="saveInput"
@reopen="reopen"
@changeDocumentUrl="changeDocumentUrl"
@spellcheck="spellcheck"
:user-input="submission"
:spellcheck-loading="spellcheckLoading"
placeholder="Ergebnis erfassen"
action="Ergebnis mit Lehrperson teilen"
shared-msg="Das Ergebnis wurde mit der Lehrperson geteilt."
:saved="!unsaved"
:spellcheck="true"
>
</submission-form>
/>
<spell-check :corrections="corrections" :text="submission.text"></spell-check>
<spell-check
:corrections="corrections"
:text="submission.text"/>
<p v-if="this.assignment.submission.submissionFeedback" class="assignment__feedback" v-html="feedbackText">
</p>
<p
class="assignment__feedback"
v-if="assignment.submission.submissionFeedback"
v-html="feedbackText"/>
</template>
<template v-if="!isStudent">
<router-link class="button button--primary" :to="{name: 'submissions', params: { id: assignment.id }}">Zu den
<router-link
:to="{name: 'submissions', params: { id: assignment.id }}"
class="button button--primary">Zu den
Ergebnissen
</router-link>
</template>
@ -47,29 +56,35 @@
import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep'
import FinalSubmission from '@/components/content-blocks/assignment/FinalSubmission';
import SubmissionInput from '@/components/content-blocks/assignment/SubmissionInput';
import SubmissionForm from '@/components/content-blocks/assignment/SubmissionForm';
import DocumentForm from '@/components/content-forms/DocumentForm';
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
import Solution from '@/components/content-blocks/Solution';
import SimpleFileUpload from '@/components/SimpleFileUpload';
import SpellCheck from '@/components/content-blocks/assignment/SpellCheck';
export default {
props: ['value'],
components: {
DocumentBlock,
DocumentForm,
SubmissionInput,
FinalSubmission,
Solution,
SimpleFileUpload,
SubmissionForm,
SpellCheck
},
data() {
return {
assignment: {
submission: this.initialSubmission(),
},
me: {
permissions: []
},
inputType: 'text',
unsaved: false,
saving: 0,
corrections: '',
spellcheckLoading: false
}
},
computed: {
...mapGetters(['scrollToAssignmentId']),
final() {
@ -230,22 +245,6 @@
me: {
query: ME_QUERY
}
},
data() {
return {
assignment: {
submission: this.initialSubmission(),
},
me: {
permissions: []
},
inputType: 'text',
unsaved: false,
saving: 0,
corrections: '',
spellcheckLoading: false
}
}
}
</script>

View File

@ -1,14 +1,16 @@
<template>
<div class="final-submission">
<document-block
v-if="userInput.document"
:value="{url: userInput.document}"
class="final-submission__document"
></document-block>
v-if="userInput.document"
/>
<div class="final-submission__explanation">
<info-icon class="final-submission__explanation-icon"></info-icon>
<span class="final-submission__explanation-text">{{sharedMsg}}</span>
<a class="final-submission__reopen" @click="$emit('reopen')">Bearbeiten</a>
<info-icon class="final-submission__explanation-icon"/>
<span class="final-submission__explanation-text">{{ sharedMsg }}</span>
<a
class="final-submission__reopen"
@click="$emit('reopen')">Bearbeiten</a>
</div>
</div>
</template>

View File

@ -1,6 +1,8 @@
<template>
<p class="spellcheck" v-if="corrections">
<span class="inline-title">Rechtschreibung:</span> <span v-html="highlightedText"></span>
<p
class="spellcheck"
v-if="corrections">
<span class="inline-title">Rechtschreibung:</span> <span v-html="highlightedText"/>
</p>
</template>

View File

@ -2,48 +2,51 @@
<div class="feedback__submission submission-form-container">
<div class="submission-form-container__inputs">
<submission-input
@input="saveInput"
:input-text="userInput.text"
:saved="saved"
:final="final"
:placeholder="placeholder"
:reopen="reopenSubmission"
></submission-input>
@input="saveInput"
/>
</div>
<div class="submission-form-container__actions" v-if="!final">
<button class="submission-form-container__submit button button--primary button--white-bg"
@click="$emit('turnIn')"
>{{action}}
<div
class="submission-form-container__actions"
v-if="!final">
<button
class="submission-form-container__submit button button--primary button--white-bg"
@click="$emit('turnIn')"
>{{ action }}
</button>
<button
class="submission-form-container__submit submission-form-container__spellcheck button button--primary button--white-bg"
v-if="spellcheck"
@click="$emit('spellcheck')"
>{{spellcheckText}}
>{{ spellcheckText }}
</button>
<div v-if="userInput.document">
<document-block
:value="{url: userInput.document}"
show-trash-icon
v-on:trash="changeDocumentUrl('')"
></document-block>
@trash="changeDocumentUrl('')"
/>
</div>
<simple-file-upload
v-if="allowsDocuments"
v-on:link-change-url="changeDocumentUrl"
:value="userInput.document"
class="submission-form-container__document"
></simple-file-upload>
<slot></slot>
v-if="allowsDocuments"
@link-change-url="changeDocumentUrl"
/>
<slot/>
</div>
<final-submission
v-if="final"
:user-input="userInput"
:shared-msg="sharedMsg"
@reopen="$emit('reopen')"></final-submission>
v-if="final"
@reopen="$emit('reopen')"/>
</div>
</template>

View File

@ -1,19 +1,23 @@
<template>
<div class="submission-form__text-answer submission-form">
<textarea
v-auto-grow
rows="1"
class="submission-form__textarea"
:placeholder="placeholder"
:readonly="final"
:value="inputText"
@input="$emit('input', $event.target.value)"
></textarea>
<div class="submission-form__save-status submission-form__save-status--saved" v-if="saved">
<tick-circle-icon class="submission-form__save-status-icon"></tick-circle-icon>
<textarea
v-auto-grow
:placeholder="placeholder"
:readonly="final"
:value="inputText"
rows="1"
class="submission-form__textarea"
@input="$emit('input', $event.target.value)"
/>
<div
class="submission-form__save-status submission-form__save-status--saved"
v-if="saved">
<tick-circle-icon class="submission-form__save-status-icon"/>
</div>
<div class="submission-form__save-status submission-form__save-status--unsaved" v-if="!saved">
<loading-icon class="submission-form__save-status-icon submission-form__saving-icon"></loading-icon>
<div
class="submission-form__save-status submission-form__save-status--unsaved"
v-if="!saved">
<loading-icon class="submission-form__save-status-icon submission-form__saving-icon"/>
</div>
</div>
</template>

View File

@ -1,16 +1,18 @@
<template>
<div class="assignment-form">
<input class="assignment-form__title skillbox-input"
placeholder="Aufgabentitel"
:value="value.title"
v-on:input="$emit('assignment-change-title', $event.target.value, index)"
<input
:value="value.title"
class="assignment-form__title skillbox-input"
placeholder="Aufgabentitel"
@input="$emit('assignment-change-title', $event.target.value, index)"
>
<textarea class="assignment-form__exercise-text skillbox-textarea"
placeholder="Aufgabe erfassen..."
:value="value.assignment"
v-on:input="$emit('assignment-change-assignment', $event.target.value, index)"
></textarea>
<info-icon class="assignment-form__help-icon help-text__icon"></info-icon>
<textarea
:value="value.assignment"
class="assignment-form__exercise-text skillbox-textarea"
placeholder="Aufgabe erfassen..."
@input="$emit('assignment-change-assignment', $event.target.value, index)"
/>
<info-icon class="assignment-form__help-icon help-text__icon"/>
<p class="assignment-form__help-description help-text__description">
Ein Eingabefeld für die Antwort wird automatisch hinzugefügt.
</p>

View File

@ -1,41 +1,48 @@
<template>
<div class="content-block-element-chooser-widget"
:class="{'content-block-element-chooser-widget--no-assignment': this.hideAssignment}">
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--link"
data-cy="choose-link-widget"
v-on:click="$emit('change-type', index, 'link_block')">
<link-icon class="content-block-element-chooser-widget__link-icon"></link-icon>
<div
:class="{'content-block-element-chooser-widget--no-assignment': hideAssignment}"
class="content-block-element-chooser-widget">
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--link"
data-cy="choose-link-widget"
@click="$emit('change-type', index, 'link_block')">
<link-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Link</div>
</div>
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--video"
data-cy="choose-video-widget"
v-on:click="$emit('change-type', index, 'video_block')">
<video-icon class="content-block-element-chooser-widget__link-icon"></video-icon>
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--video"
data-cy="choose-video-widget"
@click="$emit('change-type', index, 'video_block')">
<video-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Video</div>
</div>
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--image"
data-cy="choose-image-widget"
v-on:click="$emit('change-type', index, 'image_url_block')">
<image-icon class="content-block-element-chooser-widget__link-icon"></image-icon>
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--image"
data-cy="choose-image-widget"
@click="$emit('change-type', index, 'image_url_block')">
<image-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Bild</div>
</div>
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--text"
data-cy="choose-text-widget"
v-on:click="$emit('change-type', index, 'text_block')">
<text-icon class="content-block-element-chooser-widget__link-icon"></text-icon>
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--text"
data-cy="choose-text-widget"
@click="$emit('change-type', index, 'text_block')">
<text-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Text</div>
</div>
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--assignment"
data-cy="choose-assignment-widget"
v-on:click="$emit('change-type', index, 'assignment')"
v-if="!hideAssignment">
<speech-bubble-icon class="content-block-element-chooser-widget__link-icon"></speech-bubble-icon>
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--assignment"
data-cy="choose-assignment-widget"
v-if="!hideAssignment"
@click="$emit('change-type', index, 'assignment')">
<speech-bubble-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Aufgabe&nbsp;& Ergebnis</div>
</div>
<div class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--document"
data-cy="choose-document-widget"
v-on:click="$emit('change-type', index, 'document_block')">
<document-icon class="content-block-element-chooser-widget__link-icon"></document-icon>
<div
class="content-block-element-chooser-widget__link content-block-element-chooser-widget__link--document"
data-cy="choose-document-widget"
@click="$emit('change-type', index, 'document_block')">
<document-icon class="content-block-element-chooser-widget__link-icon"/>
<div class="content-block-element-chooser-widget__link-title">Dokument</div>
</div>
</div>

View File

@ -1,9 +1,18 @@
<template>
<div class="document-form" ref="documentform">
<div v-if="!value.url" ref="uploadcare-panel"></div>
<div v-if="value.url" class="document-form__uploaded">
<document-icon class="document-form__icon"></document-icon>
<a :href="previewUrl" class="document-form__link" target="_blank">{{previewLink}}</a>
<div
class="document-form"
ref="documentform">
<div
v-if="!value.url"
ref="uploadcare-panel"/>
<div
class="document-form__uploaded"
v-if="value.url">
<document-icon class="document-form__icon"/>
<a
:href="previewUrl"
class="document-form__link"
target="_blank">{{ previewLink }}</a>
</div>
</div>
</template>
@ -20,12 +29,6 @@
DocumentIcon
},
mounted() {
uploadcare(this, url => {
this.$emit('link-change-url', url, this.index)
});
},
computed: {
previewUrl() {
if (this.value && this.value.url) {
@ -40,7 +43,13 @@
}
return '';
}
}
},
mounted() {
uploadcare(this, url => {
this.$emit('link-change-url', url, this.index)
});
},
}
</script>

View File

@ -1,13 +1,20 @@
<template>
<div class="image-form">
<div v-if="hadError" class="image-form__error">
<div
class="image-form__error"
v-if="hadError">
Ups, das scheint kein Bild zu sein. Bitte versuche es nochmal mit einer anderen Datei, oder lade die Datei als <a
class="image-form__link" @click="switchToDocument">Dokument</a> hoch.
class="image-form__link"
@click="switchToDocument">Dokument</a> hoch.
</div>
<div v-if="!value.url" ref="uploadcare-panel"></div>
<div
v-if="!value.url"
ref="uploadcare-panel"/>
<div v-if="value.url && !hadError">
<img :src="previewUrl" @error="error">
<img
:src="previewUrl"
@error="error">
</div>
</div>
</template>
@ -17,9 +24,6 @@
export default {
props: ['value', 'index'],
mounted() {
this.mountUploadcare();
},
data() {
return {
hadError: false,
@ -36,6 +40,9 @@
return null;
}
},
mounted() {
this.mountUploadcare();
},
methods: {
error(e) {
this.hadError = true;

View File

@ -1,8 +1,16 @@
<template>
<div class="link-form">
<input placeholder="Name erfassen..." class="link-form__text skillbox-input" :value="value.text" v-on:input="$emit('link-change-text', $event.target.value, index)">
<input
:value="value.text"
placeholder="Name erfassen..."
class="link-form__text skillbox-input"
@input="$emit('link-change-text', $event.target.value, index)">
<input placeholder="URL einfügen..." class="link-form__url skillbox-input" :value="value.url" v-on:input="$emit('link-change-url', $event.target.value, index)">
<input
:value="value.url"
placeholder="URL einfügen..."
class="link-form__url skillbox-input"
@input="$emit('link-change-url', $event.target.value, index)">
</div>
</template>

View File

@ -1,10 +1,11 @@
<template>
<div class="text-form">
<textarea class="text-form__input skillbox-textarea"
data-cy="text-form-input"
placeholder="Text erfassen..."
:value="text"
v-on:input="$emit('text-change-value', $event.target.value, index)"></textarea>
<textarea
:value="text"
class="text-form__input skillbox-textarea"
data-cy="text-form-input"
placeholder="Text erfassen..."
@input="$emit('text-change-value', $event.target.value, index)"/>
</div>
</template>

View File

@ -1,9 +1,11 @@
<template>
<div class="text-form-with-help-text">
<h3 class="text-form-with-help-text__heading"><span class="text-form-with-help-text__title">{{title}}</span>
<helpful-tooltip :text="helpText"></helpful-tooltip>
<h3 class="text-form-with-help-text__heading"><span class="text-form-with-help-text__title">{{ title }}</span>
<helpful-tooltip :text="helpText"/>
</h3>
<text-form @text-change-value="$emit('change', $event)" :value="v"></text-form>
<text-form
:value="v"
@text-change-value="$emit('change', $event)"/>
</div>
</template>

View File

@ -1,27 +1,36 @@
<template>
<div>
<div v-if="!isVimeo && !isYoutube && !isSrf" class="video-form">
<info-icon class="video-form__help-icon help-text__icon"></info-icon>
<div
class="video-form"
v-if="!isVimeo && !isYoutube && !isSrf">
<info-icon class="video-form__help-icon help-text__icon"/>
<p class="video-form__help-description help-text__description">
Sie können Videos auf <a class="video-form__platform-link help-text__link" href="https://youtube.com/"
target="_blank">Youtube</a>
oder <a class="video-form__platform-link help-text__link" href="https://vimeo.com/" target="_blank">Vimeo</a>
Sie können Videos auf <a
class="video-form__platform-link help-text__link"
href="https://youtube.com/"
target="_blank">Youtube</a>
oder <a
class="video-form__platform-link help-text__link"
href="https://vimeo.com/"
target="_blank">Vimeo</a>
hochladen und anschliessen einen Link hier einfügen.
</p>
<input class="video-form__video-link skillbox-input"
placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
:value="value.url" v-on:input="$emit('video-change-url', $event.target.value, index)">
<input
:value="value.url"
class="video-form__video-link skillbox-input"
placeholder="Bsp: https://www.youtube.com/watch?v=dQw4w9WgXcQ"
@input="$emit('video-change-url', $event.target.value, index)">
</div>
<div v-if="isYoutube">
<youtube-embed :url="value.url"></youtube-embed>
<youtube-embed :url="value.url"/>
</div>
<div v-if="isVimeo">
<vimeo-embed :url="value.url"></vimeo-embed>
<vimeo-embed :url="value.url"/>
</div>
<div v-if="isSrf">
<srf-embed :url="value.url"></srf-embed>
<srf-embed :url="value.url"/>
</div>
</div>

View File

@ -1,5 +1,8 @@
<template>
<svg id="shape" xmlns="http://www.w3.org/2000/svg" viewBox="6 6 88 88">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="6 6 88 88"
id="shape">
<path
d="M50,6.48A43.62,43.62,0,0,0,6.56,47.08S6.5,48.46,6.5,50s.06,2.83.06,2.92A43.52,43.52,0,1,0,50,6.48Zm0,82.15A38.62,38.62,0,1,1,88.6,50,38.67,38.67,0,0,1,50,88.62Z"/>
<path

View File

@ -1,5 +1,8 @@
<template>
<svg class="add-note-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<svg
class="add-note-icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100">
<path
d="M81.5,88.39746H18.7627a2.49981,2.49981,0,0,1-2.5-2.5V35.35352L1.72559,20.71191A2.50054,2.50054,0,0,1,3.5,16.4502h78a2.49981,2.49981,0,0,1,2.5,2.5V85.89746A2.49981,2.49981,0,0,1,81.5,88.39746Zm-60.2373-5H79V21.4502H9.50488L20.53711,32.56152a2.5013,2.5013,0,0,1,.72559,1.76172Z"/>
<path d="M64.9209,55.08447H36.18457a2.5,2.5,0,0,1,0-5H64.9209a2.5,2.5,0,0,1,0,5Z"/>

View File

@ -1,5 +1,8 @@
<template>
<svg id="shape" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
id="shape">
<path
d="M61.74,13.24a36.43,36.43,0,0,0-22.06,7.35L2.52,48a2.5,2.5,0,0,0,0,4L39.66,79.39A36.76,36.76,0,1,0,61.74,13.24Zm0,68.52a31.5,31.5,0,0,1-19.09-6.38L8.21,50,42.66,24.6A31.76,31.76,0,1,1,61.74,81.76Z"/>
<path

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