CLASSROOMIO-T-21: Course landing page + editor
This commit is contained in:
Generated
+174
-7
@@ -1541,11 +1541,27 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.191",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/@types/lodash/-/lodash-4.14.191.tgz",
|
||||
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "15.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz",
|
||||
"integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A=="
|
||||
},
|
||||
"@types/paystack": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/@types/paystack/-/paystack-2.0.1.tgz",
|
||||
"integrity": "sha512-pM5xu4ahqFSpxNFi5fm/QJtuplUuSg3PzjnMy7XjDapebUJ+Ixw1soUCV7UbqMra9No2WFXn81AcNdtPvc1niw=="
|
||||
},
|
||||
"@types/pluralize": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/@types/pluralize/-/pluralize-0.0.29.tgz",
|
||||
"integrity": "sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA=="
|
||||
},
|
||||
"@types/pug": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz",
|
||||
@@ -1680,6 +1696,11 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
@@ -1874,7 +1895,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
@@ -1945,6 +1965,15 @@
|
||||
"resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.4.0.tgz",
|
||||
"integrity": "sha512-S18o4Y9PqI/uabdlT/jI3MY7XBJjNxnfapFIkjkMwpz6qNxLFZOm2b22OMf4ZYDL9lpNWI+Ih4fEMVPwO1KHFQ=="
|
||||
},
|
||||
"carbon-components-svelte": {
|
||||
"version": "0.70.13",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/carbon-components-svelte/-/carbon-components-svelte-0.70.13.tgz",
|
||||
"integrity": "sha512-p6wX8004BM6DrrLH1JJWbkR/VybU0gra+jDWlSW1V2aO8XmAz81evnDLZuTrvbHNkEXPiaC+athSjNsBOf0SjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"flatpickr": "4.6.9"
|
||||
}
|
||||
},
|
||||
"carbon-icons-svelte": {
|
||||
"version": "10.45.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/carbon-icons-svelte/-/carbon-icons-svelte-10.45.1.tgz",
|
||||
@@ -2046,6 +2075,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
@@ -2331,6 +2365,19 @@
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/deep-equal/-/deep-equal-1.1.1.tgz",
|
||||
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
|
||||
"requires": {
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-date-object": "^1.0.1",
|
||||
"is-regex": "^1.0.4",
|
||||
"object-is": "^1.0.1",
|
||||
"object-keys": "^1.1.1",
|
||||
"regexp.prototype.flags": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"deep-freeze": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz",
|
||||
@@ -2347,7 +2394,6 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"object-keys": "^1.0.12"
|
||||
}
|
||||
@@ -2523,6 +2569,11 @@
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
||||
@@ -2597,6 +2648,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
||||
@@ -2657,6 +2713,12 @@
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"flatpickr": {
|
||||
"version": "4.6.9",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/flatpickr/-/flatpickr-4.6.9.tgz",
|
||||
"integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==",
|
||||
"dev": true
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
@@ -2719,6 +2781,11 @@
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"functions-have-names": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
@@ -2757,7 +2824,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@@ -2895,8 +2961,15 @@
|
||||
"has-symbols": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
|
||||
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
|
||||
},
|
||||
"has-tostringtag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
@@ -3043,6 +3116,15 @@
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
@@ -3065,6 +3147,14 @@
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"is-date-object": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||
"requires": {
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-directory": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
|
||||
@@ -3126,6 +3216,15 @@
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"is-regex": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/is-regex/-/is-regex-1.1.4.tgz",
|
||||
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
@@ -3669,11 +3768,19 @@
|
||||
"integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"object-is": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/object-is/-/object-is-1.1.5.tgz",
|
||||
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3"
|
||||
}
|
||||
},
|
||||
"object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
|
||||
},
|
||||
"object.assign": {
|
||||
"version": "4.1.2",
|
||||
@@ -3738,6 +3845,11 @@
|
||||
"no-case": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"parchment": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/parchment/-/parchment-1.1.4.tgz",
|
||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
|
||||
},
|
||||
"parse-glob": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
|
||||
@@ -3818,6 +3930,15 @@
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"paystack": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/paystack/-/paystack-2.0.1.tgz",
|
||||
"integrity": "sha512-reVONV7ZUMln/iWeM60n0BbogF3/zFWmUrqbKYVNzEAv+p9TcWDCHfNZ2mBGXzIXhyTsNXWwf4wNcXe28btAHw==",
|
||||
"requires": {
|
||||
"promise": "^7.1.1",
|
||||
"request": "^2.79.0"
|
||||
}
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
@@ -3846,6 +3967,11 @@
|
||||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pluralize": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/pluralize/-/pluralize-8.0.0.tgz",
|
||||
"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="
|
||||
},
|
||||
"polka": {
|
||||
"version": "1.0.0-next.22",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/polka/-/polka-1.0.0-next.22.tgz",
|
||||
@@ -3988,6 +4114,14 @@
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"promise": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/promise/-/promise-7.3.1.tgz",
|
||||
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
|
||||
"requires": {
|
||||
"asap": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -4044,6 +4178,29 @@
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"dev": true
|
||||
},
|
||||
"quill": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/quill/-/quill-1.3.7.tgz",
|
||||
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
|
||||
"requires": {
|
||||
"clone": "^2.1.1",
|
||||
"deep-equal": "^1.0.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"extend": "^3.0.2",
|
||||
"parchment": "^1.1.4",
|
||||
"quill-delta": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"quill-delta": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/quill-delta/-/quill-delta-3.6.3.tgz",
|
||||
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
|
||||
"requires": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"extend": "^3.0.2",
|
||||
"fast-diff": "1.1.2"
|
||||
}
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@@ -4175,6 +4332,16 @@
|
||||
"@babel/runtime": "^7.8.4"
|
||||
}
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
|
||||
"integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.3",
|
||||
"functions-have-names": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"regexparam": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://flipplib.jfrog.io/artifactory/api/npm/flipp-npm/regexparam/-/regexparam-1.3.0.tgz",
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
"@sentry/tracing": "^6.13.3",
|
||||
"@supabase/supabase-js": "^1.20.0",
|
||||
"@tailwindcss/forms": "^0.3.2",
|
||||
"@types/paystack": "^2.0.1",
|
||||
"@types/pluralize": "0.0.29",
|
||||
"body-parser": "^1.19.0",
|
||||
"canvas-confetti": "^1.4.0",
|
||||
"compression": "^1.7.1",
|
||||
@@ -27,7 +29,10 @@
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^1.2.7",
|
||||
"node-sass": "^6.0.0",
|
||||
"paystack": "^2.0.1",
|
||||
"pluralize": "^8.0.0",
|
||||
"polka": "next",
|
||||
"quill": "^1.3.7",
|
||||
"sapper-environment": "^1.0.1",
|
||||
"sirv": "^1.0.0",
|
||||
"svelte-easy-crop": "^1.0.4",
|
||||
@@ -47,6 +52,8 @@
|
||||
"@rollup/plugin-replace": "^2.4.2",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@tailwindcss/typography": "^0.3.1",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"carbon-components-svelte": "^0.70.13",
|
||||
"carbon-icons-svelte": "^10.21.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"postcss": "^8.0.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Label from "./Label.svelte";
|
||||
import Avatar from "./Avatar.svelte";
|
||||
import Label from './Label.svelte';
|
||||
import Avatar from './Avatar.svelte';
|
||||
|
||||
export let poll = {};
|
||||
export let onSelect = () => {};
|
||||
@@ -19,7 +19,7 @@
|
||||
}
|
||||
|
||||
function getSelectedUsers(users) {
|
||||
return users.map((o) => o.label).join(", ");
|
||||
return users.map((o) => o.label).join(', ');
|
||||
}
|
||||
|
||||
function getTotalVotes(options) {
|
||||
@@ -103,7 +103,7 @@
|
||||
<div class="mr-5">
|
||||
<Label>Expiration</Label>
|
||||
<p class="text-sm text-gray-600 mb-4">
|
||||
{poll.expiration || "Today"}
|
||||
{poll.expiration || 'Today'}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
@@ -170,7 +170,7 @@
|
||||
{/each}
|
||||
<div class="flex items-center">
|
||||
<p class="text-sm text-gray-600">
|
||||
Expires: {poll.expiration || "Today"}
|
||||
Expires: {poll.expiration || 'Today'}
|
||||
</p>
|
||||
<span class="mx-2">|</span>
|
||||
<p class="text-sm text-gray-600">
|
||||
@@ -182,7 +182,7 @@
|
||||
{#if isAuthor}
|
||||
<div class="w-full text-center mt-3">
|
||||
<button
|
||||
class="px-5 py-2 bg-blue-700 text-white"
|
||||
class="px-5 py-3 bg-blue-700 text-white"
|
||||
on:click={() => (viewResult = !viewResult)}
|
||||
>
|
||||
{#if viewResult}
|
||||
@@ -192,7 +192,7 @@
|
||||
{/if}
|
||||
</button>
|
||||
<button
|
||||
class="px-5 py-2 bg-red-500 text-white"
|
||||
class="px-5 py-3 bg-red-500 text-white"
|
||||
on:click={handlePollDelete}
|
||||
>
|
||||
Delete
|
||||
|
||||
@@ -57,9 +57,9 @@
|
||||
</button>
|
||||
|
||||
{#if facultyFilterOpened}
|
||||
<div class="w-48 bg-white border border-grey rounded-lg mt-2 py-2">
|
||||
<div class="w-48 bg-white border border-grey rounded-lg mt-2 py-3">
|
||||
{#each faculties as faculty}
|
||||
<div class="block px-4 py-2 border-b">
|
||||
<div class="block px-4 py-3 border-b">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="border border-gray checked:bg-blue-600 checked:border-transparent mr-1 cursor-pointer"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
<div class="w-full min-h-screen flex items-center justify-center mt-4">
|
||||
<div class="container border border-gray bg-white">
|
||||
<div class="flex items-center flex-col p-2 lg:px-8 lg:py-4">
|
||||
<div class="flex items-center flex-col p-2 lg:px-8 lg:py-3">
|
||||
{#if !showOnlyContent || showLogo}
|
||||
<div class="flex items-center w-full justify-center pt-4">
|
||||
<img
|
||||
|
||||
@@ -9,13 +9,16 @@
|
||||
p {
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2em;
|
||||
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.bg-set {
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="No, please don't"
|
||||
onClick={() => {
|
||||
@@ -34,7 +34,7 @@
|
||||
}}
|
||||
/>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label="Yes, delete"
|
||||
onClick={() => {
|
||||
|
||||
@@ -132,13 +132,13 @@
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="No, cancel"
|
||||
type="submit"
|
||||
/>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label={isDeleting ? 'Deleting...' : 'Yes, delete'}
|
||||
isDisabled={isDeleting}
|
||||
@@ -174,7 +174,7 @@
|
||||
</QuestionContainer>
|
||||
<div class="float-right">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label="Delete this exercise"
|
||||
onClick={() => (shouldDelete = true)}
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
}}
|
||||
label="Save"
|
||||
variant={VARIANTS.CONTAINED_SUCCESS}
|
||||
className="py-2 px-8"
|
||||
className="py-3 px-8"
|
||||
/>
|
||||
</div>
|
||||
<div class="border border-gray-300 rounded-md mt-2">
|
||||
|
||||
@@ -228,14 +228,14 @@
|
||||
<h2 class="text-xl mb-2 mt-0">{$questionnaire.title}</h2>
|
||||
{#if STATUS.GRADED === $questionnaireMetaData.status}
|
||||
<span
|
||||
class="status-text bg-green-700 text-white rounded-full py-2 px-6 text-center"
|
||||
class="status-text bg-green-700 text-white rounded-full py-3 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Graded
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class="status-text bg-yellow-600 text-white rounded-full py-2 px-6 text-center"
|
||||
class="status-text bg-yellow-600 text-white rounded-full py-3 px-6 text-center"
|
||||
title="Status: Pending Review"
|
||||
>
|
||||
Pending
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { onMount } from 'svelte';
|
||||
import AudioConsole32 from 'carbon-icons-svelte/lib/AudioConsole32';
|
||||
import Box from '../../../../Box/index.svelte';
|
||||
import { Firework } from 'svelte-loading-spinners';
|
||||
import { Moon } from 'svelte-loading-spinners';
|
||||
import Backdrop from '../../../../Backdrop/index.svelte';
|
||||
import PrimaryButton from '../../../../PrimaryButton/index.svelte';
|
||||
import RoleBasedSecurity from '../../../../RoleBasedSecurity/index.svelte';
|
||||
@@ -122,7 +122,7 @@
|
||||
|
||||
{#if isFetching}
|
||||
<Backdrop>
|
||||
<Firework size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
</Backdrop>
|
||||
{/if}
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
|
||||
<div class="mt-5 flex items-center">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
label="Submit"
|
||||
onClick={handleAddExercise}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import MODES from '../../../../../utils/constants/mode.js';
|
||||
import { formatYoutubeVideo } from '../../../../../utils/functions/formatYoutubeVideo';
|
||||
|
||||
import Tabs from '../../../../Tabs/index.svelte';
|
||||
import TabContent from '../../../../TabContent/index.svelte';
|
||||
@@ -24,34 +25,6 @@
|
||||
|
||||
const onChange = (tab) => () => (currentTab = tab);
|
||||
|
||||
function formatYoutubeVideo(url) {
|
||||
console.log(`url`, url);
|
||||
const prefix = 'https://www.youtube.com/embed';
|
||||
|
||||
// https://www.youtube.com/embed/qajK1J1neAM
|
||||
if (url.includes('embed')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// https://youtu.be/qajK1J1neAM
|
||||
if (url.includes('.be/')) {
|
||||
const splittedUrlWithBe = url.split('.be/');
|
||||
|
||||
return `${prefix}/${splittedUrlWithBe[1]}`;
|
||||
}
|
||||
|
||||
// https://www.youtube.com/watch?v=qajK1J1neAM
|
||||
const splitedUrl = url.split('watch');
|
||||
if (splitedUrl.length !== 2) {
|
||||
errors.video = 'Wrong url format';
|
||||
return;
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(splitedUrl[1]);
|
||||
|
||||
return `${prefix}/${query.get('v')}`;
|
||||
}
|
||||
|
||||
function handleSave(prevMode) {
|
||||
if (prevMode === MODES.edit) {
|
||||
handleUpdateLessonMaterials($lesson, lessonId);
|
||||
@@ -146,7 +119,7 @@
|
||||
width="100%"
|
||||
height="569"
|
||||
class="iframe"
|
||||
src={formatYoutubeVideo(url)}
|
||||
src={formatYoutubeVideo(url, errors)}
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
|
||||
+17
-9
@@ -1,11 +1,13 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { writable, Writable, Updater } from 'svelte/store';
|
||||
import {
|
||||
createLesson,
|
||||
updateLesson,
|
||||
deleteLesson,
|
||||
} from '../../../../../utils/services/courses';
|
||||
|
||||
export const lessons = writable([]);
|
||||
import type { Lesson, Course } from '../../../../../utils/types';
|
||||
|
||||
export const lessons: Writable<Lesson[]> = writable([]);
|
||||
export const lesson = writable({
|
||||
id: null,
|
||||
totalExercises: 0,
|
||||
@@ -19,7 +21,7 @@ export const lesson = writable({
|
||||
export const isLessonDirty = writable(false);
|
||||
|
||||
export function handleAddLesson() {
|
||||
lessons.update((_lessons) => {
|
||||
lessons.update(((_lessons) => {
|
||||
return [
|
||||
..._lessons,
|
||||
{
|
||||
@@ -31,10 +33,10 @@ export function handleAddLesson() {
|
||||
is_complete: false,
|
||||
},
|
||||
];
|
||||
});
|
||||
}) as Updater<any>);
|
||||
}
|
||||
|
||||
export function handleDelete(lessonId) {
|
||||
export function handleDelete(lessonId: Lesson['id']) {
|
||||
return async () => {
|
||||
// Need to implement soft delete
|
||||
if (lessonId) {
|
||||
@@ -52,18 +54,21 @@ export function handleDelete(lessonId) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function handleSaveLesson(lesson, course_id) {
|
||||
export async function handleSaveLesson(
|
||||
lesson: Lesson,
|
||||
course_id: Course['id']
|
||||
) {
|
||||
console.log(`handleSaveLesson lesson`, lesson);
|
||||
const newLesson = {
|
||||
title: lesson.title,
|
||||
lesson_at: lesson.lesson_at,
|
||||
call_url: lesson.call_url,
|
||||
teacher_id: lesson.profile ? lesson.profile.id : null,
|
||||
teacher_id: lesson.profile ? lesson.profile.id : undefined,
|
||||
course_id,
|
||||
is_complete: lesson.is_complete,
|
||||
};
|
||||
|
||||
let updatedLesson;
|
||||
let updatedLesson: any[] | null = null;
|
||||
|
||||
if (typeof lesson.id === 'string') {
|
||||
// No need to get the result of update cause we have all in local state
|
||||
@@ -85,7 +90,10 @@ export async function handleSaveLesson(lesson, course_id) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function handleUpdateLessonMaterials(lesson, lessonId) {
|
||||
export async function handleUpdateLessonMaterials(
|
||||
lesson: any,
|
||||
lessonId: Lesson['id']
|
||||
) {
|
||||
const materials = {
|
||||
...lesson.materials,
|
||||
};
|
||||
@@ -171,7 +171,7 @@
|
||||
<a
|
||||
class="item flex items-center {(
|
||||
path || $page.path
|
||||
).includes(item.id) && 'active'} pl-7 py-2 {isStudent &&
|
||||
).includes(item.id) && 'active'} pl-7 py-3 {isStudent &&
|
||||
!item.is_complete
|
||||
? 'cursor-not-allowed'
|
||||
: ''}"
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="No"
|
||||
onClick={() => ($deleteMemberModal.open = false)}
|
||||
/>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="Yes"
|
||||
onClick={handleDelete}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
} from '../../../../utils/services/courses';
|
||||
import { invitationModal, resetForm } from './store';
|
||||
import { validateForm } from '../../../Courses/functions';
|
||||
import { course, setCourseData } from '../../../Course/store';
|
||||
import { course, setCourse } from '../../store';
|
||||
|
||||
let errors = {
|
||||
emails: '',
|
||||
@@ -47,7 +47,7 @@
|
||||
addGroupMember(membersStack).then(async (membersAdded) => {
|
||||
const group = await fetchGroup($course.group.id);
|
||||
|
||||
setCourseData(
|
||||
setCourse(
|
||||
{
|
||||
...$course,
|
||||
group: group.data,
|
||||
@@ -90,7 +90,7 @@
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex items-center">
|
||||
<PrimaryButton className="px-6 py-2" label="Finish" type="submit" />
|
||||
<PrimaryButton className="px-6 py-3" label="Finish" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
}
|
||||
|
||||
$settingsDialog.open = false;
|
||||
const logo = await updateCourse($course.id, {
|
||||
avatar,
|
||||
const logo = await updateCourse($course.id, avatar, {
|
||||
title: $course.title,
|
||||
description: $course.description,
|
||||
});
|
||||
@@ -67,13 +66,13 @@
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="No, cancel"
|
||||
type="submit"
|
||||
/>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
label="Yes, delete"
|
||||
onClick={handleDelete}
|
||||
@@ -103,12 +102,12 @@
|
||||
|
||||
<div class="mt-5 flex items-center justify-between">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label="Delete"
|
||||
onClick={() => (shouldDelete = true)}
|
||||
/>
|
||||
<PrimaryButton className="px-6 py-2" label="Update" type="submit" />
|
||||
<PrimaryButton className="px-6 py-3" label="Update" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
@@ -26,12 +26,12 @@ export function getLessonsRoute(courseId, lessonId) {
|
||||
return `${path}/${lessonId}`;
|
||||
}
|
||||
|
||||
export function getLectureNo(index) {
|
||||
export function getLectureNo(index, initNo = '00') {
|
||||
if (index <= 9) {
|
||||
return `00${index}`;
|
||||
return `${initNo}${index}`;
|
||||
}
|
||||
|
||||
return `0${index}`;
|
||||
return `${initNo.replace('0', '')}${index}`;
|
||||
}
|
||||
|
||||
export function formatAnswers(data) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import PrimaryButton from '../PrimaryButton/index.svelte';
|
||||
import PageBody from '../PageBody/index.svelte';
|
||||
import EditContent from '../EditContent/index.svelte';
|
||||
import { course } from '../Course/store';
|
||||
import { course } from './store';
|
||||
import { updateCourse } from '../../utils/services/courses';
|
||||
|
||||
let mode = MODES.view;
|
||||
|
||||
@@ -1,8 +1,35 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { writable, Writable } from 'svelte/store';
|
||||
import { lessons } from './components/Lesson/store/lessons';
|
||||
//@ts-ignore
|
||||
import { ROLE } from '../../utils/constants/roles';
|
||||
import type { Course } from '../../utils/types';
|
||||
|
||||
export const course = writable({ id: '' });
|
||||
export const course: Writable<Course> = writable({
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
cost: 0,
|
||||
currency: '',
|
||||
metadata: {
|
||||
requirements: '',
|
||||
description: '',
|
||||
goals: '',
|
||||
videoUrl: '',
|
||||
showDiscount: false,
|
||||
discount: 0,
|
||||
reward: {
|
||||
show: false,
|
||||
description: '',
|
||||
},
|
||||
instructor: {
|
||||
name: '',
|
||||
role: '',
|
||||
coursesNo: 0,
|
||||
description: '',
|
||||
imgUrl: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const mockGroupMember = {
|
||||
id: '434534534535',
|
||||
@@ -27,7 +54,7 @@ export const group = writable({
|
||||
people: [],
|
||||
});
|
||||
|
||||
export async function setCourseData(data, setLesson = true) {
|
||||
export async function setCourse(data: Course, setLesson = true) {
|
||||
if (!data || !(Object.values(data) && Object.values(data).length)) return;
|
||||
// const tutorsById = {};
|
||||
|
||||
@@ -38,27 +65,37 @@ export async function setCourseData(data, setLesson = true) {
|
||||
people: [],
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
if (Array.isArray(groupData.members)) {
|
||||
// @ts-ignore
|
||||
for (const member of groupData.members) {
|
||||
if (member.role_id === ROLE.STUDENT) {
|
||||
// @ts-ignore
|
||||
groupData.students.push(member);
|
||||
} else if (member.profile) {
|
||||
// @ts-ignore
|
||||
groupData.tutors.push(member.profile);
|
||||
|
||||
// tutorsById[member.profile.id] = member.profile;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
groupData.people = groupData.members;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
delete groupData.members;
|
||||
// @ts-ignore
|
||||
group.set(groupData);
|
||||
}
|
||||
|
||||
if (setLesson) {
|
||||
// @ts-ignore
|
||||
const orderedLessons = (data.lessons || [])
|
||||
// @ts-ignore
|
||||
.sort((a, b) => new Date(a.created_at) - new Date(b.created_at))
|
||||
// @ts-ignore
|
||||
.sort((a, b) => a.order - b.order);
|
||||
// .map((lesson) => ({
|
||||
// ...lesson,
|
||||
@@ -67,6 +104,29 @@ export async function setCourseData(data, setLesson = true) {
|
||||
lessons.set(orderedLessons);
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
delete data.lessons;
|
||||
|
||||
if (data.metadata && !Object.values(data.metadata)) {
|
||||
data.metadata = {
|
||||
requirements: '',
|
||||
description: '',
|
||||
goals: '',
|
||||
videoUrl: '',
|
||||
showDiscount: false,
|
||||
discount: 0,
|
||||
reward: {
|
||||
show: false,
|
||||
description: '',
|
||||
},
|
||||
instructor: {
|
||||
name: '',
|
||||
role: '',
|
||||
coursesNo: 0,
|
||||
description: '',
|
||||
imgUrl: '',
|
||||
},
|
||||
};
|
||||
}
|
||||
course.set(data);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { Firework } from 'svelte-loading-spinners';
|
||||
import { Moon } from 'svelte-loading-spinners';
|
||||
import Navigation from '../Course/components/Navigation/index.svelte';
|
||||
import Backdrop from '../../components/Backdrop/index.svelte';
|
||||
// import { lessons, lesson } from '../Course/components/Lesson/store/lessons';
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
{#if isFetching}
|
||||
<Backdrop>
|
||||
<Firework size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
</Backdrop>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<script>
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
import EditContent from '../../../EditContent/index.svelte';
|
||||
import { landingPageStore } from '../../store';
|
||||
</script>
|
||||
|
||||
<TextField
|
||||
label="Company name"
|
||||
bind:value={$landingPageStore.aboutUs.companyName}
|
||||
className="mt-3 font-bold"
|
||||
/>
|
||||
<TextField
|
||||
label="Company Logo URL"
|
||||
bind:value={$landingPageStore.aboutUs.logoUrl}
|
||||
className="mt-3 font-bold"
|
||||
/>
|
||||
|
||||
<p class="mt-3 font-bold">Description</p>
|
||||
<EditContent bind:value={$landingPageStore.aboutUs.description} />
|
||||
@@ -4,14 +4,14 @@
|
||||
import { isMobile } from '../../../../utils/store/useMobile';
|
||||
import IconButton from '../../../IconButton/index.svelte';
|
||||
|
||||
let show = true;
|
||||
export let show = true;
|
||||
</script>
|
||||
|
||||
<section
|
||||
class="root z-10 {!show && 'hide'} {$isMobile ? 'fixed shadow-xl' : 'sticky'}"
|
||||
>
|
||||
{#if show}
|
||||
<div class="relative h-full bg-gray-100">
|
||||
<div class="relative h-full bg-white">
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -46,6 +46,7 @@
|
||||
min-width: 300px;
|
||||
max-width: 300px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-right: 1px solid var(--border-color);
|
||||
background-color: rgb(250, 251, 252);
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
<script>
|
||||
import EditContent from '../../../EditContent/index.svelte';
|
||||
import { landingPageStore } from '../../store';
|
||||
import { set, get, cloneDeep } from 'lodash';
|
||||
import TextEditor from '../../../TextEditor/index.svelte';
|
||||
|
||||
export let course;
|
||||
</script>
|
||||
|
||||
<EditContent bind:value={$landingPageStore.description.content} />
|
||||
<div class="h-2/5">
|
||||
<TextEditor
|
||||
content={get(course, 'metadata.description')}
|
||||
onChange={(html) => {
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, 'metadata.description', html);
|
||||
course = _course;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import { set, get, cloneDeep } from 'lodash';
|
||||
import TextEditor from '../../../TextEditor/index.svelte';
|
||||
|
||||
export let course;
|
||||
</script>
|
||||
|
||||
<div class="h-2/5">
|
||||
<TextEditor
|
||||
content={get(course, 'metadata.goals')}
|
||||
onChange={(html) => {
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, 'metadata.goals', html);
|
||||
course = _course;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1,38 +1,31 @@
|
||||
<script>
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
import { landingPageStore } from '../../store';
|
||||
|
||||
export let course = {};
|
||||
</script>
|
||||
|
||||
<TextArea
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Title"
|
||||
bind:value={$landingPageStore.header.title}
|
||||
rows="3"
|
||||
className="mt-3 font-bold"
|
||||
bgColor="bg-white"
|
||||
bind:value={course.title}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
label="Description"
|
||||
bind:value={$landingPageStore.header.description}
|
||||
bind:value={course.description}
|
||||
rows="6"
|
||||
className="mt-3 font-bold"
|
||||
bgColor="bg-white"
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mt-3 font-bold"
|
||||
label="Currency"
|
||||
bind:value={$landingPageStore.header.currency}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mt-3 font-bold"
|
||||
label="Cost"
|
||||
bind:value={$landingPageStore.header.cost}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Button Label"
|
||||
className="mt-3 font-bold"
|
||||
bind:value={$landingPageStore.header.buttonLabel}
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Short Video"
|
||||
placeholder="www.youtube.com/watch?v=uYRq60G5XTk"
|
||||
helperMessage="Enter a link to a youtube video"
|
||||
type="text"
|
||||
bind:value={course.metadata.videoUrl}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<script>
|
||||
import { set, cloneDeep, isEmpty, get } from 'lodash';
|
||||
import TextArea from '../../../Form/TextArea.svelte';
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
import UploadImage from '../../../UploadImage/index.svelte';
|
||||
import { uploadAvatar } from '../../../../utils/services/courses';
|
||||
|
||||
export let course = {};
|
||||
let name;
|
||||
let role;
|
||||
let imgUrl;
|
||||
let description;
|
||||
let courseNo;
|
||||
let avatar;
|
||||
let hasBeenSet = false;
|
||||
|
||||
function setter(value, setterKey) {
|
||||
if (!value) return;
|
||||
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, setterKey, value);
|
||||
course = _course;
|
||||
}
|
||||
|
||||
async function onAvatarChange(_avatar) {
|
||||
if (!_avatar) return;
|
||||
|
||||
const logo = await uploadAvatar(course.id, _avatar);
|
||||
imgUrl = logo;
|
||||
}
|
||||
|
||||
function setDefaults(course) {
|
||||
if (isEmpty(course) || hasBeenSet) return;
|
||||
|
||||
hasBeenSet = true;
|
||||
name = get(course, 'metadata.instructor.name');
|
||||
role = get(course, 'metadata.instructor.role');
|
||||
imgUrl = get(course, 'metadata.instructor.imgUrl');
|
||||
description = get(course, 'metadata.instructor.description');
|
||||
courseNo = get(course, 'metadata.instructor.courseNo');
|
||||
}
|
||||
|
||||
$: setter(name, 'metadata.instructor.name');
|
||||
$: setter(role, 'metadata.instructor.role');
|
||||
$: setter(imgUrl, 'metadata.instructor.imgUrl');
|
||||
$: setter(description, 'metadata.instructor.description');
|
||||
$: setter(courseNo, 'metadata.instructor.courseNo');
|
||||
|
||||
$: onAvatarChange(avatar);
|
||||
|
||||
$: setDefaults(course);
|
||||
</script>
|
||||
|
||||
<div class="mt-5">
|
||||
<label for="upload" class="font-bold">Instructor Logo</label>
|
||||
<UploadImage bind:avatar src={imgUrl} />
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Instructor name"
|
||||
placeholder="Your company name"
|
||||
bind:value={name}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Instructor Role"
|
||||
placeholder="e.g Software developer"
|
||||
bind:value={role}
|
||||
/>
|
||||
|
||||
<TextArea
|
||||
label="About instructor"
|
||||
placeholder="A short background about the instructor"
|
||||
rows="6"
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
bind:value={description}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Total number of courses"
|
||||
type="number"
|
||||
bind:value={courseNo}
|
||||
/>
|
||||
@@ -0,0 +1,97 @@
|
||||
<script>
|
||||
import { set, isEmpty, get, cloneDeep } from 'lodash';
|
||||
import { Toggle } from 'carbon-components-svelte';
|
||||
import TextEditor from '../../../TextEditor/index.svelte';
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
|
||||
export let course = {};
|
||||
|
||||
let discount = 0;
|
||||
let showDiscount;
|
||||
let giftToggled;
|
||||
|
||||
let hasBeenSet = false;
|
||||
|
||||
function setDefaults(course) {
|
||||
console.log('\n\nsetDefaults course', course);
|
||||
if (isEmpty(course) || hasBeenSet) return;
|
||||
hasBeenSet = true;
|
||||
|
||||
discount = get(course, 'metadata.discount', 0);
|
||||
showDiscount = get(course, 'metadata.showDiscount', false);
|
||||
giftToggled = get(course, 'metadata.reward.show', false);
|
||||
}
|
||||
|
||||
function setter(value, setterKey) {
|
||||
console.log('\n\nsetter course', value);
|
||||
if (typeof value !== 'boolean' && !value) return;
|
||||
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, setterKey, value);
|
||||
course = _course;
|
||||
}
|
||||
|
||||
$: setter(showDiscount, 'metadata.showDiscount');
|
||||
$: setter(discount, 'metadata.discount');
|
||||
$: setter(giftToggled, 'metadata.reward.show');
|
||||
|
||||
$: setDefaults(course);
|
||||
</script>
|
||||
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Currency"
|
||||
value={course.currency}
|
||||
isDisabled={true}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Cost"
|
||||
type="number"
|
||||
bind:value={course.cost}
|
||||
/>
|
||||
|
||||
<div class="mt-5">
|
||||
<!-- <label for="text-field" class="m-0 font-bold mb-2">Discount</label> -->
|
||||
<Toggle labelText="Discount" bind:toggled={showDiscount}>
|
||||
<span slot="labelA">No</span>
|
||||
<span slot="labelB">Yes</span>
|
||||
</Toggle>
|
||||
</div>
|
||||
|
||||
{#if showDiscount}
|
||||
<TextField
|
||||
className="mt-5"
|
||||
labelClassName="font-bold"
|
||||
label="Discout Percent"
|
||||
type="number"
|
||||
bind:value={discount}
|
||||
helperMessage="In Percentage %"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="mt-5">
|
||||
<!-- <label for="text-field" class="m-0 font-bold mb-2">Discount</label> -->
|
||||
<Toggle labelText="Gift on Completion" bind:toggled={giftToggled}>
|
||||
<span slot="labelA">No</span>
|
||||
<span slot="labelB">Yes</span>
|
||||
</Toggle>
|
||||
</div>
|
||||
|
||||
{#if giftToggled}
|
||||
<p class="font-bold mt-5">About Gift</p>
|
||||
|
||||
<div class="h-2/5">
|
||||
<TextEditor
|
||||
content={get(course, 'metadata.reward.description')}
|
||||
onChange={(html) => {
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, 'metadata.reward.description', html);
|
||||
course = _course;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1,6 +1,17 @@
|
||||
<script>
|
||||
import EditContent from '../../../EditContent/index.svelte';
|
||||
import { landingPageStore } from '../../store';
|
||||
import { set, get, cloneDeep } from 'lodash';
|
||||
import TextEditor from '../../../TextEditor/index.svelte';
|
||||
|
||||
export let course;
|
||||
</script>
|
||||
|
||||
<EditContent bind:value={$landingPageStore.requirement.content} />
|
||||
<div class="h-2/5">
|
||||
<TextEditor
|
||||
content={get(course, 'metadata.requirements')}
|
||||
onChange={(html) => {
|
||||
const _course = cloneDeep(course);
|
||||
set(_course, 'metadata.requirements', html);
|
||||
course = _course;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<script>
|
||||
import TextField from '../../../Form/TextField.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
import CloseButton from '../../../Buttons/Close/index.svelte';
|
||||
import { VARIANTS } from '../../../PrimaryButton/constants';
|
||||
import { landingPageStore } from '../../store';
|
||||
|
||||
function handleAdd() {
|
||||
landingPageStore.update((store) => {
|
||||
const { syllabus } = store;
|
||||
|
||||
const lessons = [
|
||||
...syllabus.lessons,
|
||||
{
|
||||
title: '',
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
...store,
|
||||
syllabus: {
|
||||
lessons,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(index) {
|
||||
landingPageStore.update((store) => {
|
||||
const { syllabus } = store;
|
||||
|
||||
return {
|
||||
...store,
|
||||
syllabus: {
|
||||
lessons: syllabus.lessons.filter(
|
||||
(lesson, lessonIndex) => index !== lessonIndex
|
||||
),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<h4 class="mb-3">Lessons</h4>
|
||||
|
||||
{#each $landingPageStore.syllabus.lessons as lesson, index}
|
||||
<div class="mb-4 relative">
|
||||
<TextField bind:value={lesson.title} />
|
||||
<div class="absolute -top-3 -right-3">
|
||||
<CloseButton
|
||||
onClick={() => handleDelete(index)}
|
||||
contained={true}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<div class="mt-3">
|
||||
<PrimaryButton label="Add" onClick={handleAdd} variant={VARIANTS.OUTLINED} />
|
||||
</div>
|
||||
@@ -8,13 +8,18 @@
|
||||
import HeaderForm from './HeaderForm.svelte';
|
||||
import RequirementForm from './RequirementForm.svelte';
|
||||
import DescriptionForm from './DescriptionForm.svelte';
|
||||
import SyllabusForm from './SyllabusForm.svelte';
|
||||
import AboutUsForm from './AboutUsForm.svelte';
|
||||
import PricingForm from './PricingForm.svelte';
|
||||
import GoalsForm from './GoalsForm.svelte';
|
||||
import InstructorForm from './InstructorForm.svelte';
|
||||
import PrimaryButton from '../../../PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '../../../PrimaryButton/constants';
|
||||
import { updateCourse } from '../../../../utils/services/courses';
|
||||
|
||||
export let course;
|
||||
export let courseId;
|
||||
|
||||
let borderBottomGrey = 'border-r-0 border-b border-l-0 border-gray-300';
|
||||
let loading = false;
|
||||
|
||||
const sections = [
|
||||
{
|
||||
@@ -31,11 +36,15 @@
|
||||
},
|
||||
{
|
||||
key: 4,
|
||||
title: 'Syllabus',
|
||||
title: 'Goals',
|
||||
},
|
||||
{
|
||||
key: 5,
|
||||
title: 'About Us',
|
||||
title: 'Instructor',
|
||||
},
|
||||
{
|
||||
key: 6,
|
||||
title: 'Pricing',
|
||||
},
|
||||
];
|
||||
let selectedSection = null;
|
||||
@@ -55,29 +64,66 @@
|
||||
};
|
||||
}
|
||||
|
||||
function handlePublish() {}
|
||||
function generateSlug(title) {
|
||||
return title
|
||||
.replaceAll(' ', '-')
|
||||
.toLowerCase()
|
||||
.concat(`-${new Date().getTime()}`);
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
loading = true;
|
||||
course.slug = course.slug || generateSlug(course.title);
|
||||
|
||||
await updateCourse(courseId, undefined, {
|
||||
...course,
|
||||
attendance: undefined,
|
||||
group: undefined,
|
||||
lessons: undefined,
|
||||
slug: course.slug,
|
||||
});
|
||||
loading = false;
|
||||
|
||||
return `${window.location.origin}/course/${course.slug}`;
|
||||
}
|
||||
/**
|
||||
* 1. Use all you have for editing now
|
||||
* 2. Make course one interface we might need to computation
|
||||
*/
|
||||
async function handlePreview() {
|
||||
const link = `${window.location.origin}/course/${course.slug}`;
|
||||
window.open(link, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<Container show={true}>
|
||||
<!-- Publish Header Section -->
|
||||
{#if !selectedSection}
|
||||
<div
|
||||
class="flex justify-between items-center px-2 {borderBottomGrey} w-full"
|
||||
>
|
||||
<div class="flex justify-between items-center px-2 w-full">
|
||||
<CloseButton onClick={handleClose} />
|
||||
<PrimaryButton
|
||||
label="Publish"
|
||||
onClick={handlePublish}
|
||||
disablePadding={true}
|
||||
/>
|
||||
<div class="flex items-center">
|
||||
<PrimaryButton
|
||||
label="Save"
|
||||
type="button"
|
||||
className="mr-1"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
onClick={handleSave}
|
||||
isLoading={loading}
|
||||
/>
|
||||
<PrimaryButton
|
||||
label="Preview"
|
||||
type="button"
|
||||
onClick={handlePreview}
|
||||
isDisabled={loading || !course.slug}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex justify-between items-center px-2 bg-white {borderBottomGrey} w-full mb-2"
|
||||
>
|
||||
<div class="flex justify-between items-center px-2 w-full mb-2">
|
||||
<h3 class="">Page Builder</h3>
|
||||
</div>
|
||||
{#each sections as section}
|
||||
<button
|
||||
class="w-full flex items-center justify-between px-2 py-3 bg-white border border-b-0 border-gray-300"
|
||||
class="w-full flex items-center justify-between px-2 py-3 border border-b-0 border-gray-300"
|
||||
on:click={handleSectionSelect(section.key)}
|
||||
>
|
||||
<p class="mr-2">{section.title} section</p>
|
||||
@@ -85,24 +131,27 @@
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="flex items-center bg-white {borderBottomGrey} w-full">
|
||||
<!-- Title -->
|
||||
<div class="flex items-center {borderBottomGrey} w-full">
|
||||
<IconButton onClick={handleClose}>
|
||||
<ArrowLeft24 class="carbon-icon" title="Go back" />
|
||||
</IconButton>
|
||||
<h3 class="">{selectedSection.title}</h3>
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
<div class="p-2 h-4/5 overflow-y">
|
||||
{#if selectedSection.key === 1}
|
||||
<HeaderForm />
|
||||
<HeaderForm bind:course />
|
||||
{:else if selectedSection.key === 2}
|
||||
<RequirementForm />
|
||||
<RequirementForm bind:course />
|
||||
{:else if selectedSection.key === 3}
|
||||
<DescriptionForm />
|
||||
<DescriptionForm bind:course />
|
||||
{:else if selectedSection.key === 4}
|
||||
<SyllabusForm />
|
||||
<GoalsForm bind:course />
|
||||
{:else if selectedSection.key === 5}
|
||||
<AboutUsForm />
|
||||
<InstructorForm bind:course />
|
||||
{:else if selectedSection.key === 6}
|
||||
<PricingForm bind:course />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<script lang="ts">
|
||||
import PrimaryButton from '../../PrimaryButton/index.svelte';
|
||||
import { VARIANTS } from '../../PrimaryButton/constants';
|
||||
import type { Course } from '../../../utils/types';
|
||||
import { get } from 'lodash';
|
||||
|
||||
export let className = '';
|
||||
export let courseData: Course = {
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
let calculatedCost = 0;
|
||||
let discount = 0;
|
||||
let formatter: Intl.NumberFormat | undefined;
|
||||
|
||||
function calcDisc(percent: number, cost: number) {
|
||||
if (!percent) return cost;
|
||||
return Math.round((percent / 100) * cost) || cost;
|
||||
}
|
||||
|
||||
function addToCart() {}
|
||||
|
||||
function buyNow() {}
|
||||
|
||||
function setFormatter(currency: string | undefined) {
|
||||
if (!currency) return;
|
||||
formatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currency,
|
||||
minimumFractionDigits: 2,
|
||||
});
|
||||
}
|
||||
|
||||
$: setFormatter(courseData.currency);
|
||||
$: discount = get(courseData, 'metadata.discount', 0);
|
||||
$: calculatedCost = calcDisc(discount, courseData.cost || 0);
|
||||
</script>
|
||||
|
||||
<!-- Pricing Details -->
|
||||
<aside
|
||||
class="{className} price-container md:sticky md:top-0 md:shadow-2xl md:rounded-lg m-h-fit"
|
||||
>
|
||||
<div class="p-10">
|
||||
<!-- Pricing -->
|
||||
<div class="mb-6">
|
||||
<p class="font-medium text-lg">
|
||||
{formatter?.format(calculatedCost) || calculatedCost}
|
||||
</p>
|
||||
{#if courseData?.metadata?.showDiscount}
|
||||
<p class="font-light text-sm text-gray-500">
|
||||
{discount}% Discount.
|
||||
<span class="line-through"
|
||||
>{formatter?.format(courseData?.cost || 0) || courseData.cost}</span
|
||||
>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Call To Action Buttons -->
|
||||
<div class="flex flex-col w-full items-center">
|
||||
<PrimaryButton
|
||||
label="Add to Cart"
|
||||
className="w-full sm:w-full py-3 mb-3"
|
||||
onClick={addToCart}
|
||||
/>
|
||||
<PrimaryButton
|
||||
label="Buy Now"
|
||||
className="w-full sm:w-full py-3 mb-3"
|
||||
variant={VARIANTS.OUTLINED}
|
||||
onClick={addToCart}
|
||||
/>
|
||||
<p class="font-light text-sm text-gray-500">Early bird offer. Buy ASAP</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gift Container -->
|
||||
{#if courseData?.metadata?.reward?.show}
|
||||
<div
|
||||
class="p-10 flex items-center flex-col border-t border-b border-gray-300"
|
||||
>
|
||||
{@html get(courseData, 'metadata.reward.description', '')}
|
||||
</div>
|
||||
{/if}
|
||||
</aside>
|
||||
|
||||
<style lang="scss">
|
||||
.banner {
|
||||
background-color: #040f2d;
|
||||
min-height: 472px;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: #7888b7;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
max-width: 559px;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.active {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.active::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: var(--main-primary-color);
|
||||
display: block;
|
||||
bottom: -14px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.price-container {
|
||||
width: 405px;
|
||||
min-width: 330px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.price-container {
|
||||
width: 100%;
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.course-content {
|
||||
max-width: 608px;
|
||||
}
|
||||
|
||||
:global(.list ul li) {
|
||||
margin-left: 1rem;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
:global(.plyr) {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,10 @@
|
||||
export const SECTION_KEYS = {
|
||||
HEADER: 'header',
|
||||
REQUIREMENT: 'requirement',
|
||||
DESCRIPTION: 'description',
|
||||
SYLLABUS: 'syllabus',
|
||||
ABOUT_US: 'about-us',
|
||||
REQUIREMENT: '#requirement',
|
||||
DESCRIPTION: '#description',
|
||||
GOALS: '#goals',
|
||||
LESSONS: '#lessons',
|
||||
INSTRUCTOR: '#instructor',
|
||||
};
|
||||
|
||||
export const NAV_ITEMS = [
|
||||
@@ -16,11 +17,15 @@ export const NAV_ITEMS = [
|
||||
label: 'Description',
|
||||
},
|
||||
{
|
||||
key: SECTION_KEYS.SYLLABUS,
|
||||
label: 'Syllabus',
|
||||
key: SECTION_KEYS.GOALS,
|
||||
label: 'Goals',
|
||||
},
|
||||
{
|
||||
key: SECTION_KEYS.ABOUT_US,
|
||||
label: 'About Us',
|
||||
key: SECTION_KEYS.LESSONS,
|
||||
label: 'Lessons',
|
||||
},
|
||||
{
|
||||
key: SECTION_KEYS.INSTRUCTOR,
|
||||
label: 'Instructor',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,163 +1,347 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
// import paystack from 'paystack';
|
||||
import get from 'lodash/get';
|
||||
import { onMount } from 'svelte';
|
||||
import RecentlyViewed16 from 'carbon-icons-svelte/lib/RecentlyViewed16';
|
||||
import CurrencyDollar16 from 'carbon-icons-svelte/lib/CurrencyDollar16';
|
||||
import Alarm16 from 'carbon-icons-svelte/lib/Alarm16';
|
||||
import pluralize from 'pluralize';
|
||||
import { stores } from '@sapper/app';
|
||||
import Expandable from '../Expandable/index.svelte';
|
||||
import Notebook from 'carbon-icons-svelte/lib/Notebook16/Notebook16.svelte'; //note
|
||||
import PresentationFile from 'carbon-icons-svelte/lib/PresentationFile16/PresentationFile16.svelte'; // exercise
|
||||
import Video from 'carbon-icons-svelte/lib/Video16/Video16.svelte'; //video
|
||||
import PageNumber from 'carbon-icons-svelte/lib/PageNumber16/PageNumber16.svelte';
|
||||
import PlayFilled from 'carbon-icons-svelte/lib/PlayFilled16/PlayFilled16.svelte';
|
||||
import PrimaryButton from '../PrimaryButton/index.svelte';
|
||||
import MarkdownRender from '../MarkdownRender/index.svelte';
|
||||
import { NAV_ITEMS, SECTION_KEYS } from './constants';
|
||||
import { landingPageStore } from './store';
|
||||
import PricingSection from './components/PricingSection.svelte';
|
||||
import { getLectureNo } from '../Course/function';
|
||||
import { NAV_ITEMS } from './constants';
|
||||
import Chip from '../Chip/index.svelte';
|
||||
import { getEmbedId } from '../../utils/functions/formatYoutubeVideo';
|
||||
import type { Course, Lesson } from '../../utils/types';
|
||||
|
||||
let activeNavItem = '';
|
||||
export let courseData: Course = {
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
};
|
||||
let lessons: Array<Lesson> = [];
|
||||
let player: any;
|
||||
|
||||
const expandableRootClass =
|
||||
'max-w-4xl pt-5 my-3 border-r-0 border-b border-l-0 border-gray-300';
|
||||
const { page } = stores();
|
||||
// const paystackApi = paystack('');
|
||||
|
||||
function handleEnrol() {}
|
||||
function handleNavItemClick(navItem) {
|
||||
return () => {
|
||||
activeNavItem = navItem;
|
||||
};
|
||||
let activeNav = NAV_ITEMS[0].key;
|
||||
let instructor = {};
|
||||
let video: string | undefined;
|
||||
|
||||
function playVideo() {
|
||||
console.log('open modal with video');
|
||||
}
|
||||
|
||||
function locationHashChanged() {
|
||||
activeNav = window.location.hash;
|
||||
}
|
||||
|
||||
function addToCart() {}
|
||||
|
||||
function buyNow() {
|
||||
// if (courseData.cost) {
|
||||
// paystackApi.tra;
|
||||
// } else {
|
||||
// // Course is free
|
||||
// }
|
||||
}
|
||||
|
||||
function initPlyr(_player: any, _video: string | undefined) {
|
||||
if (!player) return;
|
||||
|
||||
// @ts-ignore
|
||||
const plyr = new Plyr('#player', {});
|
||||
// @ts-ignore
|
||||
window.player = plyr;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
activeNavItem = (window.location.hash || NAV_ITEMS[0].key).replace('#', '');
|
||||
window.onhashchange = locationHashChanged;
|
||||
});
|
||||
|
||||
$: video = get(courseData, 'metadata.videoUrl');
|
||||
$: lessons = get(courseData, 'lessons', []);
|
||||
$: instructor = courseData?.metadata?.instructor || {};
|
||||
$: initPlyr(player, video);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{$landingPageStore.header.title}</title>
|
||||
<title>{get(courseData, 'title', '')}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="w-full bg-white flex flex-col items-center">
|
||||
<section
|
||||
class="w-full flex items-center justify-center bg-gray-900 text-white p-6 pb-10"
|
||||
>
|
||||
<div class="container flex justify-between items-center">
|
||||
<div class="course-description mr-3">
|
||||
<h1>
|
||||
{$landingPageStore.header.title}
|
||||
<!-- Header Section -->
|
||||
<header class="banner w-full flex items-center justify-center p-">
|
||||
<div
|
||||
class="md:w-5/6 w-full flex items-center justify-between flex-col-reverse md:flex-row"
|
||||
>
|
||||
<!-- Course Description -->
|
||||
<div class="md:w-2/5 w-11/12 py-10">
|
||||
<h1 class="text-5xl text-white my-4 font-bold">
|
||||
{get(courseData, 'title', '')}
|
||||
</h1>
|
||||
<h5>
|
||||
{$landingPageStore.header.description}
|
||||
</h5>
|
||||
<div>
|
||||
<span class="flex items-center">
|
||||
<RecentlyViewed16 class="carbon-icon mr-2" />
|
||||
Last updated 10/2021
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-container bg-white p-3 flex items-start flex-col">
|
||||
<p class="mb-2 text-black text-md flex items-center">
|
||||
<Alarm16 class="carbon-icon active mr-2" />
|
||||
{$landingPageStore.header.duration}
|
||||
<p class="text-md text-white mb-6">
|
||||
{get(courseData, 'description', '')}
|
||||
</p>
|
||||
<p class="mb-2 text-black text-md flex items-center">
|
||||
<CurrencyDollar16 class="carbon-icon active mr-2" />
|
||||
{$landingPageStore.header.currency}{$landingPageStore.header.cost}
|
||||
|
||||
<p class="author my-3 text-sm">
|
||||
{get(courseData, 'metadata.instructor.name', '')}
|
||||
</p>
|
||||
<PrimaryButton
|
||||
label={$landingPageStore.header.buttonLabel}
|
||||
onClick={handleEnrol}
|
||||
className="md:w-full"
|
||||
label="Start Course"
|
||||
className="px-6 py-5 mt-6 sm:w-fit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Banner Image getEmbedId(videoUrl) -->
|
||||
{#if video}
|
||||
<div class="banner-image md:w-2/3 w-full flex">
|
||||
<div
|
||||
bind:this={player}
|
||||
id="player"
|
||||
data-plyr-provider="youtube"
|
||||
data-plyr-embed-id={getEmbedId(video)}
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="container">
|
||||
<div
|
||||
bind:this={player}
|
||||
id="player"
|
||||
class="banner-image w-2/3 h-96 relative"
|
||||
data-plyr-provider="youtube"
|
||||
data-plyr-embed-id="bTqVqk7FSmY"
|
||||
/>
|
||||
</div> -->
|
||||
{:else}
|
||||
<!-- <div
|
||||
class="banner-image w-2/3 h-96 relative cursor-pointer"
|
||||
on:click={playVideo}
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="absolute top-0 left-0 backdrop w-full h-full"
|
||||
/>
|
||||
<img
|
||||
src="/images/course-banner-image.png"
|
||||
alt="banner"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-full z-50 flex items-center justify-center"
|
||||
>
|
||||
<img src="/images/play_video.png" alt="play video" />
|
||||
</div>
|
||||
</div> -->
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
</header>
|
||||
<PricingSection className="md:hidden" {courseData} />
|
||||
<!-- Body -->
|
||||
<div class="bg-white w-full">
|
||||
<div class="py-8 lg:w-10/12 w-full m-2 lg:m-auto flex justify-between">
|
||||
<!-- Course Details -->
|
||||
<div class="course-content w-full p-3 md:w-9/12 md:mr-14">
|
||||
<!-- Navigation -->
|
||||
<nav class="flex items-center border-b border-gray-300 pb-3">
|
||||
{#each NAV_ITEMS as navItem}
|
||||
<a
|
||||
href="{$page.path}{navItem.key}"
|
||||
class="{navItem.key === activeNav &&
|
||||
'active text-blue-700'} rounded-lg px-2 mr-6 text-slate-700 font-normal hover:bg-slate-100 hover:text-slate-900"
|
||||
>
|
||||
{navItem.label}
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<nav
|
||||
class="w-full z-10 max-w-4xl flex items-center justify-evenly py-4 px-4 bg-gray-100 mt-4 sticky top-0"
|
||||
>
|
||||
{#each NAV_ITEMS as navItem}
|
||||
<p
|
||||
class="text-md text-gray-900 {activeNavItem === navItem.key &&
|
||||
'font-bold'}"
|
||||
on:click={handleNavItemClick(navItem.key)}
|
||||
>
|
||||
<a href="{$page.path}#{navItem.key}">{navItem.label}</a>
|
||||
</p>
|
||||
{/each}
|
||||
</nav>
|
||||
<!-- Sections - Requirement -->
|
||||
<section id="requirement" class="border-b border-gray-300 mt-8 pb-10">
|
||||
<h3 class="text-2xl font-bold mt-0 mb-3">Requirement</h3>
|
||||
<ul class="list font-light">
|
||||
{@html get(courseData, 'metadata.requirements', '')}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<Expandable
|
||||
id={SECTION_KEYS.REQUIREMENT}
|
||||
title="Requirement"
|
||||
titleClass="text-2xl"
|
||||
rootClass={expandableRootClass}
|
||||
supportsLink={true}
|
||||
>
|
||||
<MarkdownRender
|
||||
content={$landingPageStore.requirement.content}
|
||||
disableMaxWidth={true}
|
||||
/>
|
||||
</Expandable>
|
||||
<!-- Sections - Course Description -->
|
||||
<section id="description" class="border-b border-gray-300 mt-8 pb-10">
|
||||
<h3 class="text-2xl font-bold mt-0 mb-3">Course Description</h3>
|
||||
<p class="text-sm font-light">
|
||||
{@html get(courseData, 'metadata.description', '')}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Expandable
|
||||
id={SECTION_KEYS.DESCRIPTION}
|
||||
title="Description"
|
||||
titleClass="text-2xl"
|
||||
rootClass={expandableRootClass}
|
||||
supportsLink={true}
|
||||
>
|
||||
<MarkdownRender
|
||||
content={$landingPageStore.description.content}
|
||||
disableMaxWidth={true}
|
||||
/>
|
||||
</Expandable>
|
||||
<!-- Sections - Goal -->
|
||||
<section id="goals" class="mt-8 pb-10">
|
||||
<h3 class="text-2xl font-bold mt-0 mb-3">What you'll learn</h3>
|
||||
<ul class="list font-light">
|
||||
{@html get(courseData, 'metadata.goals', '')}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<Expandable
|
||||
id={SECTION_KEYS.SYLLABUS}
|
||||
title="Syllabus"
|
||||
titleClass="text-2xl"
|
||||
rootClass={expandableRootClass}
|
||||
supportsLink={true}
|
||||
>
|
||||
{#each $landingPageStore.syllabus.lessons as lesson, index}
|
||||
<div class="flex items-center my-2">
|
||||
<strong class="mr-2">Lesson {index + 1}. </strong>
|
||||
{lesson.title}
|
||||
<!-- Sections - Certificate -->
|
||||
<section class="border-b border-gray-300 mt-8 pb-10">
|
||||
<h3 class="text-2xl font-bold mt-0">Certificate</h3>
|
||||
<p class="text-sm font-light mb-3">
|
||||
When you complete all of the courses in the program, you'll earn a
|
||||
Certificate to share with your professional network.
|
||||
</p>
|
||||
<img
|
||||
src="/images/certificate-template.svg"
|
||||
alt="certificate template"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<!-- Sections - Lessons -->
|
||||
<section id="lessons" class="border-b border-gray-300 mt-8 pb-10">
|
||||
<div class="flex items-center justify-between w-full mb-3">
|
||||
<h3 class="text-2xl font-bold mt-0 mb-3">Course Content</h3>
|
||||
<p class="text-sm font-light">
|
||||
<!-- {lessons.length} lessons -->
|
||||
{pluralize('lesson', lessons.length, true)}
|
||||
</p>
|
||||
</div>
|
||||
{#each lessons as lesson, index}
|
||||
<div
|
||||
class="w-full flex md:items-center justify-between mb-6 flex-col md:flex-row"
|
||||
>
|
||||
<div class="flex items-center mb-3">
|
||||
<Chip
|
||||
value={getLectureNo(index + 1, '0')}
|
||||
className="bg-blue-100 text-blue-700"
|
||||
/>
|
||||
<p class="ml-2 text-sm font-light">{lesson.title}</p>
|
||||
</div>
|
||||
|
||||
<div class="w-2/4">
|
||||
<div class="flex items-center">
|
||||
{#if lesson.slide_url}
|
||||
<span class="text-sm font-light flex w-2/4"
|
||||
><PresentationFile class="mr-1" />1 slide</span
|
||||
>
|
||||
{/if}
|
||||
{#if lesson.note}
|
||||
<span class="text-sm font-light flex w-2/4"
|
||||
><Notebook class="mr-1" />1 note</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
{#if lesson.video_url}
|
||||
<span class="text-sm font-light flex w-2/4"
|
||||
><Video class="mr-1" />1 video</span
|
||||
>
|
||||
{/if}
|
||||
{#if get(lesson, 'totalExercises[0].count')}
|
||||
<span class="text-sm font-light flex w-2/4"
|
||||
><PageNumber class="mr-1" />{pluralize(
|
||||
'exercise',
|
||||
get(lesson, 'totalExercises[0].count', 0),
|
||||
true
|
||||
)}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<!-- Sections - Instructor -->
|
||||
<section id="instructor" class="mt-8 pb-10">
|
||||
<h3 class="text-2xl font-bold mt-0 mb-3">Instructor</h3>
|
||||
<div class="flex items-center mb-4">
|
||||
<img
|
||||
alt="Author Avatar"
|
||||
class="block rounded-full h-20 w-20 mr-3"
|
||||
src={get(instructor, 'imgUrl', '')}
|
||||
/>
|
||||
<div>
|
||||
<p class="text-md font-light">{get(instructor, 'name', '')}</p>
|
||||
<p class="text-xs font-light">{get(instructor, 'role', '')}</p>
|
||||
<p class="text-md font-light flex items-center">
|
||||
<PlayFilled class="text-blue-700" />
|
||||
<span class="ml-1"
|
||||
>{get(instructor, 'courseNo', '')} courses</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm font-light">
|
||||
{get(instructor, 'description', '')}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
{/each}
|
||||
</Expandable>
|
||||
|
||||
<Expandable
|
||||
id={SECTION_KEYS.ABOUT_US}
|
||||
title="About Us - {$landingPageStore.aboutUs.companyName}"
|
||||
titleClass="text-2xl"
|
||||
rootClass={expandableRootClass}
|
||||
supportsLink={true}
|
||||
>
|
||||
<div class="w-full">
|
||||
<img
|
||||
src={$landingPageStore.aboutUs.logoUrl}
|
||||
alt={$landingPageStore.aboutUs.companyName}
|
||||
height="260"
|
||||
width="260"
|
||||
class="rounded-full m-auto"
|
||||
/>
|
||||
<!-- Pricing Details -->
|
||||
<PricingSection className="hidden md:block" {courseData} />
|
||||
</div>
|
||||
<MarkdownRender
|
||||
content={$landingPageStore.aboutUs.description}
|
||||
disableMaxWidth={true}
|
||||
/>
|
||||
</Expandable>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
max-width: 800px;
|
||||
.banner {
|
||||
background-color: #040f2d;
|
||||
min-height: 472px;
|
||||
}
|
||||
|
||||
.course-description {
|
||||
max-width: 500px;
|
||||
}
|
||||
.author {
|
||||
color: #7888b7;
|
||||
}
|
||||
|
||||
.action-container {
|
||||
height: fit-content;
|
||||
width: 200px;
|
||||
}
|
||||
.banner-image {
|
||||
max-width: 559px;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.active {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.active::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: var(--main-primary-color);
|
||||
display: block;
|
||||
bottom: -14px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.price-container {
|
||||
width: 405px;
|
||||
min-width: 330px;
|
||||
height: fit-content;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.course-content {
|
||||
max-width: 608px;
|
||||
}
|
||||
|
||||
nav {
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:global(.list ul li) {
|
||||
margin-left: 1rem;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
:global(.plyr) {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="root w-72 border bg-white px-2 py-2 mr-4 mb-4 rounded-md hover:shadow-2xl transition ease-in-out relative"
|
||||
class="root w-72 border bg-white px-2 py-3 mr-4 mb-4 rounded-md hover:shadow-2xl transition ease-in-out relative"
|
||||
>
|
||||
<Dropdown
|
||||
options={getOptions()}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
<div class="mt-5 flex items-center flex-row-reverse">
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
label="Create"
|
||||
type="submit"
|
||||
isLoading={$copyCourseModal.isSaving}
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
/>
|
||||
|
||||
<div class="mt-5 flex items-center">
|
||||
<PrimaryButton className="px-6 py-2" label="Finish" type="submit" />
|
||||
<PrimaryButton className="px-6 py-3" label="Finish" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import Box from '../Box/index.svelte';
|
||||
import { Firework } from 'svelte-loading-spinners';
|
||||
import { Moon } from 'svelte-loading-spinners';
|
||||
import Card from './components/Card/index.svelte';
|
||||
import CopyCourseModal from './components/CopyCourseModal/index.svelte';
|
||||
import { courses, courseMetaDeta } from './store';
|
||||
@@ -13,7 +13,7 @@
|
||||
>
|
||||
{#if $courseMetaDeta.isLoading}
|
||||
<Box>
|
||||
<Firework size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
</Box>
|
||||
{:else}
|
||||
{#each $courses as courseData}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
let open = false;
|
||||
|
||||
const notIconClass =
|
||||
'border rounded-lg border-grey py-2 px-3 focus:outline-none focus:border-gray-400 focus:bg-gray-200';
|
||||
'border rounded-lg border-grey py-3 px-3 focus:outline-none focus:border-gray-400 focus:bg-gray-200';
|
||||
|
||||
function isOptionLast(index) {
|
||||
return index === options.length - 1;
|
||||
@@ -41,11 +41,11 @@
|
||||
|
||||
{#if open}
|
||||
<div
|
||||
class="dropdown w-48 bg-white border border-grey rounded-lg mt-2 py-2 absolute"
|
||||
class="dropdown w-48 bg-white border border-grey rounded-lg mt-2 py-3 absolute"
|
||||
>
|
||||
{#each options as option, index}
|
||||
<button
|
||||
class="block px-4 py-2 {!isOptionLast(index) && 'border-b'} w-full"
|
||||
class="block px-4 py-3 {!isOptionLast(index) && 'border-b'} w-full"
|
||||
on:click={onClick(option)}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* your styles go here */
|
||||
</style>
|
||||
|
||||
<!-- markup (zero or more items) goes here -->
|
||||
<div class="my-1">
|
||||
<hr />
|
||||
<div class="flex justify-center w-full">
|
||||
<span class="text-gray-700 text-sm py-2">Created with love by
|
||||
<span class="text-gray-700 text-sm py-3"
|
||||
>Created with love by
|
||||
<a
|
||||
class="underline text-blue-700"
|
||||
href="https://twitter.com/rotimi_best"
|
||||
target="_blank">Rotimi Best</a></span>
|
||||
target="_blank">Rotimi Best</a
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* your styles go here */
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
export let rows = '3';
|
||||
export let maxRows = 3;
|
||||
export let className = '';
|
||||
export let bgColor = 'bg-gray-100';
|
||||
export let labelClassName = '';
|
||||
export let bgColor = 'bg-gray-200';
|
||||
export let helperMessage = '';
|
||||
export let errorMessage = '';
|
||||
export let isRequired = false;
|
||||
@@ -17,7 +18,7 @@
|
||||
|
||||
<label class="block w-full relative {className}">
|
||||
{#if label}
|
||||
<p for="text-field" class="">
|
||||
<p for="text-field" class={labelClassName}>
|
||||
{label}
|
||||
|
||||
{#if isRequired}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
export let helperMessage = '';
|
||||
export let autoComplete = true;
|
||||
export let onChange = () => {}; // This is to know if element is 'dirty'
|
||||
export let onInputChange = () => {};
|
||||
|
||||
let ref;
|
||||
let fieldNode;
|
||||
@@ -61,10 +62,11 @@
|
||||
{/if}
|
||||
<input
|
||||
use:typeAction
|
||||
class="form-input p-3 mt-1 block w-full border-none bg-gray-100 {inputClassName} {errorMessage
|
||||
class="form-input p-3 mt-1 block w-full border-none bg-gray-200 {inputClassName} {errorMessage
|
||||
? 'border-red-500'
|
||||
: ''}"
|
||||
on:keydown={onKeyDown}
|
||||
on:change={onInputChange}
|
||||
{placeholder}
|
||||
bind:value
|
||||
bind:this={ref}
|
||||
@@ -92,7 +94,7 @@
|
||||
<style>
|
||||
.password-eye {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
top: 23px;
|
||||
right: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -93,13 +93,13 @@
|
||||
<PrimaryButton
|
||||
label="Login"
|
||||
variant={VARIANTS.NONE}
|
||||
className="py-2 px-6 mx-2 app-background-color"
|
||||
className="py-3 px-6 mx-2 app-background-color"
|
||||
onClick={() => goto('/login')}
|
||||
/>
|
||||
<PrimaryButton
|
||||
label="Sign Up"
|
||||
variant={VARIANTS.CONTAINED}
|
||||
className="py-2 px-6 mx-2"
|
||||
className="py-3 px-6 mx-2"
|
||||
onClick={() => goto('/signup')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
export let isLoading = false;
|
||||
|
||||
$: variant = isLoading ? VARIANTS.OUTLINED : variant;
|
||||
$: isDisabled = isLoading ? true : isDisabled;
|
||||
$: isDisabled = isLoading ? true : false;
|
||||
</script>
|
||||
|
||||
<button
|
||||
@@ -21,15 +21,15 @@
|
||||
'bg-opacity-25 hover:bg-opacity-25 cursor-not-allowed'} flex items-center h-auto {VARIANTS_CLASS[
|
||||
variant
|
||||
]} {!disablePadding &&
|
||||
'py-1 px-5'} rounded-none {className} py-2 w-full justify-center sm:w-auto {isLoading &&
|
||||
'py-2 border-blue-700'} hover:shadow-xl transition delay-150 duration-300 ease-in-out"
|
||||
'py-1 px-5'} rounded-none {className} py-3 w-fit justify-center sm:w-auto {isLoading &&
|
||||
'py-3 border-blue-700'} hover:shadow-xl transition delay-150 duration-300 ease-in-out"
|
||||
on:click={onClick}
|
||||
{name}
|
||||
{type}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{#if isLoading}
|
||||
<Moon size="30" color="#1d4ed8" unit="px" duration="1s" />
|
||||
<Moon size="20" color="#1d4ed8" unit="px" duration="1s" />
|
||||
{:else if !!label}
|
||||
{label}
|
||||
{:else}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
|
||||
@@ -1,89 +1,77 @@
|
||||
<script>
|
||||
import marked from 'marked';
|
||||
import PrimaryButton from '../PrimaryButton/index.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isWriteMode = true;
|
||||
export let value = ``;
|
||||
export let placeholder = ``;
|
||||
const activeClassName = 'border-b-2 border-blue-700';
|
||||
export let onChange;
|
||||
export let content;
|
||||
export let disable;
|
||||
export let docId;
|
||||
export let placeholder = 'Type something...';
|
||||
|
||||
const handleTabClick = (tab) => (event) => {
|
||||
isWriteMode = tab === 1;
|
||||
const settings = {
|
||||
modules: {
|
||||
toolbar: {
|
||||
modules: {
|
||||
syntax: true,
|
||||
toolbar: [
|
||||
[{ header: [1, 2, 3, false] }],
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
['link', 'code-block'],
|
||||
],
|
||||
},
|
||||
placeholder: 'Type something...',
|
||||
theme: 'snow',
|
||||
},
|
||||
},
|
||||
theme: 'snow',
|
||||
placeholder,
|
||||
};
|
||||
|
||||
let quill = null;
|
||||
let container = null;
|
||||
|
||||
function resetContent(docId) {
|
||||
if (quill && quill.setText) {
|
||||
if (content) {
|
||||
quill.setContents(JSON.parse(content));
|
||||
} else {
|
||||
quill.setText('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
const { default: Quill } = await import('quill');
|
||||
|
||||
Quill.prototype.getHTML = function () {
|
||||
return this.container.querySelector('.ql-editor').innerHTML;
|
||||
};
|
||||
|
||||
Quill.prototype.setHTML = function (html) {
|
||||
this.container.querySelector('.ql-editor').innerHTML = html;
|
||||
};
|
||||
|
||||
quill = new Quill(container, settings);
|
||||
|
||||
quill.setHTML(content || '');
|
||||
|
||||
quill.on('text-change', function () {
|
||||
onChange(quill.getHTML());
|
||||
});
|
||||
});
|
||||
|
||||
$: {
|
||||
if (quill && quill.setText) {
|
||||
if (disable) {
|
||||
quill.disable(disable);
|
||||
} else {
|
||||
quill.enable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: resetContent(docId);
|
||||
</script>
|
||||
|
||||
<div class="root mt-5 border border-gray rounded-md">
|
||||
<div class="p-2 pb-0">
|
||||
<button
|
||||
on:click={handleTabClick(1)}
|
||||
class="focus:outline-none p-2 {isWriteMode && `${activeClassName}`}"
|
||||
>
|
||||
Write
|
||||
</button>
|
||||
<button
|
||||
on:click={handleTabClick(2)}
|
||||
class="focus:outline-none p-2 {!isWriteMode && `${activeClassName}`}"
|
||||
>
|
||||
Preview
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="m-2 mb-0 p-0">
|
||||
{#if isWriteMode}
|
||||
<textarea
|
||||
bind:value
|
||||
{placeholder}
|
||||
class="border border-gray rounded-md p-2"
|
||||
/>
|
||||
{:else}
|
||||
<article class="m-auto preview prose prose-sm sm:prose p-2">
|
||||
{@html marked(value)}
|
||||
</article>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !isWriteMode}
|
||||
<hr />
|
||||
{/if}
|
||||
<div class="flex justify-between items-center px-2 pb-1">
|
||||
<div class="flex items-center text-sm">
|
||||
<svg
|
||||
class="octicon mr-2"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="16"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Styling with Markdown is supported</span>
|
||||
</div>
|
||||
|
||||
<PrimaryButton label="Answer" />
|
||||
</div>
|
||||
<div class="p-2 w-full h-full">
|
||||
<div bind:this={container} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.preview {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.octicon {
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
<svelte:head>
|
||||
<title>{status}</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/carbon-components-svelte/css/g90.css"
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<div class="w-3/5 h-3/5 m-auto flex flex-col items-center justify-center">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { stores, goto } from '@sapper/app';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { Firework } from 'svelte-loading-spinners';
|
||||
import { Moon } from 'svelte-loading-spinners';
|
||||
import Tailwindcss from '../components/Tailwindcss.svelte';
|
||||
import Navigation from '../components/Navigation/index.svelte';
|
||||
import Snackbar from '../components/Snackbar/index.svelte';
|
||||
@@ -91,7 +91,7 @@
|
||||
// If user coming to login page, then
|
||||
if (!$profile.role) {
|
||||
goto(ROUTE.ONBOARDING);
|
||||
} else {
|
||||
} else if (['login', 'signup', 'onboarding'].includes(path)) {
|
||||
goto('/dashboard');
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,10 @@
|
||||
</script>
|
||||
|
||||
<svelte:window on:resize={handleResize} />
|
||||
|
||||
<svelte:head>
|
||||
<link href="/css/carbon.css" rel="stylesheet" />
|
||||
</svelte:head>
|
||||
<Tailwindcss />
|
||||
|
||||
<!-- <Nav {segment} /> -->
|
||||
@@ -169,7 +173,7 @@
|
||||
<main>
|
||||
{#if $preloading && $delayedPreloading}
|
||||
<Backdrop>
|
||||
<Firework size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
</Backdrop>
|
||||
{/if}
|
||||
{#if !ROUTES_TO_HIDE_NAV.includes($page.path) && !isCoursePage(path)}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<div class="flex items-center justify-evenly max-w-6xl m-auto">
|
||||
{#each boxes as box}
|
||||
<div
|
||||
class="bg-white rounded-md py-4 px-16 active shadow-md hover:shadow-2xl border-2 hover:border-blue-700 cursor-pointer {animate}"
|
||||
class="bg-white rounded-md py-3 px-16 active shadow-md hover:shadow-2xl border-2 hover:border-blue-700 cursor-pointer {animate}"
|
||||
>
|
||||
{#if box.icon === 'students'}
|
||||
<UserSpeaker32 class="carbon-icon" />
|
||||
@@ -106,7 +106,7 @@
|
||||
<div class="flex items-center justify-evenly mt-16">
|
||||
<div class="table rounded-md bg-white shadow-md">
|
||||
<div class="flex border-t-0 {borderBottomGrey} p-3">
|
||||
<h3 class="m-0 py-2 pl-2 text-lg">Outstanding students</h3>
|
||||
<h3 class="m-0 py-3 pl-2 text-lg">Outstanding students</h3>
|
||||
</div>
|
||||
|
||||
{#each topStudents as topStudent, index}
|
||||
@@ -123,7 +123,7 @@
|
||||
|
||||
<div class="table rounded-md bg-white shadow-md">
|
||||
<div class="flex border-t-0 {borderBottomGrey} p-3">
|
||||
<h3 class="m-0 py-2 pl-2 text-lg">Top rated courses</h3>
|
||||
<h3 class="m-0 py-3 pl-2 text-lg">Top rated courses</h3>
|
||||
</div>
|
||||
|
||||
{#each topCourses as topCourse, index}
|
||||
|
||||
+17
-17
@@ -1,22 +1,22 @@
|
||||
<script>
|
||||
import ChevronDown24 from "carbon-icons-svelte/lib/ChevronDown24";
|
||||
import TextEditor from "../components/TextEditor/index.svelte";
|
||||
import ChevronDown24 from 'carbon-icons-svelte/lib/ChevronDown24';
|
||||
import TextEditor from '../components/TextEditor/index.svelte';
|
||||
|
||||
let questionTitle = "";
|
||||
let questionDescription = "";
|
||||
let questionTitle = '';
|
||||
let questionDescription = '';
|
||||
let facultyFilterOpened = false;
|
||||
|
||||
const faculties = [
|
||||
"ИЭКСУ",
|
||||
"ИМБТ",
|
||||
"ИМИ",
|
||||
"ИПТДМ",
|
||||
"ИИИР",
|
||||
"ГФ",
|
||||
"ХТФ",
|
||||
"ИДЗО",
|
||||
"УИИ",
|
||||
"УНИ",
|
||||
'ИЭКСУ',
|
||||
'ИМБТ',
|
||||
'ИМИ',
|
||||
'ИПТДМ',
|
||||
'ИИИР',
|
||||
'ГФ',
|
||||
'ХТФ',
|
||||
'ИДЗО',
|
||||
'УИИ',
|
||||
'УНИ',
|
||||
];
|
||||
let checkbox = faculties.reduce((acc, cur) => {
|
||||
acc[cur] = false;
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="main-title my-1 flex">
|
||||
<div class="faculty-filter mr-1">
|
||||
<button
|
||||
class="flex border rounded-lg border-grey py-2 px-3 focus:outline-none focus:border-gray-400 focus:bg-gray-200 relative"
|
||||
class="flex border rounded-lg border-grey py-3 px-3 focus:outline-none focus:border-gray-400 focus:bg-gray-200 relative"
|
||||
on:click={handleFacultyFilters}
|
||||
>
|
||||
<span class="flex flex-row"
|
||||
@@ -49,10 +49,10 @@
|
||||
|
||||
{#if facultyFilterOpened}
|
||||
<div
|
||||
class="w-48 bg-white border border-grey rounded-lg mt-2 py-2 absolute"
|
||||
class="w-48 bg-white border border-grey rounded-lg mt-2 py-3 absolute"
|
||||
>
|
||||
{#each faculties as faculty}
|
||||
<div class="block px-4 py-2 border-b">
|
||||
<div class="block px-4 py-3 border-b">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="border border-gray checked:bg-blue-600 checked:border-transparent mr-1 cursor-pointer"
|
||||
|
||||
@@ -5,9 +5,39 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import Backdrop from '../../components/Backdrop/index.svelte';
|
||||
import { Moon } from 'svelte-loading-spinners';
|
||||
import CourseLandingPage from '../../components/CourseLandingPage/index.svelte';
|
||||
import { profile } from '../../utils/store/user';
|
||||
import { setCourse, course } from '../../components/Course/store';
|
||||
import { lessons } from '../../components/Course/components/Lesson/store/lessons';
|
||||
import { fetchCourse } from '../../utils/services/courses';
|
||||
|
||||
export let slug;
|
||||
|
||||
let courseData = {};
|
||||
let loading = false;
|
||||
|
||||
async function triggerSetCourse(_profile) {
|
||||
loading = true;
|
||||
const { data } = await fetchCourse(undefined, slug);
|
||||
setCourse(data);
|
||||
loading = false;
|
||||
}
|
||||
|
||||
function setCourseData(course, lessons) {
|
||||
courseData = { ...course, lessons };
|
||||
console.log('courseData', courseData);
|
||||
}
|
||||
|
||||
$: triggerSetCourse($profile);
|
||||
$: setCourseData($course, $lessons);
|
||||
</script>
|
||||
|
||||
<CourseLandingPage {slug} />
|
||||
{#if loading}
|
||||
<Backdrop>
|
||||
<Moon size="60" color="#1d4ed8" unit="px" duration="1s" />
|
||||
</Backdrop>
|
||||
{:else}
|
||||
<CourseLandingPage {courseData} />
|
||||
{/if}
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
import PageNav from '../../../components/PageNav/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import Box from '../../../components/Box/index.svelte';
|
||||
import {
|
||||
course,
|
||||
group,
|
||||
setCourseData,
|
||||
} from '../../../components/Course/store';
|
||||
import { course, group, setCourse } from '../../../components/Course/store';
|
||||
import { getLectureNo } from '../../../components/Course/function';
|
||||
import { fetchCourse } from '../../../utils/services/courses';
|
||||
import { lessons } from '../../../components/Course/components/Lesson/store/lessons';
|
||||
@@ -120,7 +116,7 @@
|
||||
}
|
||||
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
setAttendance(data);
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<script>
|
||||
import Overview from '../../../components/Course/index.svelte';
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
import { setCourseData } from '../../../components/Course/store';
|
||||
import { setCourse } from '../../../components/Course/store';
|
||||
import { fetchCourse } from '../../../utils/services/courses';
|
||||
|
||||
export let courseId;
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
const { data } = await fetchCourse(courseId);
|
||||
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
prevCourseId = courseId;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,28 @@
|
||||
import CourseLandingPage from '../../../components/CourseLandingPage/index.svelte';
|
||||
import Editor from '../../../components/CourseLandingPage/components/Editor/index.svelte';
|
||||
import { isMobile } from '../../../utils/store/useMobile';
|
||||
import { profile } from '../../../utils/store/user';
|
||||
import { setCourse, course } from '../../../components/Course/store';
|
||||
import { lessons } from '../../../components/Course/components/Lesson/store/lessons';
|
||||
import { fetchCourse } from '../../../utils/services/courses';
|
||||
|
||||
export let courseId;
|
||||
|
||||
let courseData = {};
|
||||
|
||||
async function triggerSetCourse(_profile) {
|
||||
if ($course.id) return;
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourse(data);
|
||||
}
|
||||
|
||||
function setCourseData(course, lessons) {
|
||||
courseData = { ...course, lessons };
|
||||
}
|
||||
|
||||
$: triggerSetCourse($profile);
|
||||
$: setCourseData($course, $lessons);
|
||||
$: console.log('courseData changed', courseData);
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -18,9 +38,9 @@
|
||||
in:fly={{ y: 500, duration: 500 }}
|
||||
out:fly={{ y: 500, duration: 500 }}
|
||||
>
|
||||
<Editor {courseId} />
|
||||
<Editor {courseId} bind:course={courseData} />
|
||||
<div class="rightBar" class:isMobile={$isMobile}>
|
||||
<CourseLandingPage />
|
||||
<CourseLandingPage bind:courseData />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
import Materials from '../../../../../components/Course/components/Lesson/Materials/index.svelte';
|
||||
import Exercises from '../../../../../components/Course/components/Lesson/Exercises/index.svelte';
|
||||
import MODES from '../../../../../utils/constants/mode.js';
|
||||
import {
|
||||
setCourseData,
|
||||
course,
|
||||
} from '../../../../../components/Course/store';
|
||||
import { setCourse, course } from '../../../../../components/Course/store';
|
||||
import { lesson } from '../../../../../components/Course/components/Lesson/store/lessons';
|
||||
|
||||
export let courseId;
|
||||
@@ -53,14 +50,10 @@
|
||||
|
||||
let lessonData;
|
||||
if (!$course.id) {
|
||||
const [_course, lesson] = await Promise.all([
|
||||
fetchCourse(courseId),
|
||||
fetchLesson(lessonId),
|
||||
]);
|
||||
const { data } = await fetchCourse(courseId);
|
||||
|
||||
lessonData = lesson.data;
|
||||
|
||||
setCourseData(_course.data);
|
||||
lessonData = data.lessons.find((lesson) => lesson.id === lessonId);
|
||||
setCourse(data);
|
||||
} else if (prevLessonId !== lessonId) {
|
||||
const lesson = await fetchLesson(lessonId);
|
||||
lessonData = lesson.data;
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
} from '../../../../components/Course/components/Lesson/store/lessons';
|
||||
import {
|
||||
course,
|
||||
setCourseData,
|
||||
setCourse,
|
||||
group,
|
||||
} from '../../../../components/Course/store';
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
onMount(async () => {
|
||||
if ($course.id) return;
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
});
|
||||
|
||||
$: {
|
||||
|
||||
@@ -12,11 +12,7 @@
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import Box from '../../../components/Box/index.svelte';
|
||||
import { ROLE } from '../../../utils/constants/roles';
|
||||
import {
|
||||
setCourseData,
|
||||
course,
|
||||
group,
|
||||
} from '../../../components/Course/store';
|
||||
import { setCourse, course, group } from '../../../components/Course/store';
|
||||
import { lessons } from '../../../components/Course/components/Lesson/store/lessons';
|
||||
import {
|
||||
fetchCourse,
|
||||
@@ -50,7 +46,7 @@
|
||||
onMount(async () => {
|
||||
if (!$course.id) {
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
}
|
||||
|
||||
const { data: marks } = await fetchMarks(courseId);
|
||||
|
||||
@@ -25,11 +25,7 @@
|
||||
import IconButton from '../../../components/IconButton/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import RoleBasedSecurity from '../../../components/RoleBasedSecurity/index.svelte';
|
||||
import {
|
||||
setCourseData,
|
||||
course,
|
||||
group,
|
||||
} from '../../../components/Course/store';
|
||||
import { setCourse, course, group } from '../../../components/Course/store';
|
||||
import InviationModal from '../../../components/Course/components/People/InviationModal.svelte';
|
||||
import DeleteConfirmation from '../../../components/Course/components/People/DeleteConfirmation.svelte';
|
||||
import {
|
||||
@@ -102,7 +98,7 @@
|
||||
onMount(async () => {
|
||||
if ($course.id) return;
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
});
|
||||
|
||||
$: sortAndFilterPeople($group.people, filterBy);
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
import PageNav from '../../../components/PageNav/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import { setCourseData } from '../../../components/Course/store';
|
||||
import { setCourse } from '../../../components/Course/store';
|
||||
|
||||
export let courseData = {};
|
||||
|
||||
onMount(() => {
|
||||
setCourseData(courseData);
|
||||
setCourse(courseData);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
import { fetchCourse } from '../../../utils/services/courses';
|
||||
import { setCourseData, course } from '../../../components/Course/store';
|
||||
import { setCourse, course } from '../../../components/Course/store';
|
||||
import {
|
||||
fetchSubmissions,
|
||||
updateSubmission,
|
||||
@@ -182,7 +182,7 @@
|
||||
onMount(async () => {
|
||||
if (!$course.id) {
|
||||
const { data } = await fetchCourse(courseId);
|
||||
setCourseData(data);
|
||||
setCourse(data);
|
||||
}
|
||||
|
||||
const { data: submissions } = await fetchSubmissions(
|
||||
@@ -297,7 +297,7 @@
|
||||
<div
|
||||
class="border {item.isEarly
|
||||
? 'border-grey-700'
|
||||
: 'border-red-700'} w-full my-2 mx-0 rounded-md bg-white py-2 px-3"
|
||||
: 'border-red-700'} w-full my-2 mx-0 rounded-md bg-white py-3 px-3"
|
||||
animate:flip={{ duration: flipDurationMs }}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
import CourseContainer from '../../../components/CourseContainer/index.svelte';
|
||||
import PageNav from '../../../components/PageNav/index.svelte';
|
||||
import PageBody from '../../../components/PageBody/index.svelte';
|
||||
import { setCourseData } from '../../../components/Course/store';
|
||||
import { setCourse } from '../../../components/Course/store';
|
||||
|
||||
export let courseData = {};
|
||||
|
||||
onMount(() => {
|
||||
setCourseData(courseData);
|
||||
setCourse(courseData);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
<p class="text-md mb-6 text-center">
|
||||
We have sent a confirmation email to <span class="text-blue-700"
|
||||
>{fields.email}</span
|
||||
>. Kindly check your inbox to reset password.
|
||||
>. Kindly check your inbox to reset password or spam to reset your
|
||||
password.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<div class="flex flex-col md:flex-row">
|
||||
{#each areas as area, index}
|
||||
<div
|
||||
class="box m-3 bg-white rounded-md py-4 px-12 active shadow-xl border-2 hover:border-blue-700 {animate}"
|
||||
class="box m-3 bg-white rounded-md py-3 px-12 active shadow-xl border-2 hover:border-blue-700 {animate}"
|
||||
>
|
||||
<h3 class="text-3xl">
|
||||
{#if index === 0}
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
<PrimaryButton
|
||||
label="Back"
|
||||
variant={VARIANTS.NONE}
|
||||
className="py-2 px-6 mr-2 text-blue-700"
|
||||
className="py-3 px-6 mr-2 text-blue-700"
|
||||
onClick={() => (step = step - 1)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
This are the organizations you work at
|
||||
</p>
|
||||
|
||||
<div class="hidden my-3 py-2 px-4 border shadow-sm rounded-md">
|
||||
<div class="hidden my-3 py-3 px-4 border shadow-sm rounded-md">
|
||||
<h4 class="m-0 font-normal">
|
||||
<a class="text-blue-700 font-bold" href="/courses"> CitiBim </a>
|
||||
</h4>
|
||||
@@ -204,7 +204,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden my-2 py-2 px-4 border shadow-sm rounded-md">
|
||||
<div class="hidden my-2 py-3 px-4 border shadow-sm rounded-md">
|
||||
<h4 class="m-0 font-normal">
|
||||
<a class="text-blue-700 font-bold" href="/courses">
|
||||
Climate Change Group
|
||||
@@ -226,7 +226,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<PrimaryButton
|
||||
className="px-6 py-2"
|
||||
className="px-6 py-3"
|
||||
variant={VARIANTS.CONTAINED_DANGER}
|
||||
label="Log out"
|
||||
onClick={logout}
|
||||
|
||||
+6
-3
@@ -12,7 +12,8 @@
|
||||
<link rel="manifest" href="manifest.json" crossorigin="use-credentials">
|
||||
<link rel="icon" type="image/png" href="favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/logo-32.png">
|
||||
|
||||
<link href="/css/quill.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.3/plyr.css" />
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-C7WBN0S06R"></script>
|
||||
<script>
|
||||
@@ -23,10 +24,12 @@
|
||||
gtag('config', 'G-C7WBN0S06R');
|
||||
</script>
|
||||
|
||||
<!--Crisp Chat -->
|
||||
<!--Crisp Chat
|
||||
<script
|
||||
type="text/javascript">window.$crisp = []; window.CRISP_WEBSITE_ID = "2158dbf9-90d4-4e50-b962-e1d8a1a6deb7"; (function () { d = document; s = d.createElement("script"); s.src = "https://client.crisp.chat/l.js"; s.async = 1; d.getElementsByTagName("head")[0].appendChild(s); })();</script>
|
||||
|
||||
-->
|
||||
<script src="https://cdn.plyr.io/3.7.3/plyr.js">
|
||||
</script>
|
||||
<!-- Sapper creates a <script> tag containing `src/client.js`
|
||||
and anything else it needs to hydrate the app and
|
||||
initialise the router -->
|
||||
|
||||
@@ -21,4 +21,10 @@ export const PUBLIC_ROUTES = [
|
||||
ROUTE.RESET,
|
||||
];
|
||||
|
||||
export const ROUTES_TO_HIDE_NAV = [...PUBLIC_ROUTES, ROUTE.ONBOARDING];
|
||||
export const ROUTES_TO_HIDE_NAV = [
|
||||
ROUTE.LOGIN,
|
||||
ROUTE.SIGN_UP,
|
||||
ROUTE.FORGOT,
|
||||
ROUTE.RESET,
|
||||
ROUTE.ONBOARDING,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
export function formatYoutubeVideo(url: string, errors: { video: string }) {
|
||||
console.log(`url`, url);
|
||||
const prefix = 'https://www.youtube.com/embed';
|
||||
|
||||
// https://www.youtube.com/embed/qajK1J1neAM
|
||||
if (url.includes('embed')) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// https://youtu.be/qajK1J1neAM
|
||||
if (url.includes('.be/')) {
|
||||
const splittedUrlWithBe = url.split('.be/');
|
||||
|
||||
return `${prefix}/${splittedUrlWithBe[1]}`;
|
||||
}
|
||||
|
||||
// https://www.youtube.com/watch?v=qajK1J1neAM
|
||||
const splitedUrl = url.split('watch');
|
||||
if (splitedUrl.length !== 2) {
|
||||
errors.video = 'Not a valid youtube link';
|
||||
return;
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(splitedUrl[1]);
|
||||
|
||||
return `${prefix}/${query.get('v')}`;
|
||||
}
|
||||
|
||||
export function getEmbedId(url: string) {
|
||||
// https://www.youtube.com/embed/qajK1J1neAM
|
||||
if (url.includes('embed')) {
|
||||
return url.split('embed/')[1];
|
||||
}
|
||||
|
||||
// https://youtu.be/qajK1J1neAM
|
||||
if (url.includes('.be/')) {
|
||||
const splittedUrlWithBe = url.split('.be/');
|
||||
|
||||
return splittedUrlWithBe[1];
|
||||
}
|
||||
|
||||
// https://www.youtube.com/watch?v=qajK1J1neAM
|
||||
const splitedUrl = url.split('watch');
|
||||
if (splitedUrl.length !== 2) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const query = new URLSearchParams(splitedUrl[1]);
|
||||
|
||||
return query.get('v');
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
export function isUUID(uuid) {
|
||||
export function isUUID(uuid: string) {
|
||||
let s = '' + uuid;
|
||||
|
||||
s = s.match(
|
||||
const m: RegExpMatchArray | null = s.match(
|
||||
'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
|
||||
);
|
||||
if (s === null) {
|
||||
|
||||
if (m === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
import { supabase } from '../../functions/supabase';
|
||||
import { isUUID } from '../../functions/isUUID';
|
||||
import { QUESTION_TYPE } from '../../../components/Question/constants';
|
||||
import type {
|
||||
Lesson,
|
||||
Course,
|
||||
Group,
|
||||
Groupmember,
|
||||
Exercise,
|
||||
} from '../../../utils/types';
|
||||
|
||||
export async function fetchCourse(courseId, session) {
|
||||
console.log(`courseId`, courseId);
|
||||
let jsonUser;
|
||||
export async function fetchCourse(
|
||||
courseId?: Course['id'],
|
||||
slug?: Course['slug']
|
||||
) {
|
||||
let match: { slug?: string; id?: string } = {};
|
||||
|
||||
if (!process.browser) {
|
||||
// try {
|
||||
// jsonUser = JSON.parse(session.user);
|
||||
// } catch (error) {
|
||||
// console.log('Token is expired probably cause user not found');
|
||||
// return {
|
||||
// data: [],
|
||||
// cantFetch: true,
|
||||
// };
|
||||
// // return this.redirect(301, '/login');
|
||||
// }
|
||||
if (slug) {
|
||||
match.slug = slug;
|
||||
} else {
|
||||
match.id = courseId;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
@@ -33,14 +35,19 @@ export async function fetchCourse(courseId, session) {
|
||||
profile(*)
|
||||
)
|
||||
),
|
||||
slug,
|
||||
cost,
|
||||
currency,
|
||||
metadata,
|
||||
lessons:lesson(
|
||||
id, title,public, lesson_at, call_url, is_complete, order, created_at,
|
||||
note, video_url, slide_url, call_url, totalExercises:exercise(count),
|
||||
profile:teacher_id(id, avatar_url, fullname)
|
||||
),
|
||||
attendance:group_attendance(*)
|
||||
`
|
||||
)
|
||||
.match({ id: courseId })
|
||||
.match(match)
|
||||
.single();
|
||||
console.log(`error`, error);
|
||||
if (!data || error) {
|
||||
@@ -56,7 +63,7 @@ export async function fetchCourse(courseId, session) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchGroup(groupId) {
|
||||
export async function fetchGroup(groupId: Group['id']) {
|
||||
const { data, error } = await supabase
|
||||
.from('group')
|
||||
.select(`*,members:groupmember(*,profile(*))`)
|
||||
@@ -69,7 +76,10 @@ export async function fetchGroup(groupId) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function setProfileIdOfGroupMember(email, profileId) {
|
||||
export async function setProfileIdOfGroupMember(
|
||||
email: string,
|
||||
profileId: string
|
||||
) {
|
||||
const { data, error } = await supabase
|
||||
.from('groupmember')
|
||||
.update({
|
||||
@@ -84,13 +94,41 @@ export async function setProfileIdOfGroupMember(email, profileId) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function updateCourse(courseId, course) {
|
||||
if (course.avatar) {
|
||||
export async function uploadAvatar(courseId: string, avatar: string) {
|
||||
const filename = `course/${courseId + Date.now()}.webp`;
|
||||
let logo;
|
||||
|
||||
const { data } = await supabase.storage
|
||||
.from('avatars')
|
||||
.upload(filename, avatar, {
|
||||
cacheControl: '3600',
|
||||
upsert: false,
|
||||
});
|
||||
|
||||
if (data && data.Key) {
|
||||
const { publicURL } = supabase.storage
|
||||
.from('avatars')
|
||||
.getPublicUrl(filename);
|
||||
|
||||
if (!publicURL) return;
|
||||
|
||||
logo = publicURL;
|
||||
}
|
||||
|
||||
return logo;
|
||||
}
|
||||
|
||||
export async function updateCourse(
|
||||
courseId: Course['id'],
|
||||
avatar: string | undefined,
|
||||
course: Course
|
||||
) {
|
||||
if (avatar) {
|
||||
const filename = `course/${courseId + Date.now()}.webp`;
|
||||
|
||||
const { data } = await supabase.storage
|
||||
.from('avatars')
|
||||
.upload(filename, course.avatar, {
|
||||
.upload(filename, avatar, {
|
||||
cacheControl: '3600',
|
||||
upsert: false,
|
||||
});
|
||||
@@ -100,12 +138,12 @@ export async function updateCourse(courseId, course) {
|
||||
.from('avatars')
|
||||
.getPublicUrl(filename);
|
||||
|
||||
if (!publicURL) return;
|
||||
|
||||
course.logo = publicURL;
|
||||
}
|
||||
}
|
||||
|
||||
delete course.avatar;
|
||||
|
||||
await supabase
|
||||
.from('course')
|
||||
.update(course, { returning: 'minimal' })
|
||||
@@ -114,28 +152,28 @@ export async function updateCourse(courseId, course) {
|
||||
return course.logo;
|
||||
}
|
||||
|
||||
export async function deleteCourse(courseId) {
|
||||
export async function deleteCourse(courseId: Course['id']) {
|
||||
return await supabase.from('course').delete().match({ id: courseId });
|
||||
}
|
||||
|
||||
export function addGroupMember(member) {
|
||||
export function addGroupMember(member: any) {
|
||||
return supabase.from('groupmember').insert(member, {
|
||||
returning: 'minimal',
|
||||
});
|
||||
}
|
||||
|
||||
export function updatedGroupMember(update, match) {
|
||||
export function updatedGroupMember(update: any, match: any) {
|
||||
return supabase
|
||||
.from('groupmember')
|
||||
.update(update, { returning: 'minimal' })
|
||||
.match(match);
|
||||
}
|
||||
|
||||
export function deleteGroupMember(groupMemberId) {
|
||||
export function deleteGroupMember(groupMemberId: Groupmember['id']) {
|
||||
return supabase.from('groupmember').delete().match({ id: groupMemberId });
|
||||
}
|
||||
|
||||
export function fetchLesson(lessonId) {
|
||||
export function fetchLesson(lessonId: Lesson['id']) {
|
||||
return supabase
|
||||
.from('lesson')
|
||||
.select(
|
||||
@@ -145,35 +183,39 @@ export function fetchLesson(lessonId) {
|
||||
.single();
|
||||
}
|
||||
|
||||
export function createLesson(lesson) {
|
||||
export function createLesson(lesson: any) {
|
||||
return supabase.from('lesson').insert(lesson);
|
||||
}
|
||||
|
||||
export function updateLesson(lesson, lessonId) {
|
||||
export function updateLesson(lesson: any, lessonId: Lesson['id']) {
|
||||
return supabase
|
||||
.from('lesson')
|
||||
.update({ ...lesson, id: undefined }, { returning: 'minimal' })
|
||||
.match({ id: lessonId });
|
||||
}
|
||||
|
||||
export function deleteLesson(lessonId) {
|
||||
export function deleteLesson(lessonId: Lesson['id']) {
|
||||
// Need to implement soft delete
|
||||
return supabase.from('lesson').delete().match({ id: lessonId });
|
||||
}
|
||||
|
||||
export function createExercise(exercise) {
|
||||
export function createExercise(exercise: any) {
|
||||
return supabase.from('exercise').insert(exercise);
|
||||
}
|
||||
|
||||
export function fetchExercisesByMarks(courseId) {
|
||||
export function fetchExercisesByMarks(courseId: Course['id']) {
|
||||
return supabase.rpc('get_exercises').eq('course_id', courseId);
|
||||
}
|
||||
|
||||
function isNew(item) {
|
||||
function isNew(item: any) {
|
||||
return isNaN(item);
|
||||
}
|
||||
|
||||
export async function upsertExercise(questionnaire, exerciseId) {
|
||||
// TODO: Add questionnaire type
|
||||
export async function upsertExercise(
|
||||
questionnaire: any,
|
||||
exerciseId: Exercise['id']
|
||||
) {
|
||||
const {
|
||||
questions,
|
||||
title,
|
||||
@@ -263,10 +305,12 @@ export async function upsertExercise(questionnaire, exerciseId) {
|
||||
// Delete cause this is not a field in the table
|
||||
delete newQuestion.question_type_id;
|
||||
|
||||
// @ts-ignore
|
||||
newQuestion.question_type = { id: question_type_id };
|
||||
newQuestion.id = id;
|
||||
newQuestion.name = name;
|
||||
newQuestion.order = order;
|
||||
// @ts-ignore
|
||||
newQuestion.options = [];
|
||||
|
||||
// Don't map options for 'Paragraph' questions
|
||||
@@ -297,9 +341,11 @@ export async function upsertExercise(questionnaire, exerciseId) {
|
||||
if (is_dirty || isNew(option.id)) {
|
||||
const { data } = await supabase.from('option').upsert(newOption);
|
||||
if (Array.isArray(data)) {
|
||||
// @ts-ignore
|
||||
newQuestion.options.push(data[0]);
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
newQuestion.options.push(newOption);
|
||||
}
|
||||
}
|
||||
@@ -312,12 +358,16 @@ export async function upsertExercise(questionnaire, exerciseId) {
|
||||
return updatedQuestions;
|
||||
}
|
||||
|
||||
interface LooseObject {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export async function submitExercise(
|
||||
answers,
|
||||
questions,
|
||||
exerciseId,
|
||||
courseId,
|
||||
groupMemberId
|
||||
answers: Array<string>,
|
||||
questions: Array<{ name: string; id: string }>,
|
||||
exerciseId: Exercise['id'],
|
||||
courseId: Course['id'],
|
||||
groupMemberId: Groupmember['id'] | undefined
|
||||
) {
|
||||
if (!groupMemberId) {
|
||||
return;
|
||||
@@ -326,7 +376,7 @@ export async function submitExercise(
|
||||
const questionsByName = questions.reduce(
|
||||
(acc, q) => ({ ...acc, [q.name]: q.id }),
|
||||
{}
|
||||
);
|
||||
) as LooseObject;
|
||||
const questionAnswers = [];
|
||||
|
||||
const { data: submission } = await supabase.from('submission').insert({
|
||||
@@ -343,7 +393,7 @@ export async function submitExercise(
|
||||
question_id: questionsByName[questionName],
|
||||
open_answer: '',
|
||||
answers: [],
|
||||
submission_id: submission[0].id,
|
||||
submission_id: Array.isArray(submission) ? submission[0].id : null,
|
||||
};
|
||||
|
||||
if (typeof value === 'string') {
|
||||
@@ -359,7 +409,10 @@ export async function submitExercise(
|
||||
console.log(`res`, res);
|
||||
}
|
||||
|
||||
export async function deleteExercise(questions, exerciseId) {
|
||||
export async function deleteExercise(
|
||||
questions: Array<{ id: string }>,
|
||||
exerciseId: Exercise['id']
|
||||
) {
|
||||
for (const question of questions) {
|
||||
const { id } = question;
|
||||
|
||||
+79
-68
@@ -1,25 +1,30 @@
|
||||
export * from './config';
|
||||
export * from './dashboard';
|
||||
|
||||
export interface CurrentSessionType {
|
||||
access_token: string;
|
||||
expires_at: number;
|
||||
expires_in: number;
|
||||
refresh_token: string;
|
||||
token_type: string;
|
||||
}
|
||||
|
||||
export interface UserType {
|
||||
openAuthModal: boolean;
|
||||
fetchingUser: boolean | null;
|
||||
isLoggedIn: boolean;
|
||||
currentSession: CurrentSessionType | null;
|
||||
expiresAt: number;
|
||||
interface CourseMetadata {
|
||||
requirements?: string;
|
||||
description?: string;
|
||||
goals?: string;
|
||||
videoUrl?: '';
|
||||
showDiscount: false;
|
||||
discount: 0;
|
||||
reward?: {
|
||||
show: boolean;
|
||||
description: string;
|
||||
};
|
||||
instructor?: {
|
||||
name: string;
|
||||
role: string;
|
||||
coursesNo: number;
|
||||
description: string;
|
||||
imgUrl: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Generated from https://supabase-schema.vercel.app/
|
||||
export interface Organization {
|
||||
id: string /* primary key */;
|
||||
name: string;
|
||||
name: any; // type unknown;
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
@@ -29,27 +34,30 @@ export interface Profile {
|
||||
avatar_url?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
email?: string;
|
||||
email?: any; // type unknown;
|
||||
can_add_course?: boolean;
|
||||
role?: any; // type unknown;
|
||||
goal?: any; // type unknown;
|
||||
source?: any; // type unknown;
|
||||
}
|
||||
|
||||
export interface Question_type {
|
||||
id: number /* primary key */;
|
||||
label: string;
|
||||
label: any; // type unknown;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
typename?: string;
|
||||
typename?: any; // type unknown;
|
||||
}
|
||||
|
||||
export interface Submissionstatus {
|
||||
id: number /* primary key */;
|
||||
label: string;
|
||||
label: any; // type unknown;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: string /* primary key */;
|
||||
name: string;
|
||||
name: any; // type unknown;
|
||||
description?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
@@ -58,8 +66,8 @@ export interface Group {
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
type: string;
|
||||
description?: string;
|
||||
type: any; // type unknown;
|
||||
description?: any; // type unknown;
|
||||
id: number /* primary key */;
|
||||
updated_at?: string;
|
||||
created_at?: string;
|
||||
@@ -67,24 +75,27 @@ export interface Role {
|
||||
|
||||
export interface Waitinglist {
|
||||
id: number /* primary key */;
|
||||
email: string;
|
||||
email: any; // type unknown;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
export interface Course {
|
||||
created_at?: string;
|
||||
description: string;
|
||||
group_id?: string /* foreign key to group.id */;
|
||||
group?: Group;
|
||||
title: any; // type unknown;
|
||||
description: any; // type unknown;
|
||||
overview?: any; // type unknown;
|
||||
id: string /* primary key */;
|
||||
is_template?: boolean;
|
||||
lessons: Lesson[];
|
||||
logo?: string;
|
||||
organization_id?: string /* foreign key to organization.id */;
|
||||
organization?: Organization;
|
||||
overview?: string;
|
||||
title: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
group_id?: string /* foreign key to group.id */;
|
||||
is_template?: boolean;
|
||||
organization_id?: string /* foreign key to organization.id */;
|
||||
logo?: string;
|
||||
slug?: any; // type unknown;
|
||||
metadata?: CourseMetadata;
|
||||
cost?: number;
|
||||
currency?: string;
|
||||
group?: Group;
|
||||
organization?: Organization;
|
||||
}
|
||||
|
||||
export interface Groupmember {
|
||||
@@ -92,9 +103,9 @@ export interface Groupmember {
|
||||
group_id: string /* foreign key to group.id */;
|
||||
role_id: number /* foreign key to role.id */;
|
||||
profile_id?: string /* foreign key to profile.id */;
|
||||
email?: string;
|
||||
email?: any; // type unknown;
|
||||
created_at?: string;
|
||||
assigned_student_id?: string;
|
||||
assigned_student_id?: any; // type unknown;
|
||||
group?: Group;
|
||||
role?: Role;
|
||||
profile?: Profile;
|
||||
@@ -123,46 +134,47 @@ export interface Group_attendance {
|
||||
}
|
||||
|
||||
export interface Lesson {
|
||||
call_url?: string;
|
||||
note?: any; // type unknown;
|
||||
video_url?: any; // type unknown;
|
||||
slide_url?: any; // type unknown;
|
||||
course_id: string /* foreign key to course.id */;
|
||||
created_at: string;
|
||||
exercise?: Exercise[];
|
||||
id: string /* primary key */;
|
||||
is_complete?: boolean;
|
||||
lesson_at?: string;
|
||||
note?: string;
|
||||
order: number;
|
||||
profile?: Profile;
|
||||
public?: boolean;
|
||||
slide_url?: string;
|
||||
teacher_id?: string /* foreign key to profile.id */;
|
||||
title: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
video_url?: string;
|
||||
title: any; // type unknown;
|
||||
public?: boolean;
|
||||
lesson_at?: string;
|
||||
teacher_id?: string /* foreign key to profile.id */;
|
||||
is_complete?: boolean;
|
||||
call_url?: string;
|
||||
order?: number;
|
||||
course?: Course;
|
||||
profile?: Profile;
|
||||
}
|
||||
|
||||
export interface Exercise {
|
||||
title: any; // type unknown;
|
||||
description?: any; // type unknown;
|
||||
lesson_id?: string /* foreign key to lesson.id */;
|
||||
created_at?: string;
|
||||
description?: string;
|
||||
due_by?: string;
|
||||
id: string /* primary key */;
|
||||
lesson_id: string /* foreign key */;
|
||||
questions?: Question[];
|
||||
title: string;
|
||||
updated_at?: string;
|
||||
id: string /* primary key */;
|
||||
due_by?: string;
|
||||
lesson?: Lesson;
|
||||
}
|
||||
|
||||
export interface Question {
|
||||
created_at?: string;
|
||||
exercise_id: string /* foreign key to exercise.id */;
|
||||
id: number /* primary key */;
|
||||
name?: string;
|
||||
options: Option[];
|
||||
order?: number;
|
||||
points?: string;
|
||||
question_type_id: number /* foreign key to question_type.id */;
|
||||
title: string;
|
||||
title: any; // type unknown;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
exercise_id: string /* foreign key to exercise.id */;
|
||||
name?: string;
|
||||
points?: any; // type unknown;
|
||||
order?: number;
|
||||
question_type?: Question_type;
|
||||
exercise?: Exercise;
|
||||
}
|
||||
|
||||
export interface Submission {
|
||||
@@ -182,15 +194,14 @@ export interface Submission {
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
created_at?: string;
|
||||
id?: number /* primary key */;
|
||||
id: number /* primary key */;
|
||||
label: any; // type unknown;
|
||||
is_correct: boolean;
|
||||
is_dirty?: boolean;
|
||||
label: string;
|
||||
question_id: number /* foreign key to question.id */;
|
||||
question?: Question;
|
||||
updated_at?: string;
|
||||
value?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
question?: Question;
|
||||
}
|
||||
|
||||
export interface Question_answer {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,957 @@
|
||||
/*!
|
||||
* Quill Editor v1.3.6
|
||||
* https://quilljs.com/
|
||||
* Copyright (c) 2014, Jason Chen
|
||||
* Copyright (c) 2013, salesforce.com
|
||||
*/
|
||||
.ql-container {
|
||||
box-sizing: border-box;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-clipboard {
|
||||
left: -100000px;
|
||||
height: 1px;
|
||||
overflow-y: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ql-clipboard p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ql-editor {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.42;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
padding: 12px 15px;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.ql-editor > * {
|
||||
cursor: text;
|
||||
}
|
||||
.ql-editor p,
|
||||
.ql-editor ol,
|
||||
.ql-editor ul,
|
||||
.ql-editor pre,
|
||||
.ql-editor blockquote,
|
||||
.ql-editor h1,
|
||||
.ql-editor h2,
|
||||
.ql-editor h3,
|
||||
.ql-editor h4,
|
||||
.ql-editor h5,
|
||||
.ql-editor h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol,
|
||||
.ql-editor ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol > li,
|
||||
.ql-editor ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.ql-editor ul > li::before {
|
||||
content: '\2022';
|
||||
}
|
||||
.ql-editor ul[data-checked='true'],
|
||||
.ql-editor ul[data-checked='false'] {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-editor ul[data-checked='true'] > li *,
|
||||
.ql-editor ul[data-checked='false'] > li * {
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked='true'] > li::before,
|
||||
.ql-editor ul[data-checked='false'] > li::before {
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked='true'] > li::before {
|
||||
content: '\2611';
|
||||
}
|
||||
.ql-editor ul[data-checked='false'] > li::before {
|
||||
content: '\2610';
|
||||
}
|
||||
.ql-editor li::before {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
width: 1.2em;
|
||||
}
|
||||
.ql-editor li:not(.ql-direction-rtl)::before {
|
||||
margin-left: -1.5em;
|
||||
margin-right: 0.3em;
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor li.ql-direction-rtl::before {
|
||||
margin-left: 0.3em;
|
||||
margin-right: -1.5em;
|
||||
}
|
||||
.ql-editor ol li:not(.ql-direction-rtl),
|
||||
.ql-editor ul li:not(.ql-direction-rtl) {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol li.ql-direction-rtl,
|
||||
.ql-editor ul li.ql-direction-rtl {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
.ql-editor ol li {
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
counter-increment: list-0;
|
||||
}
|
||||
.ql-editor ol li:before {
|
||||
content: counter(list-0, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-increment: list-1;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1:before {
|
||||
content: counter(list-1, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-increment: list-2;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2:before {
|
||||
content: counter(list-2, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-increment: list-3;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3:before {
|
||||
content: counter(list-3, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-increment: list-4;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4:before {
|
||||
content: counter(list-4, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-reset: list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-increment: list-5;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5:before {
|
||||
content: counter(list-5, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-reset: list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-increment: list-6;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6:before {
|
||||
content: counter(list-6, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-reset: list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-increment: list-7;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7:before {
|
||||
content: counter(list-7, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-reset: list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-increment: list-8;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8:before {
|
||||
content: counter(list-8, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-reset: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9 {
|
||||
counter-increment: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9:before {
|
||||
content: counter(list-9, decimal) '. ';
|
||||
}
|
||||
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-right {
|
||||
margin: 0 0 0 auto;
|
||||
}
|
||||
.ql-editor .ql-bg-black {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-editor .ql-bg-red {
|
||||
background-color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-bg-orange {
|
||||
background-color: #f90;
|
||||
}
|
||||
.ql-editor .ql-bg-yellow {
|
||||
background-color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-bg-green {
|
||||
background-color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-bg-blue {
|
||||
background-color: #06c;
|
||||
}
|
||||
.ql-editor .ql-bg-purple {
|
||||
background-color: #93f;
|
||||
}
|
||||
.ql-editor .ql-color-white {
|
||||
color: #fff;
|
||||
}
|
||||
.ql-editor .ql-color-red {
|
||||
color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-color-orange {
|
||||
color: #f90;
|
||||
}
|
||||
.ql-editor .ql-color-yellow {
|
||||
color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-color-green {
|
||||
color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-color-blue {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-editor .ql-color-purple {
|
||||
color: #93f;
|
||||
}
|
||||
.ql-editor .ql-font-serif {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-editor .ql-font-monospace {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-editor .ql-size-small {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
.ql-editor .ql-size-large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-editor .ql-size-huge {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.ql-editor .ql-direction-rtl {
|
||||
direction: rtl;
|
||||
text-align: inherit;
|
||||
}
|
||||
.ql-editor .ql-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.ql-editor .ql-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.ql-editor .ql-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor.ql-blank::before {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
content: attr(data-placeholder);
|
||||
font-style: italic;
|
||||
left: 15px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
.ql-snow.ql-toolbar:after,
|
||||
.ql-snow .ql-toolbar:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow.ql-toolbar button,
|
||||
.ql-snow .ql-toolbar button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 24px;
|
||||
padding: 3px 5px;
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow.ql-toolbar button svg,
|
||||
.ql-snow .ql-toolbar button svg {
|
||||
float: left;
|
||||
height: 100%;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:active:hover,
|
||||
.ql-snow .ql-toolbar button:active:hover {
|
||||
outline: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar input.ql-image[type='file'],
|
||||
.ql-snow .ql-toolbar input.ql-image[type='file'] {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover,
|
||||
.ql-snow .ql-toolbar button:hover,
|
||||
.ql-snow.ql-toolbar button:focus,
|
||||
.ql-snow .ql-toolbar button:focus,
|
||||
.ql-snow.ql-toolbar button.ql-active,
|
||||
.ql-snow .ql-toolbar button.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
|
||||
fill: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
|
||||
stroke: #06c;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active),
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) {
|
||||
color: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
|
||||
stroke: #444;
|
||||
}
|
||||
}
|
||||
.ql-snow {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow .ql-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-out-bottom,
|
||||
.ql-snow .ql-out-top {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
position: absolute;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-flip {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.ql-snow .ql-formats {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-formats:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow .ql-stroke {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-stroke-miter {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-fill,
|
||||
.ql-snow .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow .ql-empty {
|
||||
fill: none;
|
||||
}
|
||||
.ql-snow .ql-even {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
.ql-snow .ql-thin,
|
||||
.ql-snow .ql-stroke.ql-thin {
|
||||
stroke-width: 1;
|
||||
}
|
||||
.ql-snow .ql-transparent {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.ql-snow .ql-direction svg:last-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:last-child {
|
||||
display: inline;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:first-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-editor h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-editor h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-editor h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-editor h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-editor h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-editor h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-editor a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ql-snow .ql-editor blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.ql-snow .ql-editor code,
|
||||
.ql-snow .ql-editor pre {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ql-snow .ql-editor pre {
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.ql-snow .ql-editor code {
|
||||
font-size: 85%;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-editor pre.ql-syntax {
|
||||
background-color: #23241f;
|
||||
color: #f8f8f2;
|
||||
overflow: visible;
|
||||
}
|
||||
.ql-snow .ql-editor img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-picker-label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 2px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker-label::before {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
}
|
||||
.ql-snow .ql-picker-options {
|
||||
background-color: #fff;
|
||||
display: none;
|
||||
min-width: 100%;
|
||||
padding: 4px 8px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-picker-options .ql-picker-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
color: #ccc;
|
||||
z-index: 2;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
|
||||
fill: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
|
||||
stroke: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
top: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ql-snow .ql-color-picker,
|
||||
.ql-snow .ql-icon-picker {
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label svg,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label svg {
|
||||
right: 4px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-options {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-item {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-options {
|
||||
padding: 3px 5px;
|
||||
width: 152px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-item {
|
||||
border: 1px solid transparent;
|
||||
float: left;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
padding: 0px;
|
||||
width: 16px;
|
||||
}
|
||||
.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
|
||||
position: absolute;
|
||||
margin-top: -9px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
}
|
||||
.ql-snow
|
||||
.ql-picker.ql-header
|
||||
.ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow
|
||||
.ql-picker.ql-font
|
||||
.ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow
|
||||
.ql-picker.ql-size
|
||||
.ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow
|
||||
.ql-picker.ql-header
|
||||
.ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow
|
||||
.ql-picker.ql-font
|
||||
.ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow
|
||||
.ql-picker.ql-size
|
||||
.ql-picker-item[data-label]:not([data-label=''])::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
|
||||
content: 'Heading 1';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
|
||||
content: 'Heading 2';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
|
||||
content: 'Heading 3';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
|
||||
content: 'Heading 4';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
|
||||
content: 'Heading 5';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
|
||||
content: 'Heading 6';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font {
|
||||
width: 108px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: 'Sans Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
|
||||
content: 'Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
|
||||
content: 'Monospace';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
|
||||
content: 'Small';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
|
||||
content: 'Large';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
|
||||
content: 'Huge';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
|
||||
font-size: 10px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
|
||||
font-size: 32px;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-background .ql-picker-item {
|
||||
background-color: #fff;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-color .ql-picker-item {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
padding: 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-formats {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-label {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-options {
|
||||
border: 1px solid transparent;
|
||||
box-shadow: rgba(0, 0, 0, 0.2) 0 2px 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
|
||||
border-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow + .ql-container.ql-snow {
|
||||
border-top: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0px 0px 5px #ddd;
|
||||
color: #444;
|
||||
padding: 5px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-tooltip::before {
|
||||
content: 'Visit URL:';
|
||||
line-height: 26px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip input[type='text'] {
|
||||
display: none;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
height: 26px;
|
||||
margin: 0px;
|
||||
padding: 3px 5px;
|
||||
width: 170px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-preview {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-action::after {
|
||||
border-right: 1px solid #ccc;
|
||||
content: 'Edit';
|
||||
margin-left: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-remove::before {
|
||||
content: 'Remove';
|
||||
margin-left: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
line-height: 26px;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-preview,
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-remove {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing input[type='text'] {
|
||||
display: inline-block;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0px;
|
||||
content: 'Save';
|
||||
padding-right: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode='link']::before {
|
||||
content: 'Enter link:';
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode='formula']::before {
|
||||
content: 'Enter formula:';
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode='video']::before {
|
||||
content: 'Enter video:';
|
||||
}
|
||||
.ql-snow a {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-container.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 323 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Reference in New Issue
Block a user