From 26c1efa4518dbb72ebcd0c7e6d739d53279b315f Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Mon, 6 Apr 2020 21:22:37 +0800 Subject: [PATCH 01/14] Added GroundControl --- package-lock.json | 581 +++++++++++++++++- package.json | 1 + src/actions/actionTypes.ts | 7 + src/actions/groundcontrol.ts | 14 + src/actions/index.ts | 1 + src/components/academy/NavigationBar.tsx | 9 + src/components/academy/index.tsx | 2 + src/components/assessment/assessmentShape.ts | 1 + src/components/groundControl/DeleteCell.tsx | 59 ++ src/components/groundControl/Dropzone.tsx | 147 +++++ src/components/groundControl/EditCell.tsx | 103 ++++ .../groundControl/GroundControl.tsx | 233 +++++++ src/components/groundControl/PublishCell.tsx | 66 ++ .../groundControl/GroundControlContainer.ts | 36 ++ src/sagas/backend.ts | 102 +++ src/sagas/requests.ts | 68 ++ src/styles/index.scss | 1 + 17 files changed, 1414 insertions(+), 17 deletions(-) create mode 100644 src/actions/groundcontrol.ts create mode 100644 src/components/groundControl/DeleteCell.tsx create mode 100644 src/components/groundControl/Dropzone.tsx create mode 100644 src/components/groundControl/EditCell.tsx create mode 100644 src/components/groundControl/GroundControl.tsx create mode 100644 src/components/groundControl/PublishCell.tsx create mode 100644 src/containers/groundControl/GroundControlContainer.ts diff --git a/package-lock.json b/package-lock.json index cc9f046dc3..3df06e256c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,6 +112,155 @@ } } }, + "@blueprintjs/datetime": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/@blueprintjs/datetime/-/datetime-3.15.2.tgz", + "integrity": "sha512-FQw1BqbO9RBKzLWiXHkSVFxyGFRXHaugG5ST4go+p2IibrxuRDjD6YvrFXo+FLEzi+MsftMo6FkPNm2xApfmHw==", + "requires": { + "@blueprintjs/core": "^3.23.0", + "classnames": "^2.2", + "react-day-picker": "7.3.2", + "tslib": "~1.9.0" + }, + "dependencies": { + "@blueprintjs/core": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/@blueprintjs/core/-/core-3.24.0.tgz", + "integrity": "sha512-qW29DDPjzYsT27J6n97C0jZ1ifvEEziwNC98UhaKdSE7I8qxbLsb+ft2JOop+pEX4ab67T1lhQKAiQjWCPKZng==", + "requires": { + "@blueprintjs/icons": "^3.14.0", + "@types/dom4": "^2.0.1", + "classnames": "^2.2", + "dom4": "^2.1.5", + "normalize.css": "^8.0.1", + "popper.js": "^1.15.0", + "react-lifecycles-compat": "^3.0.4", + "react-popper": "^1.3.7", + "react-transition-group": "^2.9.0", + "resize-observer-polyfill": "^1.5.1", + "tslib": "~1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + } + } + }, + "@blueprintjs/icons": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.14.0.tgz", + "integrity": "sha512-cvQ3CSdy0DqVqcXcPqSxoycJw497TVP5goyE6xCFlVs84477ahxh7Uung6J+CCoDVBuI87h576LtuyjwSxorvQ==", + "requires": { + "classnames": "^2.2", + "tslib": "~1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + } + } + }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/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" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + } + } + }, "@blueprintjs/icons": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@blueprintjs/icons/-/icons-3.9.1.tgz", @@ -847,8 +996,7 @@ "@types/dom4": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz", - "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==", - "dev": true + "integrity": "sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==" }, "@types/dotenv": { "version": "6.1.1", @@ -5123,8 +5271,7 @@ "dom4": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/dom4/-/dom4-2.1.5.tgz", - "integrity": "sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ==", - "dev": true + "integrity": "sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ==" }, "domain-browser": { "version": "1.2.0", @@ -7297,8 +7444,7 @@ "gud": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", - "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==", - "dev": true + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" }, "gzip-size": { "version": "3.0.0", @@ -7442,8 +7588,7 @@ "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-unicode": { "version": "2.0.1", @@ -8423,6 +8568,11 @@ } } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -14883,8 +15033,7 @@ "object-is": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", - "dev": true + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" }, "object-keys": { "version": "1.0.12", @@ -14903,7 +15052,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -15593,8 +15741,7 @@ "popper.js": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", - "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==", - "dev": true + "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" }, "portfinder": { "version": "1.0.16", @@ -17400,6 +17547,31 @@ "prop-types": "^15.5.8" } }, + "react-day-picker": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-7.3.2.tgz", + "integrity": "sha512-mij2j2Un/v2V2ow+hf/hFBMdl6Eis/C/YhBtlI6Xpbvh3Q6WMix78zEkCdw6i9GldafOrpnupWKofv/h5oSI4g==", + "requires": { + "prop-types": "^15.6.2" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, "react-dev-utils": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.1.tgz", @@ -18046,6 +18218,81 @@ "safe-regex": "^1.1.0" } }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", @@ -18288,8 +18535,7 @@ "resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "dev": true + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" }, "resolve": { "version": "1.6.0", @@ -19840,6 +20086,308 @@ "function-bind": "^1.0.2" } }, + "string.prototype.trimend": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", + "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", + "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -20674,8 +21222,7 @@ "typed-styles": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", - "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==", - "dev": true + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" }, "typedarray": { "version": "0.0.6", diff --git a/package.json b/package.json index a46fd2ef52..ca5b3a063c 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ } }, "dependencies": { + "@blueprintjs/datetime": "^3.15.2", "@pusher/chatkit-client": "^1.5.0", "ace-builds": "^1.4.8", "acorn": "^4.0.13", diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts index 247ab8cda8..fdfa79bbe2 100755 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -118,3 +118,10 @@ export const FETCH_NOTIFICATIONS = 'FETCH_NOTIFICATIONS'; export const ACKNOWLEDGE_NOTIFICATIONS = 'ACKNOWLEDGE_NOTIFICATIONS'; export const UPDATE_NOTIFICATIONS = 'UPDATE_NOTIFICATIONS'; export const NOTIFY_CHATKIT_USERS = 'NOTIFY_CHATKIT_USERS'; + +/** GroundControl */ + +export const CHANGE_DATE_ASSESSMENT = 'CHANGE_DATE_ASSESSMENT'; +export const DELETE_ASSESSMENT = 'DELETE_ASSESSMENT'; +export const PUBLISH_ASSESSMENT = 'PUBLISH_ASSESSMENT'; +export const UPLOAD_ASSESSMENT = 'UPLOAD_ASSESSMENT'; diff --git a/src/actions/groundcontrol.ts b/src/actions/groundcontrol.ts new file mode 100644 index 0000000000..538be0f451 --- /dev/null +++ b/src/actions/groundcontrol.ts @@ -0,0 +1,14 @@ +import { action } from 'typesafe-actions'; + +import * as actionTypes from './actionTypes'; + +export const changeDateAssessment = (id: number, openAt: string, closeAt: string) => + action(actionTypes.CHANGE_DATE_ASSESSMENT, { id, openAt, closeAt }); + +export const deleteAssessment = (id: number) => action(actionTypes.DELETE_ASSESSMENT, id); + +export const publishAssessment = (bool: boolean, id: number) => + action(actionTypes.PUBLISH_ASSESSMENT, { id, bool }); + +export const uploadAssessment = (file: File, forceUpdate: boolean) => + action(actionTypes.UPLOAD_ASSESSMENT, { file, forceUpdate }); diff --git a/src/actions/index.ts b/src/actions/index.ts index 4f37fef163..3ad21f18ad 100755 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -1,6 +1,7 @@ export * from './collabEditing'; export * from './commons'; export * from './game'; +export * from './groundcontrol'; export * from './interpreter'; export * from './material'; export * from './playground'; diff --git a/src/components/academy/NavigationBar.tsx b/src/components/academy/NavigationBar.tsx index cbd9b8b740..949f4fe453 100644 --- a/src/components/academy/NavigationBar.tsx +++ b/src/components/academy/NavigationBar.tsx @@ -81,6 +81,15 @@ const NavigationBar: React.SFC = props => ( {props.role === Role.Admin || props.role === Role.Staff ? ( + + +
Ground Control
+
+ { render={assessmentRenderFactory(AssessmentCategories.Practical)} /> + diff --git a/src/components/assessment/assessmentShape.ts b/src/components/assessment/assessmentShape.ts index b8b731bf84..e4f9fa9782 100644 --- a/src/components/assessment/assessmentShape.ts +++ b/src/components/assessment/assessmentShape.ts @@ -27,6 +27,7 @@ export interface IAssessmentOverview { xp: number; gradingStatus: GradingStatus; private?: boolean; + isPublished?: boolean; } export enum AssessmentStatuses { diff --git a/src/components/groundControl/DeleteCell.tsx b/src/components/groundControl/DeleteCell.tsx new file mode 100644 index 0000000000..6ce2375d4d --- /dev/null +++ b/src/components/groundControl/DeleteCell.tsx @@ -0,0 +1,59 @@ +import { Classes, Dialog } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import * as React from 'react'; + +import { IAssessmentOverview } from '../assessment/assessmentShape'; +import { controlButton } from '../commons'; + +interface IDeleteCellProps { + data: IAssessmentOverview; + handleDeleteAssessment: (id: number) => void; +} + +interface IDeleteCellState { + dialogOpen: boolean; +} + +class DeleteCell extends React.Component { + public constructor(props: IDeleteCellProps) { + super(props); + this.state = { + dialogOpen: false + }; + } + + public render() { + return ( +
+ {controlButton('', IconNames.TRASH, this.handleOpenDialog)} + +
+ {

Are you sure to delete this Assessment?

} +
+
+
+ {controlButton('Confirm Delete', IconNames.TRASH, this.handleDelete)} + {controlButton('Cancel', IconNames.CROSS, this.handleCloseDialog)} +
+
+
+
+ ); + } + + private handleCloseDialog = () => this.setState({ dialogOpen: false }); + private handleOpenDialog = () => this.setState({ dialogOpen: true }); + private handleDelete = () => { + const { data } = this.props; + this.props.handleDeleteAssessment(data.id); + this.handleCloseDialog(); + }; +} + +export default DeleteCell; diff --git a/src/components/groundControl/Dropzone.tsx b/src/components/groundControl/Dropzone.tsx new file mode 100644 index 0000000000..f2b787ab5a --- /dev/null +++ b/src/components/groundControl/Dropzone.tsx @@ -0,0 +1,147 @@ +import { Card, Elevation, Switch } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import { FlexDirectionProperty } from 'csstype'; +import * as React from 'react'; +import { useDropzone } from 'react-dropzone'; + +import { controlButton } from '../commons'; + +interface IDispatchProps { + handleUploadAssessment: (file: File) => void; + toggleForceUpdate: () => void; + toggleDisplayConfirmation: () => void; +} + +interface IStateProps { + forceUpdate: boolean; + displayConfirmation: boolean; +} + +interface IDropzoneProps extends IDispatchProps, IStateProps {} + +// Dropzone styling +const dropZoneStyle = { + baseStyle: { + flex: 1, + display: 'flex', + height: '30vh', + flexDirection: 'column' as FlexDirectionProperty, + alignItems: 'center', + justifyContent: 'center', + padding: '20px', + borderWidth: 2, + borderRadius: 2, + borderColor: '#eeeeee', + borderStyle: 'dashed', + backgroundColor: '#fafafa', + color: '#bdbdbd', + outline: 'none', + transition: 'border .24s ease-in-out' + }, + + activeStyle: { + borderColor: '#2196f3' + }, + + acceptStyle: { + borderColor: '#00e676' + }, + + rejectStyle: { + borderColor: '#ff1744' + } +}; + +const MaterialDropzone: React.FC = props => { + const [file, setFile] = React.useState(); + const [title, setTitle] = React.useState(); + const handleConfirmUpload = () => { + props.handleUploadAssessment(file!); + setFile(undefined); + }; + const handleCancelUpload = () => setFile(undefined); + + const { + getRootProps, + getInputProps, + isDragActive, + isDragAccept, + isDragReject, + isFocused + } = useDropzone({ + onDrop: acceptedFiles => { + setFile(acceptedFiles[0]); + setTitle(acceptedFiles[0].name); + } + }); + const style = React.useMemo( + () => ({ + ...dropZoneStyle.baseStyle, + ...(isDragActive ? dropZoneStyle.activeStyle : {}), + ...(isDragAccept ? dropZoneStyle.acceptStyle : {}), + ...(isDragReject ? dropZoneStyle.rejectStyle : {}), + ...(isFocused ? dropZoneStyle.activeStyle : {}) + }), + [isDragActive, isDragAccept, isDragReject, isFocused] + ); + + const handleToggleOnChange = () => { + if (!props.forceUpdate) { + props.toggleDisplayConfirmation(); + props.toggleForceUpdate(); + } else { + props.toggleForceUpdate(); + } + }; + + const toggleButton = () => { + return ; + }; + + const handleConfirmForceUpdate = () => { + props.toggleDisplayConfirmation(); + }; + + const handleCancelForceUpdate = () => { + props.toggleDisplayConfirmation(); + props.toggleForceUpdate(); + }; + + const confirmationMessage = () => { + return ( +
+

Are you sure that you want to force update the assessment?

+ {controlButton('Yes', IconNames.CONFIRM, handleConfirmForceUpdate)} + {controlButton('No', IconNames.CROSS, handleCancelForceUpdate)} +
+ ); + }; + + return ( + <> + +
+ +

Drag 'n' drop some files here, or click to select files

+
+
+ {file && ( + +
{title}
+
+ {!props.displayConfirmation && + controlButton('Confirm Upload', IconNames.UPLOAD, handleConfirmUpload)} + {!props.displayConfirmation && + controlButton('Cancel Upload', IconNames.DELETE, handleCancelUpload)} +
+
+ {!props.displayConfirmation &&

Force update opened assessment

} + {props.displayConfirmation && confirmationMessage()} + {!props.displayConfirmation && toggleButton()} +
+ )} + + ); +}; + +export default MaterialDropzone; diff --git a/src/components/groundControl/EditCell.tsx b/src/components/groundControl/EditCell.tsx new file mode 100644 index 0000000000..ad2a91160c --- /dev/null +++ b/src/components/groundControl/EditCell.tsx @@ -0,0 +1,103 @@ +import { Classes, Dialog } from '@blueprintjs/core'; +import { DateInput } from '@blueprintjs/datetime'; +import { IconNames } from '@blueprintjs/icons'; +import * as React from 'react'; + +import { controlButton } from '../commons'; +import { IGroundControlAssessmentOverview } from './GroundControl'; + +interface IEditCellProps { + data: IGroundControlAssessmentOverview; + handleAssessmentChangeDate: (id: number, openAt: string, closeAt: string) => void; + forOpenDate: boolean; +} + +interface IEditCellState extends IEditCellDateState { + dialogOpen: boolean; +} + +interface IEditCellDateState { + openAt: Date; + closeAt: Date; +} + +class EditCell extends React.Component { + private maxDate = new Date(new Date(Date.now()).setFullYear(2100)); + + public constructor(props: IEditCellProps) { + super(props); + this.state = { + dialogOpen: false, + openAt: new Date(Date.parse(this.props.data.openAt)), + closeAt: new Date(Date.parse(this.props.data.closeAt)) + }; + } + + public render() { + const fieldName = this.props.forOpenDate ? 'Opening' : 'Closing'; + return ( +
+ {this.props.forOpenDate ? this.props.data.prettyOpenAt : this.props.data.prettyCloseAt} + {controlButton('', IconNames.EDIT, this.handleOpenDialog)} + +
+ {fieldName} Date: {this.dateInput()} +
+
+
+ {controlButton('Confirm Update', IconNames.TICK, this.handleUpdate)} + {controlButton('Cancel', IconNames.CROSS, this.handleCloseDialog)} +
+
+
+
+ ); + } + + private dateInput = () => { + return ( + + ); + }; + + private parseDate = (str: string) => new Date(str); + + private formatDate = (date: Date) => date.toLocaleString(); + + private handleDateChange = (selectedDate: Date) => { + if (this.props.forOpenDate) { + this.setState({ openAt: selectedDate }); + } else { + this.setState({ closeAt: selectedDate }); + } + }; + + private handleCloseDialog = () => this.setState({ dialogOpen: false }); + private handleOpenDialog = () => this.setState({ dialogOpen: true }); + private handleUpdate = () => { + const { data } = this.props; + this.props.handleAssessmentChangeDate( + data.id, + this.state.openAt.toISOString(), + this.state.closeAt.toISOString() + ); + this.handleCloseDialog(); + }; +} + +export default EditCell; diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx new file mode 100644 index 0000000000..4846ef7807 --- /dev/null +++ b/src/components/groundControl/GroundControl.tsx @@ -0,0 +1,233 @@ +import { ColDef, GridApi, GridReadyEvent } from 'ag-grid'; +import { AgGridReact } from 'ag-grid-react'; +import 'ag-grid/dist/styles/ag-grid.css'; +import 'ag-grid/dist/styles/ag-theme-balham.css'; +import * as React from 'react'; + +import { getPrettyDate } from '../../utils/dateHelpers'; +import { IAssessmentOverview } from '../assessment/assessmentShape'; +import ContentDisplay from '../commons/ContentDisplay'; +import DeleteCell from './DeleteCell'; +import Dropzone from './Dropzone'; +import EditCell from './EditCell'; +import PublishCell from './PublishCell'; + +export interface IDispatchProps { + handleAssessmentOverviewFetch: () => void; + handleDeleteAssessment: (id: number) => void; + handleUploadAssessment: (file: File, forceUpdate: boolean) => void; + handlePublishAssessment: (bool: boolean, id: number) => void; + handleAssessmentChangeDate: (id: number, openAt: string, closeAt: string) => void; +} + +export interface IGroundControlAssessmentOverview extends IAssessmentOverview { + prettyOpenAt?: string; + prettyCloseAt?: string; +} + +export interface IStateProps { + assessmentOverviews: IGroundControlAssessmentOverview[]; +} + +export interface IGroundControlProps extends IDispatchProps, IStateProps {} + +interface IGroundControlState { + forceUpdate: boolean; + displayConfirmation: boolean; +} + +class GroundControl extends React.Component { + private columnDefs: ColDef[]; + private gridApi?: GridApi; + + public constructor(props: IGroundControlProps) { + super(props); + this.state = { + forceUpdate: false, + displayConfirmation: false + }; + this.columnDefs = [ + { + headerName: 'Title', + field: 'title' + }, + { + headerName: 'Category', + field: 'category', + width: 100 + }, + { + headerName: 'Open Date', + field: '', + cellRendererFramework: EditCell, + cellRendererParams: { + handleAssessmentChangeDate: this.props.handleAssessmentChangeDate, + forOpenDate: true + }, + width: 150, + suppressSorting: true, + suppressMovable: true, + suppressMenu: true, + cellStyle: { + padding: 0 + } + }, + { + headerName: 'Close Date', + field: '', + cellRendererFramework: EditCell, + cellRendererParams: { + handleAssessmentChangeDate: this.props.handleAssessmentChangeDate, + forOpenDate: false + }, + width: 150, + suppressSorting: true, + suppressMovable: true, + suppressMenu: true, + cellStyle: { + padding: 0 + } + }, + { + headerName: 'Publish', + field: '', + cellRendererFramework: PublishCell, + cellRendererParams: { + handlePublishAssessment: this.props.handlePublishAssessment + }, + width: 100, + suppressSorting: true, + suppressMovable: true, + suppressMenu: true, + cellStyle: { + padding: 0 + }, + hide: !this.props.handlePublishAssessment + }, + { + headerName: 'Delete', + field: '', + cellRendererFramework: DeleteCell, + cellRendererParams: { + handleDeleteAssessment: this.props.handleDeleteAssessment + }, + width: 100, + suppressSorting: true, + suppressMovable: true, + suppressMenu: true, + cellStyle: { + padding: 0 + }, + hide: !this.props.handleDeleteAssessment + } + ]; + } + + public componentDidUpdate(prevProps: IGroundControlProps) { + if ( + this.gridApi && + this.props.assessmentOverviews.length !== prevProps.assessmentOverviews.length + ) { + this.gridApi.setRowData(this.sortByCategory()); + } + } + + public render() { + const data = this.sortByCategory(); + const Grid = () => ( +
+
+ +
+
+ ); + + const display = ( +
+ + +
+ ); + + return ( +
+ +
+ ); + } + + private sortByCategory = () => { + if (!this.props.assessmentOverviews) { + return []; + } + + const overview = this.props.assessmentOverviews.slice(); + return overview + .sort((assessmentX, assessmentY) => { + if (assessmentX.category < assessmentY.category) { + return -1; + } else if (assessmentX.category === assessmentY.category) { + return 0; + } else { + return 1; + } + }) + .map(assessmentOverview => { + const clone: IGroundControlAssessmentOverview = JSON.parse( + JSON.stringify(assessmentOverview) + ); + clone.prettyCloseAt = getPrettyDate(clone.closeAt); + clone.prettyOpenAt = getPrettyDate(clone.openAt); + return clone; + }); + }; + + private onGridReady = (params: GridReadyEvent) => { + this.gridApi = params.api; + this.gridApi.sizeColumnsToFit(); + }; + + private resizeGrid = () => { + if (this.gridApi) { + this.gridApi.sizeColumnsToFit(); + } + }; + + private handleUploadAssessment = (file: File) => { + this.props.handleUploadAssessment(file, this.state.forceUpdate); + this.setState({ forceUpdate: false }); + }; + + private toggleForceUpdate = () => { + this.setState({ forceUpdate: !this.state.forceUpdate }); + }; + + private toggleDisplayConfirmation = () => { + this.setState({ displayConfirmation: !this.state.displayConfirmation }); + }; +} + +export default GroundControl; diff --git a/src/components/groundControl/PublishCell.tsx b/src/components/groundControl/PublishCell.tsx new file mode 100644 index 0000000000..6e7fee2af4 --- /dev/null +++ b/src/components/groundControl/PublishCell.tsx @@ -0,0 +1,66 @@ +import { Classes, Dialog, Switch } from '@blueprintjs/core'; +import { IconNames } from '@blueprintjs/icons'; +import * as React from 'react'; + +import { IAssessmentOverview } from '../assessment/assessmentShape'; +import { controlButton } from '../commons'; + +interface IPublishCellProps { + data: IAssessmentOverview; + handlePublishAssessment: (bool: boolean, id: number) => void; +} + +interface IPublishCellState { + dialogOpen: boolean; + is_published: boolean; +} + +class PublishCell extends React.Component { + public constructor(props: IPublishCellProps) { + super(props); + this.state = { + dialogOpen: false, + is_published: this.props.data.isPublished === undefined ? false : this.props.data.isPublished + }; + } + + public render() { + const text = this.props.data.isPublished ? 'Unpublish' : 'Publish'; + const lowerText = text.charAt(0).toLowerCase() + text.substring(1); + return ( +
+ + +
+ {

Are you sure to {lowerText} this Assessment?

} +
+
+
+ {controlButton('Confirm ' + text, IconNames.CONFIRM, this.handleDelete)} + {controlButton('Cancel', IconNames.CROSS, this.handleCloseDialog)} +
+
+
+
+ ); + } + + private toggleButton = () => { + return ; + }; + private handleCloseDialog = () => this.setState({ dialogOpen: false }); + private handleOpenDialog = () => this.setState({ dialogOpen: true }); + private handleDelete = () => { + const { data } = this.props; + this.props.handlePublishAssessment(!data.isPublished, data.id); + this.handleCloseDialog(); + }; +} + +export default PublishCell; diff --git a/src/containers/groundControl/GroundControlContainer.ts b/src/containers/groundControl/GroundControlContainer.ts new file mode 100644 index 0000000000..e015ee40de --- /dev/null +++ b/src/containers/groundControl/GroundControlContainer.ts @@ -0,0 +1,36 @@ +import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; + +import { + changeDateAssessment, + deleteAssessment, + publishAssessment, + uploadAssessment +} from '../../actions/groundcontrol'; +import { fetchAssessmentOverviews } from '../../actions/session'; +import GroundControl, { + IDispatchProps, + IStateProps +} from '../../components/groundControl/GroundControl'; +import { IState } from '../../reducers/states'; + +const mapStateToProps: MapStateToProps = (state, props) => ({ + assessmentOverviews: state.session.assessmentOverviews ? state.session.assessmentOverviews : [] +}); + +const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => + bindActionCreators( + { + handleAssessmentOverviewFetch: fetchAssessmentOverviews, + handleDeleteAssessment: deleteAssessment, + handleUploadAssessment: uploadAssessment, + handlePublishAssessment: publishAssessment, + handleAssessmentChangeDate: changeDateAssessment + }, + dispatch + ); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(GroundControl); diff --git a/src/sagas/backend.ts b/src/sagas/backend.ts index f7df616127..e2575cfc67 100644 --- a/src/sagas/backend.ts +++ b/src/sagas/backend.ts @@ -555,6 +555,108 @@ function* backendSaga(): SagaIterator { yield put(actions.fetchMaterialIndex(parentId)); yield call(showSuccessMessage, 'Deleted successfully!', 1000); }); + + yield takeEvery(actionTypes.CHANGE_DATE_ASSESSMENT, function*( + action: ReturnType + ) { + const tokens = yield select((state: IState) => ({ + accessToken: state.session.accessToken, + refreshToken: state.session.refreshToken + })); + const id = action.payload.id; + const closeAt = action.payload.closeAt; + const openAt = action.payload.openAt; + const errorMessageWrapper: string[] = ['Something went wrong']; + const resp: Response = yield request.changeDateAssessment( + id, + closeAt, + openAt, + tokens, + errorMessageWrapper + ); + + if (!resp || !resp.ok) { + yield call(showWarningMessage, errorMessageWrapper[0], 5000); + return; + } + + yield put(actions.fetchAssessmentOverviews()); + yield call(showSuccessMessage, 'Updated successfully!', 1000); + }); + + yield takeEvery(actionTypes.DELETE_ASSESSMENT, function*( + action: ReturnType + ) { + const tokens = yield select((state: IState) => ({ + accessToken: state.session.accessToken, + refreshToken: state.session.refreshToken + })); + const id = action.payload; + const resp: Response = yield request.deleteAssessment(id, tokens); + + if (!resp || !resp.ok) { + yield request.handleResponseError(resp); + return; + } + + yield put(actions.fetchAssessmentOverviews()); + yield call(showSuccessMessage, 'Deleted successfully!', 1000); + }); + + yield takeEvery(actionTypes.PUBLISH_ASSESSMENT, function*( + action: ReturnType + ) { + const tokens = yield select((state: IState) => ({ + accessToken: state.session.accessToken, + refreshToken: state.session.refreshToken + })); + const id = action.payload.id; + const bool = action.payload.bool; + const resp: Response = yield request.publishAssessment(id, bool, tokens); + + if (!resp || !resp.ok) { + yield request.handleResponseError(resp); + return; + } + + yield put(actions.fetchAssessmentOverviews()); + + if (bool) { + yield call(showSuccessMessage, 'Published successfully!', 1000); + } else { + yield call(showSuccessMessage, 'Unpublished successfully!', 1000); + } + }); + + yield takeEvery(actionTypes.UPLOAD_ASSESSMENT, function*( + action: ReturnType + ) { + const tokens = yield select((state: IState) => ({ + accessToken: state.session.accessToken, + refreshToken: state.session.refreshToken + })); + const file = action.payload.file; + const forceUpdate = action.payload.forceUpdate; + const responseMessageWrapper = ['Something went wrong']; + const resp: Response = yield request.uploadAssessment( + file, + tokens, + responseMessageWrapper, + forceUpdate + ); + if (resp) { + if (responseMessageWrapper[0] === 'OK') { + yield call(showSuccessMessage, 'Uploaded successfully!', 2000); + } else if (responseMessageWrapper[0] === 'Force Update OK') { + yield call(showSuccessMessage, 'Assessment force updated successfully!', 2000); + } else { + yield call(showWarningMessage, responseMessageWrapper[0], 10000); + return; + } + } + + yield put(actions.fetchAssessmentOverviews()); + }); } export default backendSaga; diff --git a/src/sagas/requests.ts b/src/sagas/requests.ts index 9899982ab6..4fe28a9899 100644 --- a/src/sagas/requests.ts +++ b/src/sagas/requests.ts @@ -593,6 +593,74 @@ export const postMaterialFolder = async (title: string, parentId: number, tokens return resp; }; +export async function changeDateAssessment( + id: number, + closeAt: string, + openAt: string, + tokens: Tokens, + errorMessageWrapper: string[] +) { + const resp = await request(`assessments/update/${id}`, 'POST', { + accessToken: tokens.accessToken, + body: { closeAt, openAt }, + noHeaderAccept: true, + refreshToken: tokens.refreshToken, + shouldAutoLogout: false, + shouldRefresh: true + }); + if (resp && !resp.ok) { + await resp.text().then(errmsg => (errorMessageWrapper[0] = errmsg)); + } + return resp; +} + +export async function deleteAssessment(id: number, tokens: Tokens) { + const resp = await request(`assessments/${id}`, 'DELETE', { + accessToken: tokens.accessToken, + noHeaderAccept: true, + refreshToken: tokens.refreshToken, + shouldAutoLogout: false, + shouldRefresh: true + }); + return resp; +} + +export async function publishAssessment(id: number, bool: boolean, tokens: Tokens) { + const resp = await request(`assessments/publish/${id}`, 'POST', { + accessToken: tokens.accessToken, + body: { bool }, + noHeaderAccept: true, + refreshToken: tokens.refreshToken, + shouldAutoLogout: false, + shouldRefresh: true + }); + return resp; +} + +export const uploadAssessment = async ( + file: File, + tokens: Tokens, + responseMessageWrapper: string[], + forceUpdate: boolean +) => { + const formData = new FormData(); + formData.append('assessment[file]', file); + formData.append('forceUpdate', String(forceUpdate)); + const resp = await request(`assessments`, 'POST', { + accessToken: tokens.accessToken, + body: formData, + noContentType: true, + noHeaderAccept: true, + refreshToken: tokens.refreshToken, + shouldAutoLogout: false, + shouldRefresh: true + }); + if (resp) { + await resp.text().then(errmsg => (responseMessageWrapper[0] = errmsg)); + } + return resp; +}; + /** * @returns {(Response|null)} Response if successful, otherwise null. * diff --git a/src/styles/index.scss b/src/styles/index.scss index a3e324b547..8b12ccd4ef 100755 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,5 +1,6 @@ @import '~normalize.css/normalize.css'; @import '~@blueprintjs/core/lib/css/blueprint.css'; +@import '~@blueprintjs/datetime/lib/css/blueprint-datetime.css'; @import '~flexboxgrid/dist/flexboxgrid.css'; @import '~flexboxgrid-helpers/dist/flexboxgrid-helpers.min.css'; From 284f07cd5de1b765777ceed45c03556ccef98ed0 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Mon, 13 Apr 2020 20:26:19 +0800 Subject: [PATCH 02/14] Modified how response messages are handled --- src/sagas/backend.ts | 35 ++++++++++++++++------------------- src/sagas/requests.ts | 12 ++---------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/sagas/backend.ts b/src/sagas/backend.ts index e2575cfc67..ee38bb9ffd 100644 --- a/src/sagas/backend.ts +++ b/src/sagas/backend.ts @@ -566,17 +566,17 @@ function* backendSaga(): SagaIterator { const id = action.payload.id; const closeAt = action.payload.closeAt; const openAt = action.payload.openAt; - const errorMessageWrapper: string[] = ['Something went wrong']; - const resp: Response = yield request.changeDateAssessment( + const respMsg: string | null = yield request.changeDateAssessment( id, closeAt, openAt, tokens, - errorMessageWrapper ); - - if (!resp || !resp.ok) { - yield call(showWarningMessage, errorMessageWrapper[0], 5000); + if (respMsg == null) { + yield request.handleResponseError(respMsg); + return; + } else if (respMsg !== 'OK') { + yield call(showWarningMessage, respMsg, 5000); return; } @@ -637,24 +637,21 @@ function* backendSaga(): SagaIterator { })); const file = action.payload.file; const forceUpdate = action.payload.forceUpdate; - const responseMessageWrapper = ['Something went wrong']; - const resp: Response = yield request.uploadAssessment( + const respMsg = yield request.uploadAssessment( file, tokens, - responseMessageWrapper, forceUpdate ); - if (resp) { - if (responseMessageWrapper[0] === 'OK') { - yield call(showSuccessMessage, 'Uploaded successfully!', 2000); - } else if (responseMessageWrapper[0] === 'Force Update OK') { - yield call(showSuccessMessage, 'Assessment force updated successfully!', 2000); - } else { - yield call(showWarningMessage, responseMessageWrapper[0], 10000); - return; - } + if (!respMsg) { + yield request.handleResponseError(respMsg); + } else if (respMsg === 'OK') { + yield call(showSuccessMessage, 'Uploaded successfully!', 2000); + } else if (respMsg === 'Force Update OK') { + yield call(showSuccessMessage, 'Assessment force updated successfully!', 2000); + } else { + yield call(showWarningMessage, respMsg, 10000); + return; } - yield put(actions.fetchAssessmentOverviews()); }); } diff --git a/src/sagas/requests.ts b/src/sagas/requests.ts index 4fe28a9899..6cafc486a0 100644 --- a/src/sagas/requests.ts +++ b/src/sagas/requests.ts @@ -598,7 +598,6 @@ export async function changeDateAssessment( closeAt: string, openAt: string, tokens: Tokens, - errorMessageWrapper: string[] ) { const resp = await request(`assessments/update/${id}`, 'POST', { accessToken: tokens.accessToken, @@ -608,10 +607,7 @@ export async function changeDateAssessment( shouldAutoLogout: false, shouldRefresh: true }); - if (resp && !resp.ok) { - await resp.text().then(errmsg => (errorMessageWrapper[0] = errmsg)); - } - return resp; + return resp ? await resp.text() : null; } export async function deleteAssessment(id: number, tokens: Tokens) { @@ -640,7 +636,6 @@ export async function publishAssessment(id: number, bool: boolean, tokens: Token export const uploadAssessment = async ( file: File, tokens: Tokens, - responseMessageWrapper: string[], forceUpdate: boolean ) => { const formData = new FormData(); @@ -655,10 +650,7 @@ export const uploadAssessment = async ( shouldAutoLogout: false, shouldRefresh: true }); - if (resp) { - await resp.text().then(errmsg => (responseMessageWrapper[0] = errmsg)); - } - return resp; + return resp ? await resp.text() : null; }; /** From 0a34d4ae58dd25c4880592d80c826ab3460a0c39 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 18:14:11 +0800 Subject: [PATCH 03/14] Changed variable naming --- src/actions/groundcontrol.ts | 4 ++-- src/components/groundControl/GroundControl.tsx | 2 +- src/components/groundControl/PublishCell.tsx | 8 ++++---- src/sagas/backend.ts | 6 +++--- src/sagas/requests.ts | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/actions/groundcontrol.ts b/src/actions/groundcontrol.ts index 538be0f451..87e548ad99 100644 --- a/src/actions/groundcontrol.ts +++ b/src/actions/groundcontrol.ts @@ -7,8 +7,8 @@ export const changeDateAssessment = (id: number, openAt: string, closeAt: string export const deleteAssessment = (id: number) => action(actionTypes.DELETE_ASSESSMENT, id); -export const publishAssessment = (bool: boolean, id: number) => - action(actionTypes.PUBLISH_ASSESSMENT, { id, bool }); +export const publishAssessment = (togglePublishTo: boolean, id: number) => + action(actionTypes.PUBLISH_ASSESSMENT, { id, togglePublishTo }); export const uploadAssessment = (file: File, forceUpdate: boolean) => action(actionTypes.UPLOAD_ASSESSMENT, { file, forceUpdate }); diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx index 4846ef7807..513cb2ae5c 100644 --- a/src/components/groundControl/GroundControl.tsx +++ b/src/components/groundControl/GroundControl.tsx @@ -16,7 +16,7 @@ export interface IDispatchProps { handleAssessmentOverviewFetch: () => void; handleDeleteAssessment: (id: number) => void; handleUploadAssessment: (file: File, forceUpdate: boolean) => void; - handlePublishAssessment: (bool: boolean, id: number) => void; + handlePublishAssessment: (togglePublishTo: boolean, id: number) => void; handleAssessmentChangeDate: (id: number, openAt: string, closeAt: string) => void; } diff --git a/src/components/groundControl/PublishCell.tsx b/src/components/groundControl/PublishCell.tsx index 6e7fee2af4..38e8d57e6a 100644 --- a/src/components/groundControl/PublishCell.tsx +++ b/src/components/groundControl/PublishCell.tsx @@ -7,12 +7,12 @@ import { controlButton } from '../commons'; interface IPublishCellProps { data: IAssessmentOverview; - handlePublishAssessment: (bool: boolean, id: number) => void; + handlePublishAssessment: (togglePublishTo: boolean, id: number) => void; } interface IPublishCellState { dialogOpen: boolean; - is_published: boolean; + isPublished: boolean; } class PublishCell extends React.Component { @@ -20,7 +20,7 @@ class PublishCell extends React.Component super(props); this.state = { dialogOpen: false, - is_published: this.props.data.isPublished === undefined ? false : this.props.data.isPublished + isPublished: this.props.data.isPublished === undefined ? false : this.props.data.isPublished }; } @@ -52,7 +52,7 @@ class PublishCell extends React.Component } private toggleButton = () => { - return ; + return ; }; private handleCloseDialog = () => this.setState({ dialogOpen: false }); private handleOpenDialog = () => this.setState({ dialogOpen: true }); diff --git a/src/sagas/backend.ts b/src/sagas/backend.ts index ee38bb9ffd..b7280a6b98 100644 --- a/src/sagas/backend.ts +++ b/src/sagas/backend.ts @@ -611,8 +611,8 @@ function* backendSaga(): SagaIterator { refreshToken: state.session.refreshToken })); const id = action.payload.id; - const bool = action.payload.bool; - const resp: Response = yield request.publishAssessment(id, bool, tokens); + const togglePublishTo = action.payload.togglePublishTo; + const resp: Response = yield request.publishAssessment(id, togglePublishTo, tokens); if (!resp || !resp.ok) { yield request.handleResponseError(resp); @@ -621,7 +621,7 @@ function* backendSaga(): SagaIterator { yield put(actions.fetchAssessmentOverviews()); - if (bool) { + if (togglePublishTo) { yield call(showSuccessMessage, 'Published successfully!', 1000); } else { yield call(showSuccessMessage, 'Unpublished successfully!', 1000); diff --git a/src/sagas/requests.ts b/src/sagas/requests.ts index 6cafc486a0..2d624088f5 100644 --- a/src/sagas/requests.ts +++ b/src/sagas/requests.ts @@ -621,10 +621,10 @@ export async function deleteAssessment(id: number, tokens: Tokens) { return resp; } -export async function publishAssessment(id: number, bool: boolean, tokens: Tokens) { +export async function publishAssessment(id: number, togglePublishTo: boolean, tokens: Tokens) { const resp = await request(`assessments/publish/${id}`, 'POST', { accessToken: tokens.accessToken, - body: { bool }, + body: { togglePublishTo }, noHeaderAccept: true, refreshToken: tokens.refreshToken, shouldAutoLogout: false, From e91b01712c714269aba204a662d018901791fc56 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 18:50:19 +0800 Subject: [PATCH 04/14] Minor text changes --- src/components/groundControl/DeleteCell.tsx | 2 +- src/components/groundControl/PublishCell.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/groundControl/DeleteCell.tsx b/src/components/groundControl/DeleteCell.tsx index 6ce2375d4d..136ce70d91 100644 --- a/src/components/groundControl/DeleteCell.tsx +++ b/src/components/groundControl/DeleteCell.tsx @@ -34,7 +34,7 @@ class DeleteCell extends React.Component { canOutsideClickClose={true} >
- {

Are you sure to delete this Assessment?

} + {

Are you sure that you want to delete this Assessment?

}
diff --git a/src/components/groundControl/PublishCell.tsx b/src/components/groundControl/PublishCell.tsx index 38e8d57e6a..adeea3915d 100644 --- a/src/components/groundControl/PublishCell.tsx +++ b/src/components/groundControl/PublishCell.tsx @@ -26,7 +26,7 @@ class PublishCell extends React.Component public render() { const text = this.props.data.isPublished ? 'Unpublish' : 'Publish'; - const lowerText = text.charAt(0).toLowerCase() + text.substring(1); + const lowerCaseText = text.toLowerCase(); return (
@@ -38,7 +38,7 @@ class PublishCell extends React.Component canOutsideClickClose={true} >
- {

Are you sure to {lowerText} this Assessment?

} + {

Are you sure that you want to {lowerCaseText} this Assessment?

}
From 1f3e7f8702d792ae70f93988124e21af8bbd47d9 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 18:51:18 +0800 Subject: [PATCH 05/14] Updated NavigationBar snapshot --- .../__tests__/__snapshots__/NavigationBar.tsx.snap | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/academy/__tests__/__snapshots__/NavigationBar.tsx.snap b/src/components/academy/__tests__/__snapshots__/NavigationBar.tsx.snap index 7538039059..a5f3f32fa0 100644 --- a/src/components/academy/__tests__/__snapshots__/NavigationBar.tsx.snap +++ b/src/components/academy/__tests__/__snapshots__/NavigationBar.tsx.snap @@ -80,6 +80,12 @@ exports[`Grading NavLink renders for Role.Admin 1`] = ` + + +
+ Ground Control +
+
@@ -142,6 +148,12 @@ exports[`Grading NavLink renders for Role.Staff 1`] = ` + + +
+ Ground Control +
+
From b0d4f1bf1d330fc584f60128a4f151470ef7efb3 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 18:53:23 +0800 Subject: [PATCH 06/14] Minor formatting change --- src/sagas/backend.ts | 13 ++----------- src/sagas/requests.ts | 8 ++------ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/sagas/backend.ts b/src/sagas/backend.ts index b7280a6b98..84dad922e2 100644 --- a/src/sagas/backend.ts +++ b/src/sagas/backend.ts @@ -566,12 +566,7 @@ function* backendSaga(): SagaIterator { const id = action.payload.id; const closeAt = action.payload.closeAt; const openAt = action.payload.openAt; - const respMsg: string | null = yield request.changeDateAssessment( - id, - closeAt, - openAt, - tokens, - ); + const respMsg: string | null = yield request.changeDateAssessment(id, closeAt, openAt, tokens); if (respMsg == null) { yield request.handleResponseError(respMsg); return; @@ -637,11 +632,7 @@ function* backendSaga(): SagaIterator { })); const file = action.payload.file; const forceUpdate = action.payload.forceUpdate; - const respMsg = yield request.uploadAssessment( - file, - tokens, - forceUpdate - ); + const respMsg = yield request.uploadAssessment(file, tokens, forceUpdate); if (!respMsg) { yield request.handleResponseError(respMsg); } else if (respMsg === 'OK') { diff --git a/src/sagas/requests.ts b/src/sagas/requests.ts index 2d624088f5..77b42aceef 100644 --- a/src/sagas/requests.ts +++ b/src/sagas/requests.ts @@ -597,7 +597,7 @@ export async function changeDateAssessment( id: number, closeAt: string, openAt: string, - tokens: Tokens, + tokens: Tokens ) { const resp = await request(`assessments/update/${id}`, 'POST', { accessToken: tokens.accessToken, @@ -633,11 +633,7 @@ export async function publishAssessment(id: number, togglePublishTo: boolean, to return resp; } -export const uploadAssessment = async ( - file: File, - tokens: Tokens, - forceUpdate: boolean -) => { +export const uploadAssessment = async (file: File, tokens: Tokens, forceUpdate: boolean) => { const formData = new FormData(); formData.append('assessment[file]', file); formData.append('forceUpdate', String(forceUpdate)); From 14808c417be751d8335ff0c8f599ad93cef980d6 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 19:13:49 +0800 Subject: [PATCH 07/14] Minor naming changes --- src/actions/index.ts | 2 +- src/containers/groundControl/GroundControlContainer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/actions/index.ts b/src/actions/index.ts index 3ad21f18ad..a24f66eba4 100755 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -1,7 +1,7 @@ export * from './collabEditing'; export * from './commons'; export * from './game'; -export * from './groundcontrol'; +export * from './groundControl'; export * from './interpreter'; export * from './material'; export * from './playground'; diff --git a/src/containers/groundControl/GroundControlContainer.ts b/src/containers/groundControl/GroundControlContainer.ts index e015ee40de..fd6fd03c7c 100644 --- a/src/containers/groundControl/GroundControlContainer.ts +++ b/src/containers/groundControl/GroundControlContainer.ts @@ -6,7 +6,7 @@ import { deleteAssessment, publishAssessment, uploadAssessment -} from '../../actions/groundcontrol'; +} from '../../actions/groundControl'; import { fetchAssessmentOverviews } from '../../actions/session'; import GroundControl, { IDispatchProps, From 42f9c93fe92002b3686ffa74228fe187fb59403c Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Wed, 15 Apr 2020 21:26:32 +0800 Subject: [PATCH 08/14] Removed unused parameter --- src/containers/groundControl/GroundControlContainer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/groundControl/GroundControlContainer.ts b/src/containers/groundControl/GroundControlContainer.ts index fd6fd03c7c..10ce6f6b3c 100644 --- a/src/containers/groundControl/GroundControlContainer.ts +++ b/src/containers/groundControl/GroundControlContainer.ts @@ -14,7 +14,7 @@ import GroundControl, { } from '../../components/groundControl/GroundControl'; import { IState } from '../../reducers/states'; -const mapStateToProps: MapStateToProps = (state, props) => ({ +const mapStateToProps: MapStateToProps = state => ({ assessmentOverviews: state.session.assessmentOverviews ? state.session.assessmentOverviews : [] }); From 7b766645a7d384320120ab92eb99f33a41b4a426 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Sun, 19 Apr 2020 21:15:27 +0800 Subject: [PATCH 09/14] Added tests --- src/actions/__tests__/groundControl.ts | 57 +++++++++++++++++++ .../groundControl/GroundControlContainer.ts | 3 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/actions/__tests__/groundControl.ts diff --git a/src/actions/__tests__/groundControl.ts b/src/actions/__tests__/groundControl.ts new file mode 100644 index 0000000000..755ef42f2b --- /dev/null +++ b/src/actions/__tests__/groundControl.ts @@ -0,0 +1,57 @@ +import * as actionTypes from '../actionTypes'; +import { + changeDateAssessment, + deleteAssessment, + publishAssessment, + uploadAssessment +} from '../groundControl'; + +test('changeDateAssessment generates correct action object', () => { + const id = 10; + const openAt = "2020-01-01T00:00:00.000Z"; + const closeAt = "2021-01-01T00:00:00.000Z"; + const action = changeDateAssessment(id, openAt, closeAt); + expect(action).toEqual({ + type: actionTypes.CHANGE_DATE_ASSESSMENT, + payload: { + id, + openAt, + closeAt + } + }); +}); + +test('deleteAssessment generates correct action object', () => { + const id = 12; + const action = deleteAssessment(id); + expect(action).toEqual({ + type: actionTypes.DELETE_ASSESSMENT, + payload: id + }); +}); + +test('publishAssessment generates correct action object', () => { + const id = 54; + const togglePublishTo = false; + const action = publishAssessment(togglePublishTo, id); + expect(action).toEqual({ + type: actionTypes.PUBLISH_ASSESSMENT, + payload: { + togglePublishTo, + id + } + }); +}); + +test(' generates correct action object', () => { + const file = new File([""], "testFile"); + const forceUpdate = true; + const action = uploadAssessment(file, forceUpdate); + expect(action).toEqual({ + type: actionTypes.UPLOAD_ASSESSMENT, + payload: { + file, + forceUpdate + } + }); +}); \ No newline at end of file diff --git a/src/containers/groundControl/GroundControlContainer.ts b/src/containers/groundControl/GroundControlContainer.ts index 10ce6f6b3c..bc3a63b6d8 100644 --- a/src/containers/groundControl/GroundControlContainer.ts +++ b/src/containers/groundControl/GroundControlContainer.ts @@ -21,11 +21,12 @@ const mapStateToProps: MapStateToProps = state => ({ const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => bindActionCreators( { + handleAssessmentChangeDate: changeDateAssessment, handleAssessmentOverviewFetch: fetchAssessmentOverviews, handleDeleteAssessment: deleteAssessment, handleUploadAssessment: uploadAssessment, handlePublishAssessment: publishAssessment, - handleAssessmentChangeDate: changeDateAssessment + }, dispatch ); From cbee2c9587c3791a5c14c4c6251edae894de0121 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Sun, 19 Apr 2020 21:17:21 +0800 Subject: [PATCH 10/14] Ran tslint --- src/actions/__tests__/groundControl.ts | 10 +++++----- src/containers/groundControl/GroundControlContainer.ts | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/actions/__tests__/groundControl.ts b/src/actions/__tests__/groundControl.ts index 755ef42f2b..be02d7d3ab 100644 --- a/src/actions/__tests__/groundControl.ts +++ b/src/actions/__tests__/groundControl.ts @@ -8,8 +8,8 @@ import { test('changeDateAssessment generates correct action object', () => { const id = 10; - const openAt = "2020-01-01T00:00:00.000Z"; - const closeAt = "2021-01-01T00:00:00.000Z"; + const openAt = '2020-01-01T00:00:00.000Z'; + const closeAt = '2021-01-01T00:00:00.000Z'; const action = changeDateAssessment(id, openAt, closeAt); expect(action).toEqual({ type: actionTypes.CHANGE_DATE_ASSESSMENT, @@ -44,8 +44,8 @@ test('publishAssessment generates correct action object', () => { }); test(' generates correct action object', () => { - const file = new File([""], "testFile"); - const forceUpdate = true; + const file = new File([''], 'testFile'); + const forceUpdate = true; const action = uploadAssessment(file, forceUpdate); expect(action).toEqual({ type: actionTypes.UPLOAD_ASSESSMENT, @@ -54,4 +54,4 @@ test(' generates correct action object', () => { forceUpdate } }); -}); \ No newline at end of file +}); diff --git a/src/containers/groundControl/GroundControlContainer.ts b/src/containers/groundControl/GroundControlContainer.ts index bc3a63b6d8..84610c527d 100644 --- a/src/containers/groundControl/GroundControlContainer.ts +++ b/src/containers/groundControl/GroundControlContainer.ts @@ -25,8 +25,7 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Di handleAssessmentOverviewFetch: fetchAssessmentOverviews, handleDeleteAssessment: deleteAssessment, handleUploadAssessment: uploadAssessment, - handlePublishAssessment: publishAssessment, - + handlePublishAssessment: publishAssessment }, dispatch ); From 10bc06d0297139f09021a1d8b9a1e42af9c6f267 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Mon, 20 Apr 2020 15:05:40 +0800 Subject: [PATCH 11/14] Modified width of toggle button and warning text when deleting assessments --- src/components/groundControl/DeleteCell.tsx | 1 + src/components/groundControl/Dropzone.tsx | 6 +++++- src/components/groundControl/GroundControl.tsx | 2 +- src/components/groundControl/PublishCell.tsx | 12 ++++++++---- src/styles/_groundcontrol.scss | 7 +++++++ src/styles/index.scss | 1 + 6 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/styles/_groundcontrol.scss diff --git a/src/components/groundControl/DeleteCell.tsx b/src/components/groundControl/DeleteCell.tsx index 136ce70d91..0605614ed6 100644 --- a/src/components/groundControl/DeleteCell.tsx +++ b/src/components/groundControl/DeleteCell.tsx @@ -35,6 +35,7 @@ class DeleteCell extends React.Component { >
{

Are you sure that you want to delete this Assessment?

} + {

Students' answers and submissions will be deleted as well.

}
diff --git a/src/components/groundControl/Dropzone.tsx b/src/components/groundControl/Dropzone.tsx index f2b787ab5a..fc9cf91935 100644 --- a/src/components/groundControl/Dropzone.tsx +++ b/src/components/groundControl/Dropzone.tsx @@ -95,7 +95,11 @@ const MaterialDropzone: React.FC = props => { }; const toggleButton = () => { - return ; + return ( +
+ +
+ ); }; const handleConfirmForceUpdate = () => { diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx index 513cb2ae5c..487b5028aa 100644 --- a/src/components/groundControl/GroundControl.tsx +++ b/src/components/groundControl/GroundControl.tsx @@ -157,7 +157,7 @@ class GroundControl extends React.Component +
public render() { const text = this.props.data.isPublished ? 'Unpublish' : 'Publish'; const lowerCaseText = text.toLowerCase(); + const toggleButton = () => { + return ( +
+ +
+ ); + }; return (
- + {toggleButton()} ); } - private toggleButton = () => { - return ; - }; private handleCloseDialog = () => this.setState({ dialogOpen: false }); private handleOpenDialog = () => this.setState({ dialogOpen: true }); private handleDelete = () => { diff --git a/src/styles/_groundcontrol.scss b/src/styles/_groundcontrol.scss new file mode 100644 index 0000000000..057f083749 --- /dev/null +++ b/src/styles/_groundcontrol.scss @@ -0,0 +1,7 @@ +.GroundControl { + .toggle-button-wrapper { + margin-left: auto; + margin-right: auto; + width: 20px; + } +} diff --git a/src/styles/index.scss b/src/styles/index.scss index e4614f5633..b255120a54 100755 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -24,6 +24,7 @@ @import 'contributors'; @import 'dropdown'; @import 'game'; +@import 'groundcontrol'; @import 'login'; @import 'material'; @import 'navigationBar'; From ab7c4d598af705ebf83f894e51196cd5895921f9 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Mon, 20 Apr 2020 15:30:57 +0800 Subject: [PATCH 12/14] Modified how assessments are sorted in GroundControl --- .../groundControl/GroundControl.tsx | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx index 487b5028aa..96ed541877 100644 --- a/src/components/groundControl/GroundControl.tsx +++ b/src/components/groundControl/GroundControl.tsx @@ -2,6 +2,7 @@ import { ColDef, GridApi, GridReadyEvent } from 'ag-grid'; import { AgGridReact } from 'ag-grid-react'; import 'ag-grid/dist/styles/ag-grid.css'; import 'ag-grid/dist/styles/ag-theme-balham.css'; +import { sortBy } from 'lodash'; import * as React from 'react'; import { getPrettyDate } from '../../utils/dateHelpers'; @@ -21,12 +22,14 @@ export interface IDispatchProps { } export interface IGroundControlAssessmentOverview extends IAssessmentOverview { - prettyOpenAt?: string; - prettyCloseAt?: string; + prettyOpenAt: string; + prettyCloseAt: string; + formattedOpenAt: Date; + formattedCloseAt: Date; } export interface IStateProps { - assessmentOverviews: IGroundControlAssessmentOverview[]; + assessmentOverviews: IAssessmentOverview[]; } export interface IGroundControlProps extends IDispatchProps, IStateProps {} @@ -184,25 +187,19 @@ class GroundControl extends React.Component { - if (assessmentX.category < assessmentY.category) { - return -1; - } else if (assessmentX.category === assessmentY.category) { - return 0; - } else { - return 1; - } - }) + const overview: IGroundControlAssessmentOverview[] = this.props.assessmentOverviews + .slice() .map(assessmentOverview => { const clone: IGroundControlAssessmentOverview = JSON.parse( JSON.stringify(assessmentOverview) ); clone.prettyCloseAt = getPrettyDate(clone.closeAt); clone.prettyOpenAt = getPrettyDate(clone.openAt); + clone.formattedOpenAt = new Date(Date.parse(clone.openAt)); + clone.formattedCloseAt = new Date(Date.parse(clone.closeAt)); return clone; }); + return sortBy(overview, ['category', 'formattedOpenAt', 'formattedCloseAt']); }; private onGridReady = (params: GridReadyEvent) => { From a0f7a358d050764a3b7723162200cf491d555ad3 Mon Sep 17 00:00:00 2001 From: ScrubWzz Date: Mon, 20 Apr 2020 15:31:36 +0800 Subject: [PATCH 13/14] Changed method name --- src/components/groundControl/GroundControl.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx index 96ed541877..a054424580 100644 --- a/src/components/groundControl/GroundControl.tsx +++ b/src/components/groundControl/GroundControl.tsx @@ -131,12 +131,12 @@ class GroundControl extends React.Component (
@@ -182,7 +182,7 @@ class GroundControl extends React.Component { + private sortByCategoryAndDate = () => { if (!this.props.assessmentOverviews) { return []; } From 648110daa0231b0a238b55675254cc64e90cb8f2 Mon Sep 17 00:00:00 2001 From: scrubwzz Date: Sat, 2 May 2020 15:07:07 +0800 Subject: [PATCH 14/14] Modified how publishing of assessment is done --- package-lock.json | 43 +++---------------- src/actions/__tests__/groundControl.ts | 8 +--- src/actions/groundControl.ts | 3 +- .../groundControl/GroundControl.tsx | 2 +- src/components/groundControl/PublishCell.tsx | 8 ++-- src/sagas/backend.ts | 11 ++--- src/sagas/requests.ts | 3 +- 7 files changed, 18 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index be4216fc15..9d25f38e09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8779,8 +8779,7 @@ "is-arguments": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" }, "is-arrayish": { "version": "0.2.1", @@ -16067,8 +16066,7 @@ "popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "dev": true + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "portfinder": { "version": "1.0.16", @@ -18570,7 +18568,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.0-next.1" @@ -18580,7 +18577,6 @@ "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -18599,7 +18595,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -18609,20 +18604,17 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" }, "is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -18631,7 +18623,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -18639,14 +18630,12 @@ "object-inspect": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" }, "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==" } } }, @@ -20536,26 +20525,6 @@ } } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, "string.prototype.trimend": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", diff --git a/src/actions/__tests__/groundControl.ts b/src/actions/__tests__/groundControl.ts index be02d7d3ab..6129749c74 100644 --- a/src/actions/__tests__/groundControl.ts +++ b/src/actions/__tests__/groundControl.ts @@ -32,14 +32,10 @@ test('deleteAssessment generates correct action object', () => { test('publishAssessment generates correct action object', () => { const id = 54; - const togglePublishTo = false; - const action = publishAssessment(togglePublishTo, id); + const action = publishAssessment(id); expect(action).toEqual({ type: actionTypes.PUBLISH_ASSESSMENT, - payload: { - togglePublishTo, - id - } + payload: id }); }); diff --git a/src/actions/groundControl.ts b/src/actions/groundControl.ts index 87e548ad99..c4d28b293e 100644 --- a/src/actions/groundControl.ts +++ b/src/actions/groundControl.ts @@ -7,8 +7,7 @@ export const changeDateAssessment = (id: number, openAt: string, closeAt: string export const deleteAssessment = (id: number) => action(actionTypes.DELETE_ASSESSMENT, id); -export const publishAssessment = (togglePublishTo: boolean, id: number) => - action(actionTypes.PUBLISH_ASSESSMENT, { id, togglePublishTo }); +export const publishAssessment = (id: number) => action(actionTypes.PUBLISH_ASSESSMENT, id); export const uploadAssessment = (file: File, forceUpdate: boolean) => action(actionTypes.UPLOAD_ASSESSMENT, { file, forceUpdate }); diff --git a/src/components/groundControl/GroundControl.tsx b/src/components/groundControl/GroundControl.tsx index a054424580..8f7342c6cf 100644 --- a/src/components/groundControl/GroundControl.tsx +++ b/src/components/groundControl/GroundControl.tsx @@ -17,7 +17,7 @@ export interface IDispatchProps { handleAssessmentOverviewFetch: () => void; handleDeleteAssessment: (id: number) => void; handleUploadAssessment: (file: File, forceUpdate: boolean) => void; - handlePublishAssessment: (togglePublishTo: boolean, id: number) => void; + handlePublishAssessment: (id: number) => void; handleAssessmentChangeDate: (id: number, openAt: string, closeAt: string) => void; } diff --git a/src/components/groundControl/PublishCell.tsx b/src/components/groundControl/PublishCell.tsx index 0874c81688..d521d59876 100644 --- a/src/components/groundControl/PublishCell.tsx +++ b/src/components/groundControl/PublishCell.tsx @@ -7,7 +7,7 @@ import { controlButton } from '../commons'; interface IPublishCellProps { data: IAssessmentOverview; - handlePublishAssessment: (togglePublishTo: boolean, id: number) => void; + handlePublishAssessment: (id: number) => void; } interface IPublishCellState { @@ -49,7 +49,7 @@ class PublishCell extends React.Component
- {controlButton('Confirm ' + text, IconNames.CONFIRM, this.handleDelete)} + {controlButton('Confirm ' + text, IconNames.CONFIRM, this.handlePublish)} {controlButton('Cancel', IconNames.CROSS, this.handleCloseDialog)}
@@ -60,9 +60,9 @@ class PublishCell extends React.Component private handleCloseDialog = () => this.setState({ dialogOpen: false }); private handleOpenDialog = () => this.setState({ dialogOpen: true }); - private handleDelete = () => { + private handlePublish = () => { const { data } = this.props; - this.props.handlePublishAssessment(!data.isPublished, data.id); + this.props.handlePublishAssessment(data.id); this.handleCloseDialog(); }; } diff --git a/src/sagas/backend.ts b/src/sagas/backend.ts index 84dad922e2..6d886667ed 100644 --- a/src/sagas/backend.ts +++ b/src/sagas/backend.ts @@ -605,9 +605,8 @@ function* backendSaga(): SagaIterator { accessToken: state.session.accessToken, refreshToken: state.session.refreshToken })); - const id = action.payload.id; - const togglePublishTo = action.payload.togglePublishTo; - const resp: Response = yield request.publishAssessment(id, togglePublishTo, tokens); + const id = action.payload; + const resp: Response = yield request.publishAssessment(id, tokens); if (!resp || !resp.ok) { yield request.handleResponseError(resp); @@ -616,11 +615,7 @@ function* backendSaga(): SagaIterator { yield put(actions.fetchAssessmentOverviews()); - if (togglePublishTo) { - yield call(showSuccessMessage, 'Published successfully!', 1000); - } else { - yield call(showSuccessMessage, 'Unpublished successfully!', 1000); - } + yield call(showSuccessMessage, 'Toggled Publish successfully!', 1000); }); yield takeEvery(actionTypes.UPLOAD_ASSESSMENT, function*( diff --git a/src/sagas/requests.ts b/src/sagas/requests.ts index 77b42aceef..ba904ad3b9 100644 --- a/src/sagas/requests.ts +++ b/src/sagas/requests.ts @@ -621,10 +621,9 @@ export async function deleteAssessment(id: number, tokens: Tokens) { return resp; } -export async function publishAssessment(id: number, togglePublishTo: boolean, tokens: Tokens) { +export async function publishAssessment(id: number, tokens: Tokens) { const resp = await request(`assessments/publish/${id}`, 'POST', { accessToken: tokens.accessToken, - body: { togglePublishTo }, noHeaderAccept: true, refreshToken: tokens.refreshToken, shouldAutoLogout: false,