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

322
Pipfile.lock generated
View File

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

View File

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

View File

@ -11,7 +11,8 @@ module.exports = {
extends: [ extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention // 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. // 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 // https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard' 'standard'
], ],
@ -29,5 +30,46 @@ module.exports = {
'semi': 0, 'semi': 0,
'space-before-function-paren': 'off', 'space-before-function-paren': 'off',
'comma-dangle': '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, { module.exports = merge(prodEnv, {
NODE_ENV: '"development"', NODE_ENV: '"development"',
HEP_URL: JSON.stringify(process.env.HEP_URL),
MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST), MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST),
MATOMO_SITE_ID: JSON.stringify(process.env.MATOMO_SITE_ID), MATOMO_SITE_ID: JSON.stringify(process.env.MATOMO_SITE_ID),
}); });

View File

@ -1,6 +1,7 @@
'use strict' 'use strict'
module.exports = { module.exports = {
NODE_ENV: '"production"', NODE_ENV: '"production"',
HEP_URL: JSON.stringify(process.env.HEP_URL),
MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST), MATOMO_HOST: JSON.stringify(process.env.MATOMO_HOST),
MATOMO_SITE_ID: JSON.stringify(process.env.MATOMO_SITE_ID), 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', () => { it('login and redirect to main page', () => {
const username = 'test'; const username = 'test';
const password = 'test'; const password = 'test';
cy.visit('/beta-login');
cy.visit('/');
cy.login(username, password, true); cy.login(username, password, true);
cy.get('body').contains('Neues Wissen erwerben'); cy.get('body').contains('Neues Wissen erwerben');
}); });
@ -12,7 +11,7 @@ describe('The Login Page', () => {
const username = ''; const username = '';
const password = 'test'; const password = 'test';
cy.visit('/'); cy.visit('/beta-login');
cy.login(username, password); cy.login(username, password);
cy.get('[data-cy=email-local-errors]').contains('E-Mail ist ein Pflichtfeld'); 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 username = 'test';
const password = ''; const password = '';
cy.visit('/'); cy.visit('/beta-login');
cy.login(username, password); cy.login(username, password);
cy.get('[data-cy=password-local-errors]').contains('Passwort ist ein Pflichtfeld'); cy.get('[data-cy=password-local-errors]').contains('Passwort ist ein Pflichtfeld');
}); });
@ -30,23 +29,18 @@ describe('The Login Page', () => {
const username = 'test'; const username = 'test';
const password = '12345'; const password = '12345';
cy.visit('/'); cy.visit('/beta-login');
cy.login(username, password); cy.login(username, password);
cy.get('[data-cy=login-error]').contains('Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.'); cy.get('[data-cy=login-error]').contains('Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.');
}); });
it('redirect after login', () => { it('logs out then logs in again', () => {
const username = 'test';
const password = 'test';
cy.visit('/book/topic/berufliche-grundbildung'); const user = 'rahel.cueni';
cy.login(username, password); const pw = 'test'
cy.get('body').contains('Berufliche Grundbildung');
});
it.only('logs out then logs in again', () => {
cy.viewport('macbook-15'); cy.viewport('macbook-15');
cy.apolloLogin('rahel.cueni', 'test'); cy.apolloLogin(user, pw);
cy.visit('/me/my-class'); cy.visit('/me/my-class');
cy.get('[data-cy=header-user-widget]').should('exist').within(() => { cy.get('[data-cy=header-user-widget]').should('exist').within(() => {
cy.get('[data-cy=user-widget-avatar]').should('exist').click(); 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.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=header-user-widget]').should('exist').within(() => {
cy.get('[data-cy=user-widget-avatar]').should('exist').click(); 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', 'slug': 'lohn-und-budget',
'__typename': 'ModuleNode' '__typename': 'ModuleNode'
}, },
'lastTopic': {
'id': 'VG9waWNOb2RlOjU=',
'slug': 'geld-und-kauf',
'__typename': 'TopicNode'
},
'__typename': 'UserNode', '__typename': 'UserNode',
'permissions': [] '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 // todo: mock all the graphql queries and mutations
cy.viewport('macbook-15'); cy.viewport('macbook-15');
cy.apolloLogin('rahel.cueni', 'test');
cy.visit('/room/ein-historisches-festival'); cy.visit('/room/ein-historisches-festival');
cy.login('rahel.cueni', 'test');
cy.get('[data-cy=add-room-entry-button]').click(); cy.get('[data-cy=add-room-entry-button]').click();
cy.get('.add-content-element:first-of-type').click(); cy.get('.add-content-element:first-of-type').click();

View File

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

View File

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

View File

@ -29,16 +29,17 @@
// import 'cypress-graphql-mock'; // import 'cypress-graphql-mock';
import '@iam4x/cypress-graphql-mock'; import '@iam4x/cypress-graphql-mock';
Cypress.Commands.add('apolloLogin', (username, password) => { Cypress.Commands.add('apolloLogin', (username, password) => {
const payload = { const payload = {
'operationName': 'Login', 'operationName': 'BetaLogin',
'variables': { 'variables': {
'input': { 'input': {
'usernameInput': username, 'usernameInput': username,
'passwordInput': password '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({ cy.request({
@ -53,7 +54,7 @@ Cypress.Commands.add('apolloLogin', (username, password) => {
// todo: replace with apollo call // todo: replace with apollo call
Cypress.Commands.add("login", (username, password, visitLogin = false) => { Cypress.Commands.add("login", (username, password, visitLogin = false) => {
if (visitLogin) { if (visitLogin) {
cy.visit('/login'); cy.visit('/beta-login');
} }
if (username != '') { if (username != '') {
@ -110,22 +111,59 @@ Cypress.Commands.add('changePassword', (oldPassword, newPassword) => {
cy.get('[data-cy=change-password-button]').click(); cy.get('[data-cy=change-password-button]').click();
}); });
Cypress.Commands.add('register', (firstname, lastname, email, licenseKey) => { Cypress.Commands.add('checkEmailAvailable', (email) => {
if (firstname != '') { cy.get('[data-cy="email-input"]').type(email);
cy.get('[data-cy=firstname-input]').type(firstname); cy.get('[data-cy="hello-button"]').click();
}
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('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": { "chalk": {
"version": "1.1.3", "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=", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -2492,7 +2492,7 @@
}, },
"onetime": { "onetime": {
"version": "1.1.0", "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=", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true "dev": true
}, },
@ -9130,7 +9130,8 @@
}, },
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true "bundled": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -9148,11 +9149,13 @@
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true "bundled": true,
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -9165,15 +9168,18 @@
}, },
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true "bundled": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true "bundled": true,
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true "bundled": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -9276,7 +9282,8 @@
}, },
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true "bundled": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -9286,6 +9293,7 @@
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -9298,17 +9306,20 @@
"minimatch": { "minimatch": {
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
}, },
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true "bundled": true,
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.2.4", "version": "2.2.4",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -9325,6 +9336,7 @@
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -9397,7 +9409,8 @@
}, },
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true "bundled": true,
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@ -9407,6 +9420,7 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -9482,7 +9496,8 @@
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.1.1",
"bundled": true "bundled": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -9512,6 +9527,7 @@
"string-width": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -9529,6 +9545,7 @@
"strip-ansi": { "strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -9567,18 +9584,20 @@
}, },
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true "bundled": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.2", "version": "3.0.2",
"bundled": true "bundled": true,
"optional": true
} }
} }
}, },
"fstream": { "fstream": {
"version": "1.0.11", "version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"requires": { "requires": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"inherits": "~2.0.0", "inherits": "~2.0.0",
@ -9791,13 +9810,20 @@
} }
}, },
"globule": { "globule": {
"version": "1.2.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
"integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
"requires": { "requires": {
"glob": "~7.1.1", "glob": "~7.1.1",
"lodash": "~4.17.10", "lodash": "~4.17.12",
"minimatch": "~3.0.2" "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": { "graceful-fs": {
@ -10406,9 +10432,9 @@
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
}, },
"in-publish": { "in-publish": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ=="
}, },
"indent-string": { "indent-string": {
"version": "2.1.0", "version": "2.1.0",
@ -11617,7 +11643,8 @@
"ansi-regex": { "ansi-regex": {
"version": "2.1.1", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@ -11660,7 +11687,8 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
@ -11671,7 +11699,8 @@
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@ -11788,7 +11817,8 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@ -11800,6 +11830,7 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@ -11829,6 +11860,7 @@
"version": "2.3.5", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.2", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@ -11847,6 +11879,7 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@ -11940,6 +11973,7 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@ -12025,7 +12059,8 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@ -12061,6 +12096,7 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@ -12080,6 +12116,7 @@
"version": "3.0.1", "version": "3.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@ -12123,12 +12160,14 @@
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.3", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true "dev": true,
"optional": true
} }
} }
}, },
@ -13302,7 +13341,7 @@
}, },
"chalk": { "chalk": {
"version": "1.1.3", "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=", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true, "dev": true,
"requires": { "requires": {
@ -13485,21 +13524,11 @@
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
"dev": true "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": { "lodash.camelcase": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" "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": { "lodash.create": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", "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", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" "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": { "lodash.once": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@ -14145,7 +14169,8 @@
"nan": { "nan": {
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", "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": { "nanomatch": {
"version": "1.2.13", "version": "1.2.13",
@ -14433,9 +14458,9 @@
} }
}, },
"node-sass": { "node-sass": {
"version": "4.9.2", "version": "4.13.1",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
"integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==", "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
"requires": { "requires": {
"async-foreach": "^0.1.3", "async-foreach": "^0.1.3",
"chalk": "^1.1.1", "chalk": "^1.1.1",
@ -14444,20 +14469,29 @@
"get-stdin": "^4.0.1", "get-stdin": "^4.0.1",
"glob": "^7.0.3", "glob": "^7.0.3",
"in-publish": "^2.0.0", "in-publish": "^2.0.0",
"lodash.assign": "^4.2.0", "lodash": "^4.17.15",
"lodash.clonedeep": "^4.3.2",
"lodash.mergewith": "^4.6.0",
"meow": "^3.7.0", "meow": "^3.7.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"nan": "^2.10.0", "nan": "^2.13.2",
"node-gyp": "^3.3.1", "node-gyp": "^3.8.0",
"npmlog": "^4.0.0", "npmlog": "^4.0.0",
"request": "2.87.0", "request": "^2.88.0",
"sass-graph": "^2.2.4", "sass-graph": "^2.2.4",
"stdout-stream": "^1.4.0", "stdout-stream": "^1.4.0",
"true-case-path": "^1.0.2" "true-case-path": "^1.0.2"
}, },
"dependencies": { "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": { "ansi-styles": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
@ -14484,10 +14518,80 @@
"which": "^1.2.9" "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": { "supports-color": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" "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": { "psl": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
"dev": true
}, },
"public-encrypt": { "public-encrypt": {
"version": "4.0.2", "version": "4.0.2",
@ -18560,9 +18663,9 @@
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
}, },
"stdout-stream": { "stdout-stream": {
"version": "1.4.0", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
"integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
"requires": { "requires": {
"readable-stream": "^2.0.1" "readable-stream": "^2.0.1"
} }
@ -18773,12 +18876,12 @@
"integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
}, },
"tar": { "tar": {
"version": "2.2.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"requires": { "requires": {
"block-stream": "*", "block-stream": "*",
"fstream": "^1.0.2", "fstream": "^1.0.12",
"inherits": "2" "inherits": "2"
} }
}, },
@ -19066,25 +19169,11 @@
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
}, },
"true-case-path": { "true-case-path": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
"integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
"requires": { "requires": {
"glob": "^6.0.4" "glob": "^7.1.2"
},
"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"
}
}
} }
}, },
"tryer": { "tryer": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,33 +1,41 @@
<template> <template>
<div class="assignment-with-submissions"> <div class="assignment-with-submissions">
<p class="assignment-with-submissions__text">{{assignment.assignment}}</p> <p class="assignment-with-submissions__text">{{ assignment.assignment }}</p>
<div> <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>
<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> <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> </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"> <div class="submissions__header student-submission-row submission-header">
<p class="submission-header__title">Lernende</p> <p class="submission-header__title">Lernende</p>
<p class="submission-header__title">Ergebnisse</p> <p class="submission-header__title">Ergebnisse</p>
<p class="submission-header__title">Feedback</p> <p class="submission-header__title">Feedback</p>
</div> </div>
<router-link <router-link
:to="submissionLink(submission)" :to="submissionLink(submission)"
v-for="submission in submissions" :key="submission.id"
class="assignment-with-submissions__link" class="assignment-with-submissions__link"
:key="submission.id"> v-for="submission in submissions">
<student-submission class="assignment-with-submissions__submission" <student-submission
:submission="submission" :submission="submission"
> class="assignment-with-submissions__submission"
</student-submission> />
</router-link> </router-link>
</div> </div>
</div> </div>
@ -45,15 +53,9 @@
StudentSubmission StudentSubmission
}, },
methods: { data() {
submissionLink(submission) { return {
return `/submission/${submission.id}` me: {}
},
belongsToSchool(submission) {
if (this.currentFilter.id === '') {
return true;
}
return submission.student.schoolClasses.edges.some(edge => edge.node.id === this.currentFilter.id)
} }
}, },
@ -68,9 +70,15 @@
}, },
}, },
data() { methods: {
return { submissionLink(submission) {
me: {} 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> <template>
<div class="chapter" :data-scrollto="chapter.id"> <div
<h3 :id="'chapter-' + index">{{chapter.title}}</h3> :data-scrollto="chapter.id"
class="chapter">
<h3 :id="'chapter-' + index">{{ chapter.title }}</h3>
<bookmark-actions <bookmark-actions
:bookmarked="chapter.bookmark"
:note="note"
class="chapter__bookmark-actions" class="chapter__bookmark-actions"
@add-note="addNote" @add-note="addNote"
@edit-note="editNote" @edit-note="editNote"
:bookmarked="chapter.bookmark"
@bookmark="bookmark(!chapter.bookmark)" @bookmark="bookmark(!chapter.bookmark)"
:note="note" />
></bookmark-actions>
<p class="chapter__description"> <p class="chapter__description">
{{chapter.description}} {{ chapter.description }}
</p> </p>
<add-content-button :parent="chapter" v-if="editModule"></add-content-button> <add-content-button
:parent="chapter"
v-if="editModule"/>
<content-block :contentBlock="contentBlock" <content-block
:parent="chapter.id" :content-block="contentBlock"
:key="contentBlock.id" v-for="contentBlock in filteredContentBlocks"> :parent="chapter.id"
</content-block> :key="contentBlock.id"
v-for="contentBlock in filteredContentBlocks"/>
</div> </div>
</template> </template>
@ -44,6 +49,12 @@
AddContentButton AddContentButton
}, },
data() {
return {
me: {}
}
},
computed: { computed: {
...mapGetters(['editModule']), ...mapGetters(['editModule']),
filteredContentBlocks() { filteredContentBlocks() {
@ -66,12 +77,6 @@
} }
}, },
data() {
return {
me: {}
}
},
methods: { methods: {
bookmark(bookmarked) { bookmark(bookmarked) {
const id = this.chapter.id; const id = this.chapter.id;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="helpful-tooltip"> <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__tooltip">
<div class="helpful-tooltip__text"> <div class="helpful-tooltip__text">
{{text}} {{ text }}
</div> </div>
</div> </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> <template>
<div class="logout-widget"> <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> </div>
</template> </template>
@ -25,12 +28,11 @@
@import "@/styles/_mixins.scss"; @import "@/styles/_mixins.scss";
.logout-widget { .logout-widget {
color: $color-silver-dark;
display: flex; display: flex;
align-items: center; align-items: center;
&__logout { &__logout {
@include regular-text; @include default-link;
cursor: pointer; cursor: pointer;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,16 @@
<template> <template>
<div class="news-teaser"> <div class="news-teaser">
<h4 class="small-emph"> <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> </h4>
<a :href="url" target="_blank" class="teaser__date"> <a
{{date}} :href="url"
target="_blank"
class="teaser__date">
{{ date }}
</a> </a>
</div> </div>
</template> </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> <template>
<base-input :label="label" <base-input
:checked="checked" :label="label"
:item="item" :checked="checked"
v-on:input="passOn" :item="item"
:type="'radiobutton'" :type="'radiobutton'"
></base-input> @input="passOn"
/>
</template> </template>
<script> <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> <template>
<div class="section-block"> <div class="section-block">
<div class="section-block__illustration" @click="navigate()" :class="{'section-block--navigatable': route}"> <div
<slot></slot> :class="{'section-block--navigatable': route}"
class="section-block__illustration"
@click="navigate()">
<slot/>
</div> </div>
<div class="section-block__title block-title" @click="navigate()" :class="{'section-block--navigatable': route}"> <div
<h2 class="block-title__title">{{title}}</h2> :class="{'section-block--navigatable': route}"
<h3 class="block-title__subtitle small-emph">{{subtitle}}</h3> 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>
<div class="section-block__content section-content"> <div class="section-block__content section-content">
<div class="section-content__subsection subsection"> <div class="section-content__subsection subsection">
<a class="subsection__content button button--primary" v-if="route" @click="navigate()" <a
:class="{'section-block--navigatable': route}"> :class="{'section-block--navigatable': route}"
{{linkText}} class="subsection__content button button--primary"
v-if="route"
@click="navigate()">
{{ linkText }}
</a> </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> </div>
</div> </div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,10 @@
<template> <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"> <ul class="widget-popover__links popover-links">
<slot></slot> <slot/>
</ul> </ul>
</div> </div>
</template> </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> <template>
<nav class="book-topics"> <nav class="book-topics">
<router-link :to="{name: 'topic', params: {topicSlug: topic.slug}}" <router-link
@click.native="hideMobileNavigation" :to="{name: 'topic', params: {topicSlug: topic.slug}}"
tag="div" :class="{'book-topics__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile}"
class="book-topics__topic book-subnavigation__item" :key="topic.id"
:class="{'book-topics__topic--active': topic.active, 'book-subnavigation__item--mobile': mobile}" tag="div"
v-for="topic in topics" active-class="book-subnavigation__item--active"
:key="topic.id"> class="book-topics__topic book-subnavigation__item"
{{topic.order}}. v-for="topic in topics"
{{topic.title}} @click.native="closeSidebar('navigation')">
{{ topic.order }}.
{{ topic.title }}
</router-link> </router-link>
</nav> </nav>
</template> </template>
<script> <script>
import ALL_TOPICS_QUERY from '@/graphql/gql/allTopicsQuery.gql'; import ALL_TOPICS_QUERY from '@/graphql/gql/allTopicsQuery.gql';
import sidebarMixin from '@/mixins/sidebar';
export default { export default {
props: { props: {
@ -23,6 +26,8 @@
} }
}, },
mixins: [sidebarMixin],
data() { data() {
return { return {
topics: [] topics: []
@ -32,9 +37,6 @@
methods: { methods: {
topicId(id) { topicId(id) {
return atob(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> <template>
<div class="mobile-navigation"> <transition name="slide">
<content-navigation class="mobile-navigation__main" :mobile="true"></content-navigation> <div
<div class="mobile-navigation__close-button" @click="hideMobileNavigation"> v-click-outside="close"
<cross class="mobile-navigation__close-icon"></cross> 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>
</div> </transition>
</template> </template>
<script> <script>
import Cross from '@/components/icons/Cross'; import Cross from '@/components/icons/Cross';
import UserWidget from '@/components/UserWidget'; import ContentNavigation from '@/components/book-navigation/ContentNavigation';
import ContentNavigation from '@/components/ContentNavigation';
import ClassSelectionWidget from '@/components/school-class/ClassSelectionWidget'; import sidebarMixin from '@/mixins/sidebar';
import {meQuery} from '@/graphql/queries'; import {meQuery} from '@/graphql/queries';
export default { export default {
mixins: [sidebarMixin],
components: { components: {
ContentNavigation, ContentNavigation,
Cross, Cross
UserWidget,
ClassSelectionWidget
},
methods: {
hideMobileNavigation() {
this.$store.dispatch('showMobileNavigation', false);
}
},
apollo: {
me: meQuery
}, },
data() { data() {
return { return {
me: {} me: {}
} }
} },
methods: {
close() {
this.closeSidebar('navigation');
}
},
apollo: {
me: meQuery
},
} }
</script> </script>
@ -45,7 +55,9 @@
@import "@/styles/_variables.scss"; @import "@/styles/_variables.scss";
@import "@/styles/_mixins.scss"; @import "@/styles/_mixins.scss";
.mobile-navigation { $desktop-width: 285px;
.navigation-sidebar {
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;
@ -54,6 +66,10 @@
background-color: white; background-color: white;
z-index: 20; z-index: 20;
@include desktop {
box-shadow: 0px 2px 9px rgba(0, 0, 0, 0.12);
}
display: grid; display: grid;
grid-template-columns: 1fr 50px; grid-template-columns: 1fr 50px;
@ -69,11 +85,10 @@
overflow-y: auto; overflow-y: auto;
@include desktop { @include desktop {
display: none; width: $desktop-width;
} }
&__main { &__main {
background-color: $color-brand;
padding: $medium-spacing; padding: $medium-spacing;
grid-area: m; grid-area: m;
} }
@ -86,13 +101,20 @@
grid-column: 2; grid-column: 2;
align-self: center; align-self: center;
justify-self: center; justify-self: center;
cursor: pointer;
}
}
.slide {
&-enter-active, &-leave-active {
transition: left 0.2s;
} }
&__close-icon { &-enter, &-leave-to {
width: 30px; left: -100vw;
height: 30px; @include desktop {
opacity: 0.5; left: -$desktop-width;
fill: $color-white; }
} }
} }
</style> </style>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,13 @@
<template> <template>
<div class="infogram-block"> <div class="infogram-block">
<iframe class="infogram-block__iframe" :src="src" <iframe
:title="title" :src="src"
:height="height" scrolling="no" frameborder="0" style="border:none;"></iframe> :title="title"
:height="height"
class="infogram-block__iframe"
scrolling="no"
frameborder="0"
style="border:none;"/>
</div> </div>
</template> </template>
@ -10,26 +15,9 @@
export default { export default {
props: ['value'], props: ['value'],
mounted() { data() {
// from https://developers.infogr.am/oembed/ return {
window.addEventListener('message', event => { height: 0
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'
});
} }
}, },
@ -48,11 +36,33 @@
} }
}, },
data() { mounted() {
return { // from https://developers.infogr.am/oembed/
height: 0 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> </script>

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
<template> <template>
<div class="module-slug"> <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> </router-link>
</div> </div>
</template> </template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,36 +1,45 @@
<template> <template>
<div class="assignment" :data-scrollto="value.id"> <div
:data-scrollto="value.id"
class="assignment">
<p class="assignment__assignment-text"> <p class="assignment__assignment-text">
{{assignment.assignment}} {{ assignment.assignment }}
</p> </p>
<solution :value="solution" v-if="assignment.solution"></solution> <solution
:value="solution"
v-if="assignment.solution"/>
<template v-if="isStudent"> <template v-if="isStudent">
<submission-form <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" v-if="isStudent"
@turnIn="turnIn" @turnIn="turnIn"
@saveInput="saveInput" @saveInput="saveInput"
@reopen="reopen" @reopen="reopen"
@changeDocumentUrl="changeDocumentUrl" @changeDocumentUrl="changeDocumentUrl"
@spellcheck="spellcheck" @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>
<template v-if="!isStudent"> <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 Ergebnissen
</router-link> </router-link>
</template> </template>
@ -47,29 +56,35 @@
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep' 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 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 Solution from '@/components/content-blocks/Solution';
import SimpleFileUpload from '@/components/SimpleFileUpload';
import SpellCheck from '@/components/content-blocks/assignment/SpellCheck'; import SpellCheck from '@/components/content-blocks/assignment/SpellCheck';
export default { export default {
props: ['value'], props: ['value'],
components: { components: {
DocumentBlock,
DocumentForm,
SubmissionInput,
FinalSubmission,
Solution, Solution,
SimpleFileUpload,
SubmissionForm, SubmissionForm,
SpellCheck SpellCheck
}, },
data() {
return {
assignment: {
submission: this.initialSubmission(),
},
me: {
permissions: []
},
inputType: 'text',
unsaved: false,
saving: 0,
corrections: '',
spellcheckLoading: false
}
},
computed: { computed: {
...mapGetters(['scrollToAssignmentId']), ...mapGetters(['scrollToAssignmentId']),
final() { final() {
@ -230,22 +245,6 @@
me: { me: {
query: ME_QUERY query: ME_QUERY
} }
},
data() {
return {
assignment: {
submission: this.initialSubmission(),
},
me: {
permissions: []
},
inputType: 'text',
unsaved: false,
saving: 0,
corrections: '',
spellcheckLoading: false
}
} }
} }
</script> </script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,20 @@
<template> <template>
<div class="image-form"> <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 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>
<div v-if="!value.url" ref="uploadcare-panel"></div> <div
v-if="!value.url"
ref="uploadcare-panel"/>
<div v-if="value.url && !hadError"> <div v-if="value.url && !hadError">
<img :src="previewUrl" @error="error"> <img
:src="previewUrl"
@error="error">
</div> </div>
</div> </div>
</template> </template>
@ -17,9 +24,6 @@
export default { export default {
props: ['value', 'index'], props: ['value', 'index'],
mounted() {
this.mountUploadcare();
},
data() { data() {
return { return {
hadError: false, hadError: false,
@ -36,6 +40,9 @@
return null; return null;
} }
}, },
mounted() {
this.mountUploadcare();
},
methods: { methods: {
error(e) { error(e) {
this.hadError = true; this.hadError = true;

View File

@ -1,8 +1,16 @@
<template> <template>
<div class="link-form"> <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> </div>
</template> </template>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,8 @@
<template> <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 <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"/> 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 <path

View File

@ -1,5 +1,8 @@
<template> <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 <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"/> 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"/> <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> <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 <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"/> 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 <path

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