mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
merged with development branch
This commit is contained in:
commit
218e7fba4f
46 changed files with 1017 additions and 1109 deletions
298
package-lock.json
generated
298
package-lock.json
generated
|
@ -2865,9 +2865,9 @@
|
|||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
|
||||
},
|
||||
"bootstrap": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
|
||||
"integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA=="
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
|
||||
"integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
|
@ -6081,11 +6081,6 @@
|
|||
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
|
||||
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
|
||||
},
|
||||
"gud": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
|
||||
"integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
|
||||
},
|
||||
"gzip-size": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
|
||||
|
@ -8035,9 +8030,9 @@
|
|||
}
|
||||
},
|
||||
"jquery": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz",
|
||||
"integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ=="
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
|
||||
"integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.5.2",
|
||||
|
@ -8767,13 +8762,12 @@
|
|||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
|
||||
},
|
||||
"mini-create-react-context": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
|
||||
"integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz",
|
||||
"integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.0",
|
||||
"gud": "^1.0.0",
|
||||
"tiny-warning": "^1.0.2"
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"tiny-warning": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"mini-css-extract-plugin": {
|
||||
|
@ -9140,9 +9134,9 @@
|
|||
"integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ=="
|
||||
},
|
||||
"node-sass": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.0.tgz",
|
||||
"integrity": "sha512-AxqU+DFpk0lEz95sI6jO0hU0Rwyw7BXVEv6o9OItoXLyeygPeaSpiV4rwQb10JiTghHaa0gZeD21sz+OsQluaw==",
|
||||
"version": "4.14.1",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
|
||||
"integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
|
||||
"requires": {
|
||||
"async-foreach": "^0.1.3",
|
||||
"chalk": "^1.1.1",
|
||||
|
@ -9158,7 +9152,7 @@
|
|||
"node-gyp": "^3.8.0",
|
||||
"npmlog": "^4.0.0",
|
||||
"request": "^2.88.0",
|
||||
"sass-graph": "^2.2.4",
|
||||
"sass-graph": "2.2.5",
|
||||
"stdout-stream": "^1.4.0",
|
||||
"true-case-path": "^1.0.2"
|
||||
},
|
||||
|
@ -11595,9 +11589,9 @@
|
|||
}
|
||||
},
|
||||
"react-draggable": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.3.1.tgz",
|
||||
"integrity": "sha512-m8QeV+eIi7LhD5mXoLqDzLbokc6Ncwa0T34fF6uJzWSs4vc4fdZI/XGqHYoEn91T8S6qO+BSXslONh7Jz9VPQQ==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.2.0.tgz",
|
||||
"integrity": "sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.5",
|
||||
"prop-types": "^15.6.0"
|
||||
|
@ -11619,9 +11613,9 @@
|
|||
}
|
||||
},
|
||||
"react-grid-system": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-grid-system/-/react-grid-system-6.3.1.tgz",
|
||||
"integrity": "sha512-TWJcAdICTseZ16ONTt5LhVYLWJPzpFl4U5LSbF4/mZ/pV7fK7W8lr2bvHOnBWLvYgP3QVcLAxdpeoXj4q2QzmA==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-grid-system/-/react-grid-system-6.4.2.tgz",
|
||||
"integrity": "sha512-zQxczLIAUyFfXJOySQlQEptwZj65hxyoEvS+siTupFW76a3AKUhqrhMJ90xKDcSCF9jb9D71zjoPw13Xe1+EKA==",
|
||||
"requires": {
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
|
@ -11672,32 +11666,25 @@
|
|||
}
|
||||
},
|
||||
"react-rnd": {
|
||||
"version": "10.1.9",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.1.9.tgz",
|
||||
"integrity": "sha512-f6rkzEKehGRjKGK7XkdPQoHeUUawZidq3yzWdxeqFluupdbmvuM7Ygnmm6CgF6shAVrvfzxOSl/bejp9pNliUA==",
|
||||
"version": "10.1.10",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.1.10.tgz",
|
||||
"integrity": "sha512-xR+CasLBGXJUJQpds2CHocKp/Wze8/VKOf7KaaVDEy2MFzDJKcxPQ0J4QCAGSIaN20cTDmpfTyCzdTfwVGxN8A==",
|
||||
"requires": {
|
||||
"re-resizable": "6.3.2",
|
||||
"react-draggable": "4.3.1",
|
||||
"react-draggable": "4.2.0",
|
||||
"tslib": "1.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
|
||||
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
|
||||
"integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"hoist-non-react-statics": "^3.1.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"mini-create-react-context": "^0.3.0",
|
||||
"mini-create-react-context": "^0.4.0",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-is": "^16.6.0",
|
||||
|
@ -11706,15 +11693,15 @@
|
|||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz",
|
||||
"integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
|
||||
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"history": "^4.9.0",
|
||||
"loose-envify": "^1.3.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-router": "5.1.2",
|
||||
"react-router": "5.2.0",
|
||||
"tiny-invariant": "^1.0.2",
|
||||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
|
@ -12438,219 +12425,14 @@
|
|||
}
|
||||
},
|
||||
"sass-graph": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
|
||||
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
|
||||
"integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
|
||||
"requires": {
|
||||
"glob": "^7.0.0",
|
||||
"lodash": "^4.0.0",
|
||||
"scss-tokenizer": "^0.2.3",
|
||||
"yargs": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo="
|
||||
},
|
||||
"cliui": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
||||
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wrap-ansi": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
||||
"requires": {
|
||||
"path-exists": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
|
||||
},
|
||||
"invert-kv": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"lcid": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
||||
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
||||
"requires": {
|
||||
"invert-kv": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"parse-json": "^2.2.0",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0",
|
||||
"strip-bom": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"os-locale": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
||||
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
||||
"requires": {
|
||||
"lcid": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
||||
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
|
||||
"requires": {
|
||||
"error-ex": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||
"requires": {
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
||||
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
|
||||
"requires": {
|
||||
"load-json-file": "^1.0.0",
|
||||
"normalize-package-data": "^2.3.2",
|
||||
"path-type": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"read-pkg-up": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
|
||||
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
|
||||
"requires": {
|
||||
"find-up": "^1.0.0",
|
||||
"read-pkg": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
|
||||
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"which-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
|
||||
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8="
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
|
||||
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
|
||||
"requires": {
|
||||
"camelcase": "^3.0.0",
|
||||
"cliui": "^3.2.0",
|
||||
"decamelize": "^1.1.1",
|
||||
"get-caller-file": "^1.0.1",
|
||||
"os-locale": "^1.4.0",
|
||||
"read-pkg-up": "^1.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^1.0.1",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^1.0.2",
|
||||
"which-module": "^1.0.0",
|
||||
"y18n": "^3.2.1",
|
||||
"yargs-parser": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
|
||||
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
|
||||
"requires": {
|
||||
"camelcase": "^3.0.0"
|
||||
}
|
||||
}
|
||||
"yargs": "^13.3.2"
|
||||
}
|
||||
},
|
||||
"sass-loader": {
|
||||
|
@ -14127,9 +13909,9 @@
|
|||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
|
||||
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz",
|
||||
"integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw=="
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.20",
|
||||
|
|
16
package.json
16
package.json
|
@ -7,7 +7,7 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
||||
"@fortawesome/react-fontawesome": "^0.1.9",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"bootstrap": "^4.4.1",
|
||||
"bootstrap": "^4.5.0",
|
||||
"classnames": "^2.2.6",
|
||||
"d3-array": "^2.4.0",
|
||||
"d3-axis": "^1.0.12",
|
||||
|
@ -21,11 +21,11 @@
|
|||
"flux": "^3.1.3",
|
||||
"gaugeJS": "^1.3.7",
|
||||
"handlebars": "^4.7.6",
|
||||
"jquery": "^3.5.0",
|
||||
"jquery": "^3.5.1",
|
||||
"jszip": "^3.4.0",
|
||||
"libcimsvg": "git+https://git.rwth-aachen.de/acs/public/cim/pintura-npm-package.git",
|
||||
"lodash": "^4.17.15",
|
||||
"node-sass": "^4.14.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"rc-slider": "^9.2.4",
|
||||
|
@ -37,17 +37,17 @@
|
|||
"react-dnd-html5-backend": "^10.0.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-fullscreenable": "^2.5.1-0",
|
||||
"react-grid-system": "^6.3.1",
|
||||
"react-grid-system": "^6.4.2",
|
||||
"react-json-view": "^1.19.1",
|
||||
"react-notification-system": "^0.3.0",
|
||||
"react-rnd": "^10.1.9",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-rnd": "^10.1.10",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "^3.4.1",
|
||||
"react-svg-pan-zoom": "^3.8.0",
|
||||
"sass": "^1.26.5",
|
||||
"superagent": "^5.2.2",
|
||||
"typescript": "^3.8.3",
|
||||
"typescript": "^3.9.2",
|
||||
"validator": "^12.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -5,7 +5,7 @@ import createControls from '../../widget/edit-widget/edit-widget-control-creator
|
|||
import EditWidgetTextControl from '../../widget/edit-widget/edit-widget-text-control';
|
||||
import EditWidgetColorControl from '../../widget/edit-widget/edit-widget-color-control';
|
||||
import EditWidgetTimeControl from '../../widget/edit-widget/edit-widget-time-control';
|
||||
import EditImageWidgetControl from '../../widget/edit-widget/edit-widget-image-control';
|
||||
import EditFileWidgetControl from '../../widget/edit-widget/edit-widget-file-control';
|
||||
import EditWidgetSignalControl from '../../widget/edit-widget/edit-widget-signal-control';
|
||||
import EditWidgetSignalsControl from '../../widget/edit-widget/edit-widget-signals-control';
|
||||
import EditWidgetOrientation from '../../widget/edit-widget/edit-widget-orientation';
|
||||
|
@ -28,7 +28,7 @@ describe('edit widget control creator', () => {
|
|||
{ args: { widgetType: 'Value' }, result: { controlNumber: 5, controlTypes: [EditWidgetTextControl, EditWidgetSignalControl, EditWidgetTextSizeControl, EditWidgetCheckboxControl] } },
|
||||
{ args: { widgetType: 'Plot' }, result: { controlNumber: 5, controlTypes: [EditWidgetTimeControl, EditWidgetSignalsControl, EditWidgetTextControl, EditWidgetMinMaxControl] } },
|
||||
{ args: { widgetType: 'Table' }, result: { controlNumber: 2, controlTypes: [EditWidgetCheckboxControl] } },
|
||||
{ args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditImageWidgetControl, EditWidgetAspectControl] } },
|
||||
{ args: { widgetType: 'Image' }, result: { controlNumber: 2, controlTypes: [EditFileWidgetControl, EditWidgetAspectControl] } },
|
||||
{ args: { widgetType: 'Gauge' }, result: { controlNumber: 6, controlTypes: [EditWidgetTextControl, EditWidgetSignalControl, EditWidgetCheckboxControl, EditWidgetColorZonesControl, EditWidgetMinMaxControl] } },
|
||||
{ args: { widgetType: 'PlotTable' }, result: { controlNumber: 5, controlTypes: [EditWidgetSignalsControl, EditWidgetTextControl, EditWidgetTimeControl, EditWidgetMinMaxControl] } },
|
||||
{ args: { widgetType: 'Slider' }, result: { controlNumber: 9, controlTypes: [EditWidgetTextControl, EditWidgetOrientation, EditWidgetSignalControl, EditWidgetCheckboxControl, EditWidgetCheckboxControl, EditWidgetMinMaxControl, EditWidgetNumberControl, EditWidgetNumberControl] } },
|
||||
|
|
|
@ -19,7 +19,6 @@ import request from 'superagent/lib/client';
|
|||
import Promise from 'es6-promise';
|
||||
import NotificationsDataManager from '../data-managers/notifications-data-manager';
|
||||
|
||||
|
||||
// TODO: Add this to a central pool of notifications
|
||||
const SERVER_NOT_REACHABLE_NOTIFICATION = {
|
||||
title: 'Server not reachable',
|
||||
|
@ -55,23 +54,13 @@ class RestAPI {
|
|||
|
||||
if (token != null) {
|
||||
req.set('Authorization', "Bearer " + token);
|
||||
|
||||
}
|
||||
|
||||
req.end(function (error, res) {
|
||||
if (res == null || res.status !== 200) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (res.type ==="application/json"){
|
||||
resolve(JSON.parse(res.text));
|
||||
} else {
|
||||
// if received data is not JSON it is a File
|
||||
//create file name:
|
||||
let parts = url.split("/");
|
||||
console.log("res.text has type: ", typeof res.text);
|
||||
resolve({data: res.text, type: res.type, id: parts[parts.length-1]})
|
||||
}
|
||||
|
||||
resolve(JSON.parse(res.text));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -134,9 +123,9 @@ class RestAPI {
|
|||
});
|
||||
}
|
||||
|
||||
upload(url, data, token, progressCallback, objectType, objectID) {
|
||||
upload(url, data, token, progressCallback, scenarioID) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const req = request.post(url + "?objectType=" + objectType + "&objectID=" + objectID).send(data); //.on('progress', progressCallback);
|
||||
const req = request.post(url + "?scenarioID=" + scenarioID).send(data).on('progress', progressCallback);
|
||||
|
||||
if (token != null) {
|
||||
req.set('Authorization', "Bearer " + token);
|
||||
|
@ -151,6 +140,28 @@ class RestAPI {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
download(url, token, fileID) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let req = request.get(url + "/" + fileID).buffer(true).responseType("blob")
|
||||
// use blob response type and buffer
|
||||
if (token != null) {
|
||||
req.set('Authorization', "Bearer " + token);
|
||||
}
|
||||
|
||||
req.end(function (error, res) {
|
||||
if (error !== null || res.status !== 200) {
|
||||
reject(error);
|
||||
} else {
|
||||
// file data is contained in res.body (because of blob response type)
|
||||
let parts = url.split("/");
|
||||
resolve({data: res.body, type: res.type, id: parts[parts.length-1]})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new RestAPI();
|
||||
|
|
|
@ -99,7 +99,7 @@ class RestDataManager {
|
|||
});
|
||||
|
||||
if (this.onLoad != null) {
|
||||
this.onLoad(data);
|
||||
this.onLoad(data, token);
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
|
@ -121,7 +121,7 @@ class RestDataManager {
|
|||
});
|
||||
|
||||
if (this.onLoad != null) {
|
||||
this.onLoad(data);
|
||||
this.onLoad(data, token);
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
|
@ -133,16 +133,50 @@ class RestDataManager {
|
|||
}
|
||||
|
||||
|
||||
add(object, token = null, param = null) {
|
||||
add(object, token = null, param = null, subObjects = null) {
|
||||
var obj = {};
|
||||
obj[this.type] = this.filterKeys(object);
|
||||
|
||||
RestAPI.post(this.requestURL('load/add',null,param), obj, token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: this.type + 's/added',
|
||||
data: response[this.type],
|
||||
token: token
|
||||
});
|
||||
|
||||
// check if POST is done for import of object and issue dispatches of sub-objects
|
||||
if (subObjects !== null){
|
||||
// there are sub-objects to be added for an import
|
||||
for (let objectType of subObjects){
|
||||
let type = Object.keys(objectType) // type can be dashboards, configs, widgets, ...
|
||||
type = type[0];
|
||||
for (let newObj of objectType[type]){
|
||||
|
||||
// set the ID of the object that the sub-object shall be associated with
|
||||
if(type === "configs" || type === "dashboards"){
|
||||
// the main object is a scenario
|
||||
newObj.scenarioID = response[this.type].id
|
||||
} else if (type === "widgets") {
|
||||
// the main object is a dashboard
|
||||
newObj.dashboardID = response[this.type].id
|
||||
} else if (type === "signals") {
|
||||
// the main object is a component configuration
|
||||
newObj.configID = response[this.type].id
|
||||
}
|
||||
|
||||
// iterate over all objects of type 'type' add issue add dispatch
|
||||
AppDispatcher.dispatch({
|
||||
type: type + '/start-add',
|
||||
data: newObj,
|
||||
token: token
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: this.type + 's/add-error',
|
||||
|
|
|
@ -28,11 +28,37 @@ class ConfigStore extends ArrayStore {
|
|||
switch (action.type) {
|
||||
|
||||
case 'configs/loaded':
|
||||
|
||||
ConfigsDataManager.loadSignals(action.token, action.data);
|
||||
ConfigsDataManager.loadFiles(action.token, action.data);
|
||||
return super.reduce(state, action);
|
||||
|
||||
case 'configs/start-add':
|
||||
// Check if this is a recursive component config import or not
|
||||
if (action.data.hasOwnProperty("outputMapping") || action.data.hasOwnProperty("inputMapping")) {
|
||||
// import
|
||||
let subObjects = []
|
||||
let outputMapping = {}
|
||||
let inputMapping = {}
|
||||
|
||||
if (action.data.hasOwnProperty("outputMapping")){
|
||||
outputMapping["signals"] = action.data.outputMapping
|
||||
subObjects.push(outputMapping)
|
||||
delete action.data.outputMapping; // remove outputMapping signals from config object
|
||||
}
|
||||
if (action.data.hasOwnProperty("inputMapping")){
|
||||
inputMapping["signals"] = action.data.inputMapping
|
||||
subObjects.push(inputMapping)
|
||||
delete action.data.inputMapping; // remove inputMapping signals from config object
|
||||
}
|
||||
|
||||
// action.data should now contain the config and no sub-objects
|
||||
// sub-objects are treated in add method of RestDataManager
|
||||
this.dataManager.add(action.data, action.token,action.param, subObjects);
|
||||
return state
|
||||
|
||||
} else {
|
||||
// no import
|
||||
return super.reduce(state, action);
|
||||
}
|
||||
|
||||
default:
|
||||
return super.reduce(state, action);
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import RestDataManager from '../common/data-managers/rest-data-manager';
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
import RestAPI from "../common/api/rest-api";
|
||||
|
||||
class ConfigDataManager extends RestDataManager {
|
||||
constructor() {
|
||||
|
@ -26,55 +25,35 @@ class ConfigDataManager extends RestDataManager {
|
|||
this.onLoad = this.onConfigsLoad;
|
||||
}
|
||||
|
||||
onConfigsLoad(data) {
|
||||
onConfigsLoad(data, token) {
|
||||
if (!Array.isArray(data))
|
||||
data = [ data ];
|
||||
|
||||
for (let config of data)
|
||||
this.loadICData(config);
|
||||
}
|
||||
for (let config of data) {
|
||||
|
||||
loadICData(config) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'icData/prepare',
|
||||
inputLength: parseInt(config.inputLength, 10),
|
||||
outputLength: parseInt(config.outputLength, 10),
|
||||
id: config.icID
|
||||
});
|
||||
}
|
||||
// prepare IC data
|
||||
AppDispatcher.dispatch({
|
||||
type: 'icData/prepare',
|
||||
inputLength: parseInt(config.inputLength, 10),
|
||||
outputLength: parseInt(config.outputLength, 10),
|
||||
id: config.icID
|
||||
});
|
||||
|
||||
loadSignals(token, configs){
|
||||
|
||||
for (let config of configs) {
|
||||
// request in signals
|
||||
RestAPI.get(this.makeURL('/signals?direction=in&configID=' + config.id), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'signals/loaded',
|
||||
data: response.signals
|
||||
});
|
||||
AppDispatcher.dispatch({
|
||||
type: 'signals/start-load',
|
||||
token: token,
|
||||
param: '?direction=in&configID=' + config.id,
|
||||
});
|
||||
|
||||
// request out signals
|
||||
RestAPI.get(this.makeURL('/signals?direction=out&configID=' + config.id), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'signals/loaded',
|
||||
data: response.signals
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
loadFiles(token, configs){
|
||||
for (let config of configs) {
|
||||
// request files of config
|
||||
RestAPI.get(this.makeURL('/files?objectType=config&objectID=' + config.id), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/loaded',
|
||||
data: response.files
|
||||
});
|
||||
AppDispatcher.dispatch({
|
||||
type: 'signals/start-load',
|
||||
token: token,
|
||||
param: '?direction=out&configID=' + config.id,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ class EditConfigDialog extends React.Component {
|
|||
name: '',
|
||||
icID: '',
|
||||
configuration: null,
|
||||
startParameters: {},
|
||||
selectedFileID:0
|
||||
startParameters: this.props.config.startParameters,
|
||||
selectedFileID: this.props.config.selectedFileID
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class EditConfigDialog extends React.Component {
|
|||
if (this.state.icID !== '' && this.props.config.icID !== parseInt(this.state.icID)) {
|
||||
data.icID = parseInt(this.state.icID, 10);
|
||||
}
|
||||
if(this.state.startParameters !== {} && this.props.config.startParameters !== this.state.startParameters){
|
||||
if(this.state.startParameters !== {} && JSON.stringify(this.props.config.startParameters) !== JSON.stringify(this.state.startParameters)){
|
||||
data.startParameters = this.state.startParameters;
|
||||
}
|
||||
if (parseInt(this.state.selectedFileID, 10) !== 0 &&
|
||||
|
@ -79,9 +79,9 @@ class EditConfigDialog extends React.Component {
|
|||
this.valid = this.isValid()
|
||||
}
|
||||
|
||||
handleSelectedFileChange(newFileID){
|
||||
console.log("Config file change to: ", newFileID);
|
||||
this.setState({selectedFileID: newFileID})
|
||||
handleSelectedFileChange(event){
|
||||
//console.log("Config file change to: ", event.target.value);
|
||||
this.setState({selectedFileID: event.target.value})
|
||||
|
||||
this.valid = this.isValid()
|
||||
}
|
||||
|
@ -121,11 +121,19 @@ class EditConfigDialog extends React.Component {
|
|||
</FormControl>
|
||||
</FormGroup>
|
||||
|
||||
<SelectFile type='config' name='Configuration File' onChange={(e) => this.handleSelectedFileChange(e)} value={this.state.selectedFileID} objectID={this.props.config.id}/>
|
||||
<SelectFile
|
||||
type='config'
|
||||
name='Configuration File'
|
||||
onChange={(e) => this.handleSelectedFileChange(e)}
|
||||
files={this.props.files}
|
||||
value={this.state.selectedFileID}
|
||||
scenarioID={this.props.config.scenarioID}
|
||||
sessionToken={this.props.sessionToken}
|
||||
/>
|
||||
|
||||
<FormGroup controlId='startParameters'>
|
||||
<FormLabel> Start Parameters </FormLabel>
|
||||
<ParametersEditor content={this.props.config.startParameters} onChange={(data) => this.handleParameterChange(data)} />
|
||||
<ParametersEditor content={this.state.startParameters} onChange={(data) => this.handleParameterChange(data)} />
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
|
|
|
@ -17,18 +17,19 @@
|
|||
|
||||
import React from 'react';
|
||||
import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
|
||||
import _ from 'lodash';
|
||||
|
||||
import Dialog from '../common/dialogs/dialog';
|
||||
|
||||
class ImportConfigDialog extends React.Component {
|
||||
imported = false;
|
||||
valid = false;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
config: {}
|
||||
config: {},
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -39,12 +40,13 @@ class ImportConfigDialog extends React.Component {
|
|||
return;
|
||||
}
|
||||
|
||||
this.props.onClose(this.state.config);
|
||||
this.props.onClose(this.state);
|
||||
}
|
||||
|
||||
resetState = () => {
|
||||
this.setState({
|
||||
config: {}
|
||||
config: {},
|
||||
name: ''
|
||||
});
|
||||
|
||||
this.imported = false;
|
||||
|
@ -58,46 +60,65 @@ class ImportConfigDialog extends React.Component {
|
|||
}
|
||||
|
||||
// create file reader
|
||||
const reader = new FileReader();
|
||||
const self = this;
|
||||
let reader = new FileReader();
|
||||
let self = this;
|
||||
|
||||
reader.onload = event => {
|
||||
const config = JSON.parse(event.target.result);
|
||||
|
||||
config.icID = this.props.ics.length > 0 ? this.props.ics[0]._id : null;
|
||||
|
||||
self.imported = true;
|
||||
|
||||
this.setState({ config: config });
|
||||
self.valid = true;
|
||||
this.setState({name: config.name, config: config });
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
handleICChange = event => {
|
||||
const config = this.state.config;
|
||||
handleChange(e, index) {
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
}
|
||||
|
||||
config.icID = event.target.value;
|
||||
validateForm(target) {
|
||||
// check all controls
|
||||
let name = true;
|
||||
|
||||
this.setState({ config: config });
|
||||
if (this.state.name === '') {
|
||||
name = false;
|
||||
}
|
||||
this.valid = name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name'){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Component Configuration" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={this.resetState} valid={this.imported}>
|
||||
<Dialog
|
||||
show={this.props.show}
|
||||
title="Import Component Configuration"
|
||||
buttonTitle="Import"
|
||||
onClose={(c) => this.onClose(c)}
|
||||
onReset={() => this.resetState()}
|
||||
valid={this.valid} >
|
||||
<form>
|
||||
<FormGroup controlId='file'>
|
||||
<FormLabel>Component Configuration File</FormLabel>
|
||||
<FormControl type='file' onChange={this.loadFile} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId='IC'>
|
||||
<FormLabel>Infrastructure Component</FormLabel>
|
||||
<FormControl disabled={this.imported === false} as='select' placeholder='Select infrastructure component' value={this.state.config.icID} onChange={this.handleICChange}>
|
||||
{this.props.ics.map(ic => (
|
||||
<option key={ic.id} value={ic.id}>{_.get(ic, 'properties.name') || _.get(ic, 'rawProperties.name')}</option>
|
||||
))}
|
||||
</FormControl>
|
||||
<FormGroup controlId="name" >
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl
|
||||
readOnly={!this.imported}
|
||||
isValid={this.validateForm('name')}
|
||||
type="text"
|
||||
placeholder="Enter name"
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
/>
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
</form>
|
||||
</Dialog>
|
||||
|
|
|
@ -18,4 +18,40 @@
|
|||
import ArrayStore from '../common/array-store';
|
||||
import DashboardsDataManager from './dashboards-data-manager';
|
||||
|
||||
export default new ArrayStore('dashboards', DashboardsDataManager);
|
||||
class DashboardStore extends ArrayStore {
|
||||
constructor() {
|
||||
super('dashboards', DashboardsDataManager);
|
||||
}
|
||||
|
||||
reduce(state, action) {
|
||||
|
||||
switch (action.type) {
|
||||
case 'dashboards/start-add':
|
||||
|
||||
// Check if this is a recursive dashboard import or not
|
||||
if (action.data.hasOwnProperty("widgets")) {
|
||||
// import
|
||||
let subObjects = []
|
||||
let widgets = {}
|
||||
widgets["widgets"] = action.data.widgets
|
||||
subObjects.push(widgets)
|
||||
delete action.data.widgets; // remove widgets from dashboard object
|
||||
|
||||
// action.data should now contain the dashboard and no sub-objects
|
||||
// sub-objects are treated in add method of RestDataManager
|
||||
this.dataManager.add(action.data, action.token,action.param, subObjects);
|
||||
return state
|
||||
|
||||
} else {
|
||||
// no import
|
||||
return super.reduce(state, action);
|
||||
}
|
||||
|
||||
default:
|
||||
return super.reduce(state, action);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new DashboardStore();
|
||||
|
|
|
@ -32,6 +32,8 @@ import DashboardStore from './dashboard-store';
|
|||
import SignalStore from '../signal/signal-store'
|
||||
import FileStore from '../file/file-store';
|
||||
import WidgetStore from '../widget/widget-store';
|
||||
import ICStore from '../ic/ic-store'
|
||||
import ConfigStore from '../componentconfig/config-store'
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
|
||||
import 'react-contexify/dist/ReactContexify.min.css';
|
||||
|
@ -40,7 +42,7 @@ class Dashboard extends Component {
|
|||
|
||||
static lastWidgetKey = 0;
|
||||
static getStores() {
|
||||
return [ DashboardStore, FileStore, LoginStore, WidgetStore, SignalStore ];
|
||||
return [ DashboardStore, LoginStore,FileStore, WidgetStore, SignalStore, ConfigStore, ICStore];
|
||||
}
|
||||
|
||||
static calculateState(prevState, props) {
|
||||
|
@ -59,7 +61,7 @@ class Dashboard extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
// obtain all widgets of a dashboard
|
||||
// obtain all widgets of this dashboard
|
||||
let widgets = WidgetStore.getState().filter(w => w.dashboardID === parseInt(props.match.params.dashboard, 10));
|
||||
|
||||
// compute max y coordinate
|
||||
|
@ -71,29 +73,49 @@ class Dashboard extends Component {
|
|||
return thisWidgetHeight > maxHeightSoFar? thisWidgetHeight : maxHeightSoFar;
|
||||
}, 0);
|
||||
|
||||
// TODO filter signals to the ones belonging to the scenario at hand!
|
||||
let signals = SignalStore.getState();
|
||||
// filter component configurations to the ones that belong to this scenario
|
||||
let configs = []
|
||||
let files = []
|
||||
if (dashboard !== null) {
|
||||
configs = ConfigStore.getState().filter(config => config.scenarioID === dashboard.scenarioID);
|
||||
files = FileStore.getState().filter(file => file.scenarioID === dashboard.scenarioID);
|
||||
}
|
||||
|
||||
// get files of all widgets
|
||||
let allFiles = FileStore.getState();
|
||||
let files = [];
|
||||
let file, widget;
|
||||
for (file of allFiles){
|
||||
for (widget of widgets){
|
||||
if (file.widgetID === widget.id){
|
||||
files.push(file);
|
||||
// filter signals to the ones belonging to the scenario at hand
|
||||
let signals = []
|
||||
let allSignals = SignalStore.getState();
|
||||
let sig, con;
|
||||
for (sig of allSignals){
|
||||
for (con of configs){
|
||||
if (sig.configID === con.id){
|
||||
signals.push(sig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO create list of infrastructure components in use
|
||||
// filter ICs to the ones used by this scenario
|
||||
let ics = []
|
||||
if (configs.length > 0){
|
||||
ics = ICStore.getState().filter(ic => {
|
||||
let ICused = false;
|
||||
for (let config of configs){
|
||||
if (ic.id === config.icID){
|
||||
ICused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ICused;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
dashboard,
|
||||
widgets,
|
||||
signals,
|
||||
sessionToken: sessionToken,
|
||||
files: files,
|
||||
sessionToken,
|
||||
files,
|
||||
configs,
|
||||
ics,
|
||||
|
||||
editing: prevState.editing || false,
|
||||
paused: prevState.paused || false,
|
||||
|
@ -117,7 +139,6 @@ class Dashboard extends Component {
|
|||
return widgetKey;
|
||||
}
|
||||
|
||||
//!!!won't work anymore
|
||||
componentDidMount() {
|
||||
|
||||
// load widgets of dashboard
|
||||
|
@ -127,9 +148,25 @@ class Dashboard extends Component {
|
|||
param: '?dashboardID=' + this.state.dashboard.id
|
||||
});
|
||||
|
||||
// TODO open websockets in componentDidMount
|
||||
// open web sockets if ICs are already known
|
||||
if(this.state.ics.length > 0){
|
||||
console.log("Starting to open IC websockets:", this.state.ics);
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/open-sockets',
|
||||
data: this.state.ics
|
||||
});
|
||||
} else {
|
||||
console.log("ICs unknown in componentDidMount", this.state.dashboard)
|
||||
}
|
||||
|
||||
// TODO close websockets in componentWillUnmount
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// close web sockets of ICs
|
||||
console.log("Starting to close all web sockets");
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/close-sockets',
|
||||
});
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
|
@ -225,6 +262,15 @@ class Dashboard extends Component {
|
|||
this.setState({ editModal: true, modalData: widget, modalIndex: index });
|
||||
};
|
||||
|
||||
uploadFile(data,widget){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-upload',
|
||||
data: data,
|
||||
token: this.state.sessionToken,
|
||||
scenarioID: this.state.dashboard.scenarioID,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
closeEdit(data){
|
||||
|
||||
|
@ -262,6 +308,15 @@ class Dashboard extends Component {
|
|||
|
||||
|
||||
startEditing(){
|
||||
this.state.widgets.forEach( widget => {
|
||||
if(widget.type === 'Slider' || widget.type === 'NumberInput' || widget.type === 'Button'){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-edit',
|
||||
token: this.state.sessionToken,
|
||||
data: widget
|
||||
});
|
||||
}
|
||||
});
|
||||
this.setState({ editing: true });
|
||||
};
|
||||
|
||||
|
@ -307,14 +362,14 @@ class Dashboard extends Component {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
temp.forEach( widget => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-remove',
|
||||
data: widget,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
});
|
||||
});
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-load',
|
||||
token: this.state.sessionToken,
|
||||
|
@ -412,6 +467,7 @@ class Dashboard extends Component {
|
|||
sessionToken={this.state.sessionToken}
|
||||
show={this.state.editModal}
|
||||
onClose={this.closeEdit.bind(this)}
|
||||
onUpload = {this.uploadFile.bind(this)}
|
||||
widget={this.state.modalData}
|
||||
signals={this.state.signals}
|
||||
files={this.state.files}
|
||||
|
|
|
@ -16,5 +16,29 @@
|
|||
******************************************************************************/
|
||||
|
||||
import RestDataManager from '../common/data-managers/rest-data-manager';
|
||||
import AppDispatcher from "../common/app-dispatcher";
|
||||
|
||||
export default new RestDataManager('dashboard', '/dashboards');
|
||||
class DashboardsDataManager extends RestDataManager{
|
||||
constructor() {
|
||||
super('dashboard', '/dashboards');
|
||||
this.onLoad = this.onDashboardsLoad
|
||||
}
|
||||
|
||||
onDashboardsLoad(data, token){
|
||||
|
||||
if (!Array.isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
|
||||
for (let dashboard of data){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-load',
|
||||
token: token,
|
||||
param: '?dashboardID=' + dashboard.id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new DashboardsDataManager();
|
||||
|
|
|
@ -67,27 +67,6 @@ class ImportDashboardDialog extends React.Component {
|
|||
// read IC
|
||||
const dashboard = JSON.parse(event.target.result);
|
||||
|
||||
/*let defaultIC = "";
|
||||
if (self.props.configs != null) {
|
||||
defaultIC = self.props.configs[0].icID;
|
||||
}
|
||||
|
||||
dashboard.widgets.forEach(widget => {
|
||||
switch (widget.type) {
|
||||
case 'Value':
|
||||
case 'Plot':
|
||||
case 'Table':
|
||||
case 'PlotTable':
|
||||
case 'Gauge':
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
self.imported = true;
|
||||
self.valid = true;
|
||||
self.setState({ name: dashboard.name, widgets: dashboard.widgets, grid: dashboard.grid });
|
||||
|
@ -107,21 +86,36 @@ class ImportDashboardDialog extends React.Component {
|
|||
this.valid = name;
|
||||
|
||||
// return state to control
|
||||
if (target === 'name') return name ? "success" : "error";
|
||||
if (target === 'name'){
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dialog show={this.props.show} title="Import Dashboard" buttonTitle="Import" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<Dialog
|
||||
show={this.props.show}
|
||||
title="Import Dashboard"
|
||||
buttonTitle="Import"
|
||||
onClose={(c) => this.onClose(c)}
|
||||
onReset={() => this.resetState()}
|
||||
valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="file">
|
||||
<FormLabel>Dashboard File</FormLabel>
|
||||
<FormControl type="file" onChange={(e) => this.loadFile(e.target.files)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<FormGroup controlId="name" >
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl readOnly={!this.imported} type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl
|
||||
readOnly={!this.imported}
|
||||
isValid={this.validateForm('name')}
|
||||
type="text"
|
||||
placeholder="Enter name"
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
/>
|
||||
<FormControl.Feedback />
|
||||
</FormGroup>
|
||||
</form>
|
||||
|
|
|
@ -69,7 +69,7 @@ class NewDashboardDialog extends React.Component {
|
|||
return (
|
||||
<Dialog show={this.props.show} title="New Dashboard" buttonTitle="Add" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={this.valid}>
|
||||
<form>
|
||||
<FormGroup controlId="name" validationState={this.validateForm('name')}>
|
||||
<FormGroup controlId="name" validationstate={this.validateForm('name')}>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl type="text" placeholder="Enter name" value={this.state.name} onChange={(e) => this.handleChange(e)} />
|
||||
<FormControl.Feedback />
|
||||
|
|
|
@ -25,23 +25,36 @@ class FileStore extends ArrayStore {
|
|||
|
||||
saveFile(state, action){
|
||||
|
||||
// save file data file
|
||||
let fileID = parseInt(action.data.id)
|
||||
console.log("Received file", action);
|
||||
for (let f of state){
|
||||
if (f.id === fileID){
|
||||
f["data"] = action.data.data;
|
||||
f.type = action.data.type;
|
||||
console.log("Saving file data to file id", fileID);
|
||||
}
|
||||
}
|
||||
let fileID = parseInt(action.id)
|
||||
state.forEach((element, index, array) => {
|
||||
if (element.id === fileID) {
|
||||
// save blob object
|
||||
array[index]["data"] = new Blob([action.data.data], {type: action.data.type});
|
||||
// update file type
|
||||
array[index]["type"] = action.data.type;
|
||||
|
||||
if (array[index]["objectURL"] !== ''){
|
||||
// free memory of previously generated object URL
|
||||
URL.revokeObjectURL(array[index]["objectURL"]);
|
||||
}
|
||||
// create an object URL for the file
|
||||
array[index]["objectURL"] = URL.createObjectURL(array[index]["data"])
|
||||
}
|
||||
});
|
||||
|
||||
// announce change to listeners
|
||||
this.__emitChange();
|
||||
return state
|
||||
}
|
||||
|
||||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
case 'files/start-download':
|
||||
FilesDataManager.download(action)
|
||||
return state
|
||||
|
||||
case 'files/start-upload':
|
||||
FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.objectType, action.objectID);
|
||||
FilesDataManager.upload(action.data, action.token, action.progressCallback, action.finishedCallback, action.scenarioID);
|
||||
return state;
|
||||
|
||||
case 'files/uploaded':
|
||||
|
@ -51,15 +64,10 @@ class FileStore extends ArrayStore {
|
|||
case 'files/upload-error':
|
||||
console.log(action.error);
|
||||
return state;
|
||||
case 'files/loaded':
|
||||
if (Array.isArray(action.data)) {
|
||||
return super.reduce(state, action)
|
||||
} else {
|
||||
// in this case a file is contained in the response (no JSON)
|
||||
// TODO we have to extract and process the file here (=save it somewhere?)
|
||||
this.saveFile(state, action)
|
||||
return super.reduce(state, action)
|
||||
}
|
||||
|
||||
case 'files/downloaded':
|
||||
// in this case a file is contained in the response (no JSON)
|
||||
return this.saveFile(state, action)
|
||||
|
||||
default:
|
||||
return super.reduce(state, action);
|
||||
|
|
|
@ -24,8 +24,8 @@ class FilesDataManager extends RestDataManager {
|
|||
super('file', '/files');
|
||||
}
|
||||
|
||||
upload(file, token = null, progressCallback = null, finishedCallback = null, objectType, objectID) {
|
||||
RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, objectType, objectID).then(response => {
|
||||
upload(file, token = null, progressCallback = null, finishedCallback = null, scenarioID) {
|
||||
RestAPI.upload(this.makeURL(this.url), file, token, progressCallback, scenarioID).then(response => {
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/uploaded',
|
||||
|
@ -34,13 +34,13 @@ class FilesDataManager extends RestDataManager {
|
|||
// Trigger a files reload
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
param: '?objectType=' + objectType + '&objectID=' + objectID,
|
||||
param: '?scenarioID=' + scenarioID,
|
||||
token: token
|
||||
});
|
||||
|
||||
/*if (finishedCallback) {
|
||||
finishedCallback();
|
||||
}*/
|
||||
if (finishedCallback) {
|
||||
finishedCallback(response.file.id);
|
||||
}
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/upload-error',
|
||||
|
@ -48,6 +48,23 @@ class FilesDataManager extends RestDataManager {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
download(action){
|
||||
RestAPI.download(this.makeURL(this.url), action.token, action.data).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/downloaded',
|
||||
data: response,
|
||||
id: action.data,
|
||||
token: action.token
|
||||
});
|
||||
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/load-error',
|
||||
error: error
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new FilesDataManager();
|
||||
|
|
|
@ -16,58 +16,20 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { Container } from 'flux/utils';
|
||||
import { FormGroup, FormControl, FormLabel, Button, Col } from 'react-bootstrap';
|
||||
|
||||
import FileStore from './file-store';
|
||||
import LoginStore from '../user/login-store';
|
||||
|
||||
import { FormGroup, FormControl, FormLabel, Button, Col, ProgressBar } from 'react-bootstrap';
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
import Icon from "../common/icon";
|
||||
|
||||
class SelectFile extends React.Component {
|
||||
static getStores() {
|
||||
return [ FileStore, LoginStore ];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
uploadFile: null,
|
||||
uploadProgress: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static calculateState(prevState, props) {
|
||||
|
||||
let files = FileStore.getState().filter((file) => {
|
||||
return (file.configID === props.objectID)
|
||||
});
|
||||
|
||||
console.log("props.objectID=", props.objectID)
|
||||
|
||||
return {
|
||||
files: files,
|
||||
sessionToken: LoginStore.getState().token,
|
||||
selectedFile: '',
|
||||
uploadFile: null,
|
||||
uploadProgress: 0
|
||||
};
|
||||
}
|
||||
|
||||
/*componentDidMount() {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
}*/
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
|
||||
// send file ID to callback
|
||||
if (this.props.onChange != null) {
|
||||
this.props.onChange(event.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
selectUploadFile(event) {
|
||||
this.setState({ uploadFile: event.target.files[0] });
|
||||
};
|
||||
|
@ -80,40 +42,50 @@ class SelectFile extends React.Component {
|
|||
AppDispatcher.dispatch({
|
||||
type: 'files/start-upload',
|
||||
data: formData,
|
||||
token: this.state.sessionToken,
|
||||
//progressCallback: this.updateUploadProgress,
|
||||
//finishedCallback: this.clearProgress,
|
||||
objectType: this.props.type,
|
||||
objectID: this.props.objectID,
|
||||
token: this.props.sessionToken,
|
||||
progressCallback: this.updateUploadProgress,
|
||||
finishedCallback: this.clearProgress,
|
||||
scenarioID: this.props.scenarioID,
|
||||
});
|
||||
|
||||
// TODO make sure that edit config dialog remains open after clicking "Upload" button
|
||||
};
|
||||
|
||||
updateUploadProgress = (event) => {// TODO: this callback does not work properly (access to setState)
|
||||
updateUploadProgress = (event) => {
|
||||
this.setState({ uploadProgress: parseInt(event.percent.toFixed(), 10) });
|
||||
};
|
||||
|
||||
clearProgress = () => { // TODO this callback does not work properly (access to setState)
|
||||
if (this.props.onChange != null) {
|
||||
this.props.onChange(this.state.files[this.state.files.length - 1].id);
|
||||
clearProgress = (newFileID) => {
|
||||
/*if (this.props.onChange != null) {
|
||||
let event = {}
|
||||
event["target"] = {}
|
||||
event.target["value"] = newFileID
|
||||
this.props.onChange(event);
|
||||
}
|
||||
this.setState({ uploadProgress: 0 });
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
let fileOptions;
|
||||
if (this.state.files.length > 0){
|
||||
fileOptions = this.state.files.map(f =>
|
||||
let fileOptions = [];
|
||||
if (this.props.files.length > 0){
|
||||
fileOptions.push(
|
||||
<option key = {0} value={-1}>Select file</option>
|
||||
)
|
||||
|
||||
fileOptions.push(this.props.files.map(f =>
|
||||
<option key={f.id} value={f.id}>{f.name}</option>
|
||||
);
|
||||
));
|
||||
} else {
|
||||
fileOptions = <option >No files for this component config</option>
|
||||
fileOptions = <option >No files available</option>
|
||||
}
|
||||
|
||||
/*const progressBarStyle = {
|
||||
const progressBarStyle = {
|
||||
marginLeft: '100px',
|
||||
marginTop: '-25px'
|
||||
};*/
|
||||
marginTop: '-40px'
|
||||
};
|
||||
|
||||
return <div>
|
||||
<FormGroup>
|
||||
|
@ -122,29 +94,44 @@ class SelectFile extends React.Component {
|
|||
</FormLabel>
|
||||
|
||||
<FormGroup as={Col} sm={9} md={10}>
|
||||
<FormControl as="select" disabled={this.props.disabled} placeholder='Select file' onChange={(event) => this.handleChange(event)}>
|
||||
<FormControl
|
||||
as="select"
|
||||
value={this.props.value}
|
||||
disabled={this.props.disabled}
|
||||
placeholder='Select file'
|
||||
onChange={(event) => this.props.onChange(event)}>
|
||||
{fileOptions}
|
||||
</FormControl>
|
||||
</FormGroup>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset: 2}} >
|
||||
<FormControl disabled={this.props.disabled} type='file' onChange={(event) => this.selectUploadFile(event)} />
|
||||
<FormGroup as={Col} >
|
||||
<FormControl
|
||||
disabled={this.props.disabled}
|
||||
type='file'
|
||||
onChange={(event) => this.selectUploadFile(event)} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset : 2}}>
|
||||
<Button disabled={this.props.disabled} onClick={() => this.startFileUpload()}>
|
||||
<Icon icon="plus" /> File
|
||||
<FormGroup as={Col} >
|
||||
<Button
|
||||
disabled={this.state.uploadFile === null}
|
||||
onClick={() => this.startFileUpload()}>
|
||||
Upload
|
||||
</Button>
|
||||
</FormGroup>
|
||||
{/*<FormGroup as={Col} sm={{span: 9, offset: 3}} md={{span: 10, offset: 2}}>
|
||||
<ProgressBar striped animated now={this.state.uploadProgress} label={this.state.uploadProgress + '%'}
|
||||
style={progressBarStyle}/>
|
||||
|
||||
<FormGroup as={Col} >
|
||||
<ProgressBar
|
||||
striped={true}
|
||||
animated={true}
|
||||
now={this.state.uploadProgress}
|
||||
label={this.state.uploadProgress + '%'}
|
||||
style={progressBarStyle}
|
||||
/>
|
||||
</FormGroup>
|
||||
*/}
|
||||
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
let fluxContainerConverter = require('../common/FluxContainerConverter');
|
||||
export default Container.create(fluxContainerConverter.convert(SelectFile), { withProps: true });
|
||||
export default SelectFile;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 192 KiB |
|
@ -52,15 +52,6 @@ class ImportScenarioDialog extends React.Component {
|
|||
}
|
||||
|
||||
handleChange(e, index) {
|
||||
/*if (e.target.id === 'icID') {
|
||||
const configs = this.state.configs;
|
||||
configs[index].icID = JSON.parse(e.target.value);
|
||||
|
||||
this.setState({ configs: configs });
|
||||
|
||||
return;
|
||||
}*/
|
||||
|
||||
this.setState({ [e.target.id]: e.target.value });
|
||||
|
||||
// check all controls
|
||||
|
|
|
@ -19,14 +19,57 @@
|
|||
import ScenariosDataManager from './scenarios-data-manager';
|
||||
import ArrayStore from '../common/array-store';
|
||||
|
||||
class ScenarioStore extends ArrayStore {
|
||||
constructor() {
|
||||
super('scenarios', ScenariosDataManager);
|
||||
}
|
||||
|
||||
getUsers(token, id) {
|
||||
ScenariosDataManager.getUsers(token, id);
|
||||
class ScenarioStore extends ArrayStore{
|
||||
constructor() {
|
||||
super('scenarios', ScenariosDataManager);
|
||||
}
|
||||
|
||||
getUsers(token, id) {
|
||||
ScenariosDataManager.getUsers(token, id);
|
||||
}
|
||||
|
||||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
|
||||
case 'scenarios/start-add':
|
||||
|
||||
// Check if this is a recursive scenario import or not
|
||||
if (action.data.hasOwnProperty("configs") || action.data.hasOwnProperty("dashboards")) {
|
||||
// import
|
||||
let subObjects = []
|
||||
let configs = {}
|
||||
let dashboards = {}
|
||||
|
||||
if (action.data.hasOwnProperty("configs")){
|
||||
configs["configs"] = action.data.configs
|
||||
subObjects.push(configs)
|
||||
delete action.data.configs; // remove configs from scenario object
|
||||
}
|
||||
if (action.data.hasOwnProperty("dashboards")){
|
||||
dashboards["dashboards"] = action.data.dashboards
|
||||
subObjects.push(dashboards)
|
||||
delete action.data.dashboards; // remove dashboards from scenario object
|
||||
}
|
||||
|
||||
// action.data should now contain the scenario and no sub-objects
|
||||
// sub-objects are treated in add method of RestDataManager
|
||||
this.dataManager.add(action.data, action.token,action.param, subObjects);
|
||||
return state
|
||||
|
||||
} else {
|
||||
// no import
|
||||
return super.reduce(state, action);
|
||||
}
|
||||
|
||||
// case 'scenarios/users/start-load':
|
||||
|
||||
default:
|
||||
return super.reduce(state, action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default new ScenarioStore();
|
||||
export default new ScenarioStore();
|
||||
|
|
|
@ -70,20 +70,11 @@ class Scenario extends React.Component {
|
|||
// obtain all component configurations of a scenario
|
||||
let configs = ConfigStore.getState().filter(config => config.scenarioID === parseInt(props.match.params.scenario, 10));
|
||||
|
||||
let signals = SignalStore.getState();
|
||||
let files = FileStore.getState();
|
||||
// obtain all files of a scenario
|
||||
let files = FileStore.getState().filter(file => file.scenarioID === parseInt(props.match.params.scenario, 10));
|
||||
|
||||
let signals = SignalStore.getState();
|
||||
|
||||
// apply filter to contain only ICs that are used by configs
|
||||
let icsUsed = ICStore.getState().filter(ic => {
|
||||
let ICused = false;
|
||||
for (let config of configs){
|
||||
if (ic.id === config.icID){
|
||||
ICused = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ICused;
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
|
@ -95,7 +86,6 @@ class Scenario extends React.Component {
|
|||
signals,
|
||||
files,
|
||||
ics: ICStore.getState(),
|
||||
icsUsed,
|
||||
|
||||
deleteConfigModal: false,
|
||||
importConfigModal: false,
|
||||
|
@ -124,20 +114,6 @@ class Scenario extends React.Component {
|
|||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
// load component configurations for selected scenario
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?scenarioID='+this.state.scenario.id
|
||||
});
|
||||
|
||||
// load dashboards of selected scenario
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?scenarioID='+this.state.scenario.id
|
||||
});
|
||||
|
||||
// load ICs to enable that component configs and dashboards work with them
|
||||
AppDispatcher.dispatch({
|
||||
type: 'ics/start-load',
|
||||
|
@ -145,39 +121,6 @@ class Scenario extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// if (this.state.users) {
|
||||
// this.getUsers = false;
|
||||
// }
|
||||
if (this.state.dashboards.length > prevState.dashboards.length) {
|
||||
if (this.addWidgets) { // add widgets
|
||||
// this can only be true after dashboard import, so there is only one dashboard
|
||||
// (the newest) and this dashboards ID is used
|
||||
let dashboardID = this.state.dashboards[this.state.dashboards.length - 1].id;
|
||||
this.widgetsToAdd.forEach((widget) => {
|
||||
widget.dashboardID = dashboardID;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-add',
|
||||
data: widget,
|
||||
token: this.state.sessionToken,
|
||||
})
|
||||
})
|
||||
this.addWidgets = false;
|
||||
this.widgetsToAdd = [];
|
||||
}
|
||||
else { // get widgets
|
||||
let dashboards = Object.assign([], this.state.dashboards);
|
||||
for (var i = prevState.dashboards.length; i < this.state.dashboards.length; i++) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?dashboardID=' + dashboards[i].id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ##############################################
|
||||
* Component Configuration modification methods
|
||||
|
@ -197,13 +140,6 @@ class Scenario extends React.Component {
|
|||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
this.setState({ scenario: {} }, () => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/start-load',
|
||||
data: this.props.match.params.scenario,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
closeEditConfigModal(data){
|
||||
|
@ -232,33 +168,47 @@ class Scenario extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
importConfig(config){
|
||||
importConfig(data){
|
||||
this.setState({ importConfigModal: false });
|
||||
|
||||
if (config == null) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.scenario = this.state.scenario.id;
|
||||
let newConfig = JSON.parse(JSON.stringify(data.config))
|
||||
|
||||
newConfig["scenarioID"] = this.state.scenario.id;
|
||||
newConfig.name = data.name;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-add',
|
||||
data: config,
|
||||
data: newConfig,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
|
||||
this.setState({ scenario: {} }, () => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/start-load',
|
||||
data: this.props.match.params.scenario,
|
||||
token: this.state.sessionToken
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exportConfig(index) {
|
||||
// filter properties
|
||||
const config = Object.assign({}, this.state.configs[index]);
|
||||
let config = JSON.parse(JSON.stringify(this.state.configs[index]));
|
||||
|
||||
let signals = JSON.parse(JSON.stringify(SignalStore.getState().filter(s => s.configID === parseInt(config.id, 10))));
|
||||
signals.forEach((signal) => {
|
||||
delete signal.configID;
|
||||
delete signal.id;
|
||||
})
|
||||
|
||||
// two separate lists for inputMapping and outputMapping
|
||||
let inputSignals = signals.filter(s => s.direction === 'in');
|
||||
let outputSignals = signals.filter(s => s.direction === 'out');
|
||||
|
||||
// add signal mappings to config
|
||||
config["inputMapping"] = inputSignals;
|
||||
config["outputMapping"] = outputSignals;
|
||||
|
||||
delete config.id;
|
||||
delete config.scenarioID;
|
||||
delete config.inputLength;
|
||||
delete config.outputLength;
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' });
|
||||
|
@ -331,12 +281,12 @@ class Scenario extends React.Component {
|
|||
|
||||
closeNewDashboardModal(data) {
|
||||
this.setState({ newDashboardModal : false });
|
||||
let newDashboard = data;
|
||||
// add default grid value and scenarioID
|
||||
newDashboard["grid"] = 15;
|
||||
newDashboard["scenarioID"] = this.state.scenario.id;
|
||||
|
||||
if (data) {
|
||||
let newDashboard = data;
|
||||
// add default grid value and scenarioID
|
||||
newDashboard["grid"] = 15;
|
||||
newDashboard["scenarioID"] = this.state.scenario.id;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-add',
|
||||
data,
|
||||
|
@ -365,12 +315,7 @@ class Scenario extends React.Component {
|
|||
if (data) {
|
||||
let newDashboard = JSON.parse(JSON.stringify(data));
|
||||
newDashboard["scenarioID"] = this.state.scenario.id;
|
||||
// temporarily store widget data until dashboard is created
|
||||
if (data.widgets) {
|
||||
this.addWidgets = true;
|
||||
this.widgetsToAdd = data.widgets;
|
||||
}
|
||||
delete newDashboard.widgets;
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-add',
|
||||
data: newDashboard,
|
||||
|
@ -381,16 +326,19 @@ class Scenario extends React.Component {
|
|||
|
||||
exportDashboard(index) {
|
||||
// filter properties
|
||||
const dashboard = Object.assign({}, this.state.dashboards[index]);
|
||||
let dashboard = JSON.parse(JSON.stringify(this.state.dashboards[index]));
|
||||
|
||||
let widgets = WidgetStore.getState().filter(w => w.dashboardID === parseInt(dashboard.id, 10));
|
||||
let widgets = JSON.parse(JSON.stringify(WidgetStore.getState().filter(w => w.dashboardID === parseInt(dashboard.id, 10))));
|
||||
widgets.forEach((widget) => {
|
||||
delete widget.dashboardID;
|
||||
delete widget.id;
|
||||
})
|
||||
dashboard["widgets"] = widgets;
|
||||
delete dashboard.scenarioID;
|
||||
delete dashboard.id;
|
||||
|
||||
|
||||
var jsonObj = dashboard;
|
||||
jsonObj["widgets"] = widgets;
|
||||
|
||||
// show save dialog
|
||||
const blob = new Blob([JSON.stringify(jsonObj, null, 2)], { type: 'application/json' });
|
||||
const blob = new Blob([JSON.stringify(dashboard, null, 2)], { type: 'application/json' });
|
||||
FileSaver.saveAs(blob, 'dashboard - ' + dashboard.name + '.json');
|
||||
}
|
||||
|
||||
|
@ -532,7 +480,15 @@ class Scenario extends React.Component {
|
|||
|
||||
<div style={{ clear: 'both' }} />
|
||||
|
||||
<EditConfigDialog show={this.state.editConfigModal} onClose={data => this.closeEditConfigModal(data)} config={this.state.modalConfigData} ics={this.state.ics} />
|
||||
<EditConfigDialog
|
||||
show={this.state.editConfigModal}
|
||||
onClose={data => this.closeEditConfigModal(data)}
|
||||
config={this.state.modalConfigData}
|
||||
ics={this.state.ics}
|
||||
files={this.state.files}
|
||||
sessionToken={this.state.sessionToken}
|
||||
/>
|
||||
|
||||
<ImportConfigDialog show={this.state.importConfigModal} onClose={data => this.importConfig(data)} ics={this.state.ics} />
|
||||
<DeleteDialog title="component configuration" name={this.state.modalConfigData.name} show={this.state.deleteConfigModal} onClose={(c) => this.closeDeleteConfigModal(c)} />
|
||||
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
******************************************************************************/
|
||||
|
||||
import RestDataManager from '../common/data-managers/rest-data-manager';
|
||||
import RestAPI from "../common/api/rest-api";
|
||||
import AppDispatcher from "../common/app-dispatcher";
|
||||
import RestAPI from '../common/api/rest-api';
|
||||
|
||||
|
||||
class ScenariosDataManager extends RestDataManager {
|
||||
constructor() {
|
||||
super('scenario', '/scenarios');
|
||||
|
||||
this.onLoad = this.onScenariosLoad
|
||||
}
|
||||
|
||||
getUsers(token, id) {
|
||||
|
@ -38,33 +41,31 @@ class ScenariosDataManager extends RestDataManager {
|
|||
})
|
||||
}
|
||||
|
||||
getComponentConfigs(token, id) {
|
||||
RestAPI.get(this.makeURL('/scenarios/' + id + '/configs'), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/configs',
|
||||
configs: response.configs
|
||||
});
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/configs-error',
|
||||
error: error
|
||||
});
|
||||
});
|
||||
}
|
||||
onScenariosLoad(data, token){
|
||||
|
||||
getDashboards(token, id) {
|
||||
RestAPI.get(this.makeURL('/scenarios/' + id + '/dashboards'), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/dashboards',
|
||||
dashboards: response.dashboards
|
||||
});
|
||||
}).catch(error => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/dashboards-error',
|
||||
error: error
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!Array.isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
|
||||
for (let scenario of data){
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-load',
|
||||
token: token,
|
||||
param: '?scenarioID=' + scenario.id
|
||||
});
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-load',
|
||||
token: token,
|
||||
param: '?scenarioID=' + scenario.id
|
||||
});
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
token: token,
|
||||
param: '?scenarioID='+scenario.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
export default new ScenariosDataManager();
|
||||
|
|
|
@ -26,6 +26,7 @@ import LoginStore from '../user/login-store';
|
|||
import DashboardStore from '../dashboard/dashboard-store';
|
||||
import WidgetStore from "../widget/widget-store";
|
||||
import ConfigStore from '../componentconfig/config-store';
|
||||
import SignalStore from '../signal/signal-store'
|
||||
|
||||
import Icon from '../common/icon';
|
||||
import Table from '../common/table';
|
||||
|
@ -40,21 +41,16 @@ import DeleteDialog from '../common/dialogs/delete-dialog';
|
|||
class Scenarios extends Component {
|
||||
|
||||
static getStores() {
|
||||
return [ScenarioStore, LoginStore, DashboardStore, WidgetStore, ConfigStore];
|
||||
return [ScenarioStore, LoginStore, DashboardStore, WidgetStore, ConfigStore, SignalStore];
|
||||
}
|
||||
|
||||
static calculateState() {
|
||||
const scenarios = ScenarioStore.getState();
|
||||
const sessionToken = LoginStore.getState().token;
|
||||
|
||||
let dashboards = DashboardStore.getState();
|
||||
let configs = ConfigStore.getState();
|
||||
|
||||
return {
|
||||
scenarios,
|
||||
dashboards,
|
||||
configs,
|
||||
sessionToken,
|
||||
scenarios: ScenarioStore.getState(),
|
||||
dashboards: DashboardStore.getState(),
|
||||
configs: ConfigStore.getState(),
|
||||
sessionToken: LoginStore.getState().token,
|
||||
|
||||
newModal: false,
|
||||
deleteModal: false,
|
||||
|
@ -73,110 +69,20 @@ class Scenarios extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
// when length of scenarios array has increased, either add data (after import)
|
||||
// or load data (after export)
|
||||
if (this.state.scenarios.length > prevState.scenarios.length) {
|
||||
if (this.addDashboards || this.addConfigs) {
|
||||
let scenarioID = this.state.scenarios[this.state.scenarios.length - 1].id;
|
||||
|
||||
if (this.addDashboards) {
|
||||
this.dashboardsToAdd.forEach((dashboard) => {
|
||||
if (dashboard.widgets) {
|
||||
this.addWidgets = true;
|
||||
}
|
||||
dashboard.scenarioID = scenarioID;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-add',
|
||||
token: this.state.sessionToken,
|
||||
data: dashboard
|
||||
});
|
||||
})
|
||||
this.addDashboards = false;
|
||||
}
|
||||
|
||||
if (this.addConfigs) {
|
||||
this.configsToAdd.forEach((config) => {
|
||||
config.scenarioID = scenarioID;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-add',
|
||||
token: this.state.sessionToken,
|
||||
data: config
|
||||
})
|
||||
})
|
||||
delete this.configsToAdd;
|
||||
this.addConfigs = false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
let scenarios = Object.assign([], this.state.scenarios); // copying neccessary?
|
||||
for (var i = prevState.scenarios.length; i < scenarios.length; i++) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'dashboards/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?scenarioID=' + scenarios[i].id
|
||||
});
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?scenarioID=' + scenarios[i].id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when length of dashboards array has increased, either add widgets (after import)
|
||||
// or load widgets (after export)
|
||||
if (this.state.dashboards.length > prevState.dashboards.length) {
|
||||
if (this.addWidgets && !this.addDashboards) { // add widget data
|
||||
let dashboards = Object.assign([], this.state.dashboards);
|
||||
for (var j = prevState.dashboards.length; j < dashboards.length; j++) {
|
||||
let dboard = dashboards[j];
|
||||
let dboardID = dboard.id;
|
||||
let dashboard = this.dashboardsToAdd.shift();
|
||||
if (dashboard.name !== dboard.name) {
|
||||
console.log("Cannot add widgets, dashboard was not added as expected!");
|
||||
this.addWidgets = false;
|
||||
return;
|
||||
}
|
||||
dashboard.widgets.forEach((widget) => {
|
||||
widget.dashboardID = dboardID;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-add',
|
||||
token: this.state.sessionToken,
|
||||
data: widget
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (this.dashboardsToAdd.length === 0) {
|
||||
delete this.dashboardsToAdd;
|
||||
this.addWidgets = false;
|
||||
}
|
||||
}
|
||||
else { // load widget data
|
||||
let dashboards = Object.assign([], this.state.dashboards);
|
||||
for (var j = prevState.dashboards.length; j < dashboards.length; j++) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'widgets/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?dashboardID=' + dashboards[j].id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closeNewModal(data) {
|
||||
if(data) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/start-add',
|
||||
data: data,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
this.setState({ newModal: false });
|
||||
}
|
||||
|
||||
showDeleteModal(id) {
|
||||
// get scenario by id
|
||||
var deleteScenario;
|
||||
let deleteScenario;
|
||||
|
||||
this.state.scenarios.forEach((scenario) => {
|
||||
if (scenario.id === id) {
|
||||
|
@ -240,20 +146,9 @@ class Scenarios extends Component {
|
|||
this.setState({ importModal: false });
|
||||
|
||||
if (data) {
|
||||
let newScenario = JSON.parse(JSON.stringify(data));
|
||||
// temporarily store dashboard data until scenario is created
|
||||
if (data.dashboards) {
|
||||
this.addDashboards = true;
|
||||
this.dashboardsToAdd = data.dashboards;
|
||||
}
|
||||
if (data.configs) {
|
||||
this.addConfigs = true;
|
||||
this.configsToAdd = data.configs;
|
||||
}
|
||||
delete newScenario.dashboards;
|
||||
AppDispatcher.dispatch({
|
||||
type: 'scenarios/start-add',
|
||||
data: newScenario,
|
||||
data: data,
|
||||
token: this.state.sessionToken,
|
||||
});
|
||||
}
|
||||
|
@ -279,8 +174,24 @@ class Scenarios extends Component {
|
|||
let jsonObj = scenario;
|
||||
|
||||
configs.forEach((config) => {
|
||||
let signals = JSON.parse(JSON.stringify(SignalStore.getState().filter(s => s.configID === parseInt(config.id, 10))));
|
||||
signals.forEach((signal) => {
|
||||
delete signal.configID;
|
||||
delete signal.id;
|
||||
})
|
||||
|
||||
// two separate lists for inputMapping and outputMapping
|
||||
let inputSignals = signals.filter(s => s.direction === 'in');
|
||||
let outputSignals = signals.filter(s => s.direction === 'out');
|
||||
|
||||
// add signal mappings to config
|
||||
config["inputMapping"] = inputSignals;
|
||||
config["outputMapping"] = outputSignals;
|
||||
|
||||
delete config.id;
|
||||
delete config.scenarioID;
|
||||
delete config.inputLength;
|
||||
delete config.outputLength;
|
||||
})
|
||||
jsonObj["configs"] = configs;
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ class SignalsDataManager extends RestDataManager{
|
|||
|
||||
reloadConfig(token, data){
|
||||
// request in signals
|
||||
console.log("Reloading component config due to signal add/remove")
|
||||
RestAPI.get(this.makeURL('/configs/' + data.configID), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/edited',
|
||||
|
|
|
@ -249,7 +249,7 @@ span.signal-unit::after {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: none;
|
||||
justify-content: center;
|
||||
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
@ -265,6 +265,15 @@ span.signal-unit::after {
|
|||
|
||||
/* End button widget styling */
|
||||
|
||||
/*Lamp Widget styling*/
|
||||
|
||||
.lamp-widget {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* End lamp widget styling*/
|
||||
|
||||
.full {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -406,7 +415,13 @@ div[class*="-widget"] label {
|
|||
background-color: #fff;
|
||||
}
|
||||
|
||||
.table-widget td, .table-widget th {
|
||||
.table-widget th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table-widget td{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import EditWidgetTextControl from './edit-widget-text-control';
|
|||
import EditWidgetNumberControl from './edit-widget-number-control';
|
||||
import EditWidgetColorControl from './edit-widget-color-control';
|
||||
import EditWidgetTimeControl from './edit-widget-time-control';
|
||||
import EditImageWidgetControl from './edit-widget-image-control';
|
||||
import EditFileWidgetControl from './edit-widget-file-control';
|
||||
import EditWidgetSignalControl from './edit-widget-signal-control';
|
||||
import EditWidgetSignalsControl from './edit-widget-signals-control';
|
||||
import EditWidgetOrientation from './edit-widget-orientation';
|
||||
|
@ -33,7 +33,7 @@ import EditWidgetMinMaxControl from './edit-widget-min-max-control';
|
|||
import EditWidgetHTMLContent from './edit-widget-html-content';
|
||||
import EditWidgetParametersControl from './edit-widget-parameters-control';
|
||||
|
||||
export default function CreateControls(widgetType = null, widget = null, sessionToken = null, files = null, signals, handleChange) {
|
||||
export default function CreateControls(widgetType = null, widget = null, sessionToken = null, files = null, signals, handleChange, onUpload) {
|
||||
// Use a list to concatenate the controls according to the widget type
|
||||
var DialogControls = [];
|
||||
|
||||
|
@ -84,7 +84,7 @@ export default function CreateControls(widgetType = null, widget = null, session
|
|||
// Restrict to only image file types (MIME)
|
||||
//let imageControlFiles = files == null? [] : files.filter(file => file.type.includes('image'));
|
||||
DialogControls.push(
|
||||
<EditImageWidgetControl key={0} widget={widget} controlId={"customProperties.file"} sessionToken={sessionToken} files={files} handleChange={(e) => handleChange(e)} />,
|
||||
<EditFileWidgetControl key={0} widget={widget} controlId={"customProperties.file"} files={files} type={'image'} handleChange={(e) => handleChange(e)} onUpload={(f,i) => onUpload(f,i)} />,
|
||||
<EditWidgetAspectControl key={1} widget={widget} controlId={"customProperties.lockAspect"} handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
|
@ -149,7 +149,7 @@ export default function CreateControls(widgetType = null, widget = null, session
|
|||
// Restrict to only xml files (MIME)
|
||||
//let topologyControlFiles = files == null? [] : files.filter( file => file.type.includes('xml'));
|
||||
DialogControls.push(
|
||||
<EditImageWidgetControl key={0} widget={widget} controlId={"customProperties.file"} sessionToken={sessionToken} files={files} handleChange={(e) => handleChange(e)} />
|
||||
<EditFileWidgetControl key={0} widget={widget} controlId={"customProperties.file"} files={files} type={'xml'} handleChange={(e) => handleChange(e) } onUpload={(f,i) => onUpload(f,i)} />
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
import React from 'react';
|
||||
import {FormGroup, FormControl, FormLabel, Button, ProgressBar} from 'react-bootstrap';
|
||||
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
class EditFileWidgetControl extends React.Component {
|
||||
|
||||
class EditImageWidgetControl extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
widget: { },
|
||||
files: [],
|
||||
fileList: null,
|
||||
progress: 0
|
||||
};
|
||||
|
@ -33,7 +33,8 @@ class EditImageWidgetControl extends React.Component {
|
|||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
return {
|
||||
widget: props.widget
|
||||
widget: props.widget,
|
||||
files: props.files.filter(file => file.type.includes(props.type))
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -43,20 +44,11 @@ class EditImageWidgetControl extends React.Component {
|
|||
|
||||
for (let key in this.state.fileList) {
|
||||
if (this.state.fileList.hasOwnProperty(key) && this.state.fileList[key] instanceof File) {
|
||||
formData.append(key, this.state.fileList[key]);
|
||||
formData.append("file", this.state.fileList[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// upload files
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-upload',
|
||||
data: formData,
|
||||
token: this.props.sessionToken,
|
||||
progressCallback: this.uploadProgress,
|
||||
finishedCallback: this.clearProgress,
|
||||
objectType: "widget",
|
||||
objectID: this.props.widget.id,
|
||||
});
|
||||
this.props.onUpload(formData,this.props.widget);
|
||||
}
|
||||
|
||||
uploadProgress = (e) => {
|
||||
|
@ -68,7 +60,6 @@ class EditImageWidgetControl extends React.Component {
|
|||
}
|
||||
|
||||
handleFileChange(e){
|
||||
console.log("Changing image: ", this.props.controlId, "to", e.target.value)
|
||||
this.props.handleChange({ target: { id: this.props.controlId, value: e.target.value } });
|
||||
}
|
||||
|
||||
|
@ -80,24 +71,25 @@ class EditImageWidgetControl extends React.Component {
|
|||
isCustomProperty = false;
|
||||
}
|
||||
|
||||
console.log("edit image: files: ", this.props.files, "widget", this.state.widget, "upload file list:", this.state.fileList);
|
||||
let fileOptions = [];
|
||||
if (this.state.files.length > 0){
|
||||
fileOptions.push(
|
||||
<option key = {0} default>Select image file</option>
|
||||
)
|
||||
fileOptions.push(this.state.files.map((file, index) => (
|
||||
<option key={index+1} value={file.id}>{file.name}</option>
|
||||
)))
|
||||
} else {
|
||||
fileOptions = <option disabled value style={{ display: 'none' }}>No files found, please upload one first.</option>
|
||||
}
|
||||
|
||||
return <div>
|
||||
<FormGroup controlId="file">
|
||||
<FormLabel>Image</FormLabel>
|
||||
<FormControl
|
||||
as="select"
|
||||
placeholder="Select image file"
|
||||
value={isCustomProperty ? this.state.widget[parts[0]][parts[1]] : this.state.widget[this.props.controlId]}
|
||||
onChange={(e) => this.handleFileChange(e)}>
|
||||
{this.props.files.length === 0 ? (
|
||||
<option disabled value style={{ display: 'none' }}>No images found, please upload one first.</option>
|
||||
) : (
|
||||
this.props.files.map((file, index) => (
|
||||
<option key={index+1} value={file.id}>{file.name}</option>
|
||||
))
|
||||
)}
|
||||
</FormControl>
|
||||
onChange={(e) => this.handleFileChange(e)}>{fileOptions} </FormControl>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup controlId="upload">
|
||||
|
@ -111,4 +103,4 @@ class EditImageWidgetControl extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default EditImageWidgetControl;
|
||||
export default EditFileWidgetControl;
|
|
@ -48,7 +48,8 @@ class EditWidgetSignalControl extends Component {
|
|||
return (
|
||||
<FormGroup controlId="signal">
|
||||
<FormLabel>Select signal</FormLabel>
|
||||
<FormControl as="select" placeholder="Select signal" value={this.props.widget.signalIDs[0]} onChange={(e) => this.handleSignalChange(e)}>
|
||||
<FormControl as="select" value={this.props.widget.signalIDs[0] || ""} onChange={(e) => this.handleSignalChange(e)}>
|
||||
<option default>Select signal</option>
|
||||
{
|
||||
this.props.signals.length === 0 ? (
|
||||
<option disabled value style={{ display: 'none' }}>No signals available.</option>
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
//import { FormGroup, FormControl, FormLabel } from 'react-bootstrap';
|
||||
|
||||
import Dialog from '../../common/dialogs/dialog';
|
||||
|
||||
import CreateControls from './edit-widget-control-creator';
|
||||
|
||||
class EditWidgetDialog extends React.Component {
|
||||
|
@ -53,6 +50,7 @@ class EditWidgetDialog extends React.Component {
|
|||
}
|
||||
|
||||
assignAspectRatio(changeObject, fileId) {
|
||||
fileId = parseInt(fileId, 10)
|
||||
// get aspect ratio of file
|
||||
const file = this.props.files.find(element => element.id === fileId);
|
||||
|
||||
|
@ -65,12 +63,24 @@ class EditWidgetDialog extends React.Component {
|
|||
return changeObject;
|
||||
}
|
||||
|
||||
getTextWidth(text, fontSize) {
|
||||
let font = fontSize + "px ariel";
|
||||
let canvas = this.getTextWidth.canvas || (this.getTextWidth.canvas = document.createElement("canvas"));
|
||||
let context = canvas.getContext("2d");
|
||||
context.font = font;
|
||||
let metrics = context.measureText(text);
|
||||
return metrics.width;
|
||||
}
|
||||
|
||||
setMaxWidth(changeObject){
|
||||
if(changeObject.type === 'Label'){
|
||||
changeObject.customProperties.maxWidth = (changeObject.customProperties.textSize* 0.34) * changeObject.name.length;
|
||||
changeObject.customProperties.maxWidth = Math.ceil(this.getTextWidth(changeObject.name, changeObject.customProperties.textSize));
|
||||
}
|
||||
else if (changeObject.type === 'Value'){
|
||||
// changeObject.customProperties.maxWidth = (changeObject.customProperties.textSize* 0.5) * (changeObject.name.length+13);
|
||||
/*else if (changeObject.type === 'Value'){
|
||||
changeObject.customProperties.maxWidth = Math.ceil(this.getTextWidth(changeObject.name, changeObject.customProperties.textSize));
|
||||
}*/
|
||||
if(this.state.temporal.width > changeObject.customProperties.maxWidth){
|
||||
changeObject.width = changeObject.customProperties.maxWidth;
|
||||
}
|
||||
return changeObject;
|
||||
}
|
||||
|
@ -97,13 +107,13 @@ class EditWidgetDialog extends React.Component {
|
|||
// not a customProperty
|
||||
customProperty = false;
|
||||
}
|
||||
|
||||
|
||||
if (parts[1] === 'lockAspect') {
|
||||
//not a customProperty
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.checked : changeObject[e.target.id] = e.target.checked;
|
||||
|
||||
// correct image aspect if turned on
|
||||
if (e.target.checked && this.state.temporal.customProperties.file) {
|
||||
if (e.target.checked && (this.state.temporal.customProperties.file !== -1)) {
|
||||
changeObject = this.assignAspectRatio(changeObject, this.state.temporal.customProperties.file);
|
||||
}
|
||||
} else if (e.target.id.includes('file')) {
|
||||
|
@ -111,7 +121,7 @@ class EditWidgetDialog extends React.Component {
|
|||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value;
|
||||
|
||||
// get file and update size (if it's an image)
|
||||
if ('lockAspect' in this.state.temporal && this.state.temporal.lockAspect) {
|
||||
if ((changeObject.customProperties.file !== -1)&&('lockAspect' in this.state.temporal && this.state.temporal.lockAspect)) {
|
||||
// TODO this if condition requires changes to work!!!
|
||||
changeObject = this.assignAspectRatio(changeObject, e.target.value);
|
||||
}
|
||||
|
@ -122,7 +132,7 @@ class EditWidgetDialog extends React.Component {
|
|||
}else if(parts[1] === 'orientation'){
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = e.target.value : changeObject[e.target.id] = e.target.value ;
|
||||
changeObject = this.setNewLockRestrictions(changeObject);
|
||||
}
|
||||
}
|
||||
else if (e.target.type === 'number') {
|
||||
customProperty ? changeObject[parts[0]][parts[1]] = Number(e.target.value) : changeObject[e.target.id] = Number(e.target.value);
|
||||
} else if(e.target.id === 'name'){
|
||||
|
@ -167,7 +177,8 @@ class EditWidgetDialog extends React.Component {
|
|||
this.props.sessionToken,
|
||||
this.props.files,
|
||||
this.props.signals,
|
||||
(e) => this.handleChange(e));
|
||||
(e) => this.handleChange(e),
|
||||
(f,i) => this.props.onUpload(f,i));
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -114,7 +114,7 @@ class EditableWidgetContainer extends React.Component {
|
|||
minWidth={widget.minWidth}
|
||||
minHeight={widget.minHeight}
|
||||
maxWidth ={widget.customProperties.maxWidth || '100%' }
|
||||
lockAspectRatio={Boolean(widget.isLocked)}
|
||||
lockAspectRatio={Boolean(widget.customProperties.lockAspect)}
|
||||
bounds={'parent'}
|
||||
className={widgetClasses}
|
||||
onResizeStart={this.borderWasClicked}
|
||||
|
|
|
@ -70,7 +70,7 @@ class WidgetFactory {
|
|||
case 'Value':
|
||||
widget.minWidth = 70;
|
||||
widget.minHeight = 20;
|
||||
widget.width = 150;
|
||||
widget.width = 110;
|
||||
widget.height = 30;
|
||||
widget.customProperties.textSize = 16;
|
||||
widget.name = 'Value';
|
||||
|
@ -122,7 +122,7 @@ class WidgetFactory {
|
|||
widget.width = 200;
|
||||
widget.height = 200;
|
||||
widget.customProperties.lockAspect = true;
|
||||
widget.customProperties.file = 2; // ID of image file, -1 means non selected
|
||||
widget.customProperties.file = -1; // ID of image file, -1 means non selected
|
||||
break;
|
||||
case 'Button':
|
||||
widget.minWidth = 100;
|
||||
|
@ -134,6 +134,7 @@ class WidgetFactory {
|
|||
widget.customProperties.on_value = 1;
|
||||
widget.customProperties.off_value = 0;
|
||||
widget.customProperties.toggle = false;
|
||||
widget.customProperties.pressed = false;
|
||||
break;
|
||||
case 'NumberInput':
|
||||
widget.minWidth = 150;
|
||||
|
@ -142,6 +143,7 @@ class WidgetFactory {
|
|||
widget.height = 50;
|
||||
widget.customProperties.showUnit = false;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
widget.customProperties.value = '';
|
||||
break;
|
||||
case 'Slider':
|
||||
widget.minWidth = 380;
|
||||
|
@ -154,7 +156,8 @@ class WidgetFactory {
|
|||
widget.customProperties.rangeUseMinMax = true;
|
||||
widget.customProperties.showUnit = true;
|
||||
widget.customProperties.continous_update = false;
|
||||
widget.customProperties.default_value = 0;
|
||||
widget.customProperties.default_value = '0';
|
||||
widget.customProperties.value = '';
|
||||
widget.customProperties.resizeLeftRightLock = false;
|
||||
widget.customProperties.resizeTopBottomLock = true;
|
||||
|
||||
|
@ -185,6 +188,7 @@ class WidgetFactory {
|
|||
case 'Topology':
|
||||
widget.width = 600;
|
||||
widget.height = 400;
|
||||
widget.customProperties.file = -1; // ID of file, -1 means non selected
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -26,7 +26,7 @@ class PlotLegend extends React.Component {
|
|||
return <div className="plot-legend">
|
||||
<ul>
|
||||
{this.props.signals.map(signal =>
|
||||
<li key={signal.index} className="signal-legend" style={{ color: colorScale(signal.index) }}>
|
||||
<li key={signal.id} className="signal-legend" style={{ color: colorScale(signal.id) }}>
|
||||
<span className="signal-legend-name">{signal.name}</span>
|
||||
<span style={{ marginLeft: '0.3em' }} className="signal-unit">{signal.unit}</span>
|
||||
</li>
|
||||
|
|
|
@ -80,7 +80,7 @@ class Plot extends React.Component {
|
|||
}
|
||||
|
||||
// check if data is invalid
|
||||
if (props.data == null || props.data.length === 0 || props.data[0].length === 0) {
|
||||
if (props.data == null || props.data.length === 0) {
|
||||
// create empty plot axes
|
||||
let xScale;
|
||||
let yScale;
|
||||
|
@ -116,11 +116,14 @@ class Plot extends React.Component {
|
|||
|
||||
// only show data in requested time
|
||||
let data = props.data;
|
||||
let icDataset = data.find(function(element) {
|
||||
return element !== undefined;
|
||||
})
|
||||
|
||||
const firstTimestamp = data[0][data[0].length - 1].x - (props.time + 1) * 1000;
|
||||
if (data[0][0].x < firstTimestamp) {
|
||||
const firstTimestamp = icDataset[icDataset.length - 1].x - (props.time + 1) * 1000;
|
||||
if (icDataset[0].x < firstTimestamp) {
|
||||
// only show data in range (+100 ms)
|
||||
const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
|
||||
const index = icDataset.findIndex(value => value.x >= firstTimestamp - 100);
|
||||
data = data.map(values => values.slice(index));
|
||||
}
|
||||
|
||||
|
@ -177,7 +180,11 @@ class Plot extends React.Component {
|
|||
if (this.props.yUseMinMax) {
|
||||
yRange = [this.props.yMin, this.props.yMax];
|
||||
} else if (this.props.data.length > 0) {
|
||||
yRange = [this.props.data[0][0].y, this.props.data[0][0].y];
|
||||
let icDataset = this.props.data.find(function(element) {
|
||||
return element !== undefined;
|
||||
})
|
||||
|
||||
yRange = [icDataset[0].y, icDataset[0].y];
|
||||
|
||||
this.props.data.forEach(values => {
|
||||
const range = extent(values, p => p.y);
|
||||
|
|
|
@ -29,7 +29,8 @@ class WidgetStore extends ArrayStore {
|
|||
|
||||
case 'widgets/loaded':
|
||||
|
||||
WidgetsDataManager.loadFiles(action.token, action.data);
|
||||
//WidgetsDataManager.loadFiles(action.token, action.data);
|
||||
// TODO make sure files of scenario are loaded
|
||||
return super.reduce(state, action);
|
||||
|
||||
default:
|
||||
|
|
|
@ -69,14 +69,12 @@ class Widget extends React.Component {
|
|||
// TODO make sure that the signals are only the signals that belong to the scenario at hand
|
||||
let signals = SignalStore.getState();
|
||||
let icIDs = [];
|
||||
if ( props.data.signalIDs.length > 0){
|
||||
for (let i in props.data.signalIDs.length){
|
||||
let signal = signals.find(s => s.id === props.data.signalIDs[i]);
|
||||
let config = configs.find(m => m.id === signal.configID);
|
||||
icIDs[i] = config.icID;
|
||||
}
|
||||
}
|
||||
|
||||
for (let id of props.data.signalIDs){
|
||||
let signal = signals.find(s => s.id === id);
|
||||
let config = configs.find(m => m.id === signal.configID);
|
||||
icIDs[signal.id] = config.icID;
|
||||
}
|
||||
|
||||
return {
|
||||
icData: icData,
|
||||
|
@ -90,27 +88,6 @@ class Widget extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.state.sessionToken == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?objectID=1&objectType=widget'
|
||||
});*/
|
||||
|
||||
// TODO no not load component congfigs here, since they are loaded via the scenario, pass them as props
|
||||
/*
|
||||
AppDispatcher.dispatch({
|
||||
type: 'configs/start-load',
|
||||
token: this.state.sessionToken,
|
||||
param: '?scenarioID=1' // TODO do not hardcode scenarioID!
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
inputDataChanged(widget, data) {
|
||||
// The following assumes that a widget modifies/ uses exactly one signal
|
||||
AppDispatcher.dispatch({
|
||||
|
@ -132,13 +109,13 @@ class Widget extends React.Component {
|
|||
} else if (widget.type === 'Value') {
|
||||
return <WidgetValue widget={widget} data={this.state.icData} dummy={this.state.sequence} signals={this.state.signals} icIDs={this.state.icIDs} />
|
||||
} else if (widget.type === 'Plot') {
|
||||
return <WidgetPlot widget={widget} data={this.state.icData} dummy={this.state.sequence} paused={this.props.paused} />
|
||||
return <WidgetPlot widget={widget} data={this.state.icData} dummy={this.state.sequence} signals={this.state.signals} icIDs={this.state.icIDs} paused={this.props.paused} />
|
||||
} else if (widget.type === 'Table') {
|
||||
return <WidgetTable widget={widget} data={this.state.icData} dummy={this.state.sequence} signals={this.state.signals} icIDs={this.state.icIDs} />
|
||||
} else if (widget.type === 'Label') {
|
||||
return <WidgetLabel widget={widget} />
|
||||
} else if (widget.type === 'PlotTable') {
|
||||
return <WidgetPlotTable widget={widget} data={this.state.icData} dummy={this.state.sequence} editing={this.props.editing} onWidgetChange={(w) => this.props.onWidgetStatusChange(w, this.props.index)} paused={this.props.paused} />
|
||||
return <WidgetPlotTable widget={widget} data={this.state.icData} dummy={this.state.sequence} signals={this.state.signals} icIDs={this.state.icIDs} editing={this.props.editing} onWidgetChange={(w) => this.props.onWidgetStatusChange(w, this.props.index)} paused={this.props.paused} />
|
||||
} else if (widget.type === 'Image') {
|
||||
return <WidgetImage widget={widget} files={this.state.files} token={this.state.sessionToken} />
|
||||
} else if (widget.type === 'Button') {
|
||||
|
@ -154,7 +131,7 @@ class Widget extends React.Component {
|
|||
} else if (widget.type === 'HTML') {
|
||||
return <WidgetHTML widget={widget} editing={this.props.editing} />
|
||||
} else if (widget.type === 'Topology') {
|
||||
return <WidgetTopology widget={widget} files={this.state.files} />
|
||||
return <WidgetTopology widget={widget} files={this.state.files} token={this.state.sessionToken} />
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
|
||||
import RestDataManager from '../common/data-managers/rest-data-manager';
|
||||
import RestAPI from "../common/api/rest-api";
|
||||
import AppDispatcher from "../common/app-dispatcher";
|
||||
|
||||
class WidgetsDataManager extends RestDataManager{
|
||||
|
||||
|
@ -26,18 +24,6 @@ class WidgetsDataManager extends RestDataManager{
|
|||
super('widget', '/widgets');
|
||||
}
|
||||
|
||||
loadFiles(token, widgets){
|
||||
for (let widget of widgets) {
|
||||
// request files of widget
|
||||
RestAPI.get(this.makeURL('/files?objectType=widget&objectID=' + widget.id), token).then(response => {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/loaded',
|
||||
data: response.files
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new WidgetsDataManager()
|
||||
|
|
|
@ -24,12 +24,12 @@ class WidgetButton extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
pressed: false
|
||||
pressed: props.widget.customProperties.pressed
|
||||
}
|
||||
}
|
||||
|
||||
onPress(e) {
|
||||
console.log("button was pressed!");
|
||||
|
||||
if (!this.props.widget.customProperties.toggle) {
|
||||
this.setState({ pressed: true });
|
||||
this.valueChanged(this.props.widget.customProperties.on_value);
|
||||
|
@ -37,12 +37,12 @@ class WidgetButton extends Component {
|
|||
}
|
||||
|
||||
onRelease(e) {
|
||||
console.log("button was released!");
|
||||
|
||||
let nextState = false;
|
||||
if (this.props.widget.customProperties.toggle) {
|
||||
nextState = !this.state.pressed;
|
||||
}
|
||||
|
||||
this.props.widget.customProperties.pressed = nextState;
|
||||
this.setState({ pressed: nextState });
|
||||
this.valueChanged(nextState ? this.props.widget.customProperties.on_value : this.props.widget.customProperties.off_value);
|
||||
}
|
||||
|
|
|
@ -21,39 +21,60 @@ import AppDispatcher from '../../common/app-dispatcher';
|
|||
|
||||
class WidgetImage extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
file: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Query the image referenced by the widget
|
||||
let widgetFile = this.props.widget.customProperties.file;
|
||||
if (widgetFile !== -1 && !this.props.files.find(file => file.id === widgetFile)) {
|
||||
if (widgetFile !== -1 && this.state.file === undefined) {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-load',
|
||||
type: 'files/start-download',
|
||||
data: widgetFile,
|
||||
token: this.props.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const file = this.props.files.find(file => file.id === this.props.widget.customProperties.file);
|
||||
let fileHasData = false;
|
||||
let fileData, objectURL;
|
||||
if (file){
|
||||
fileHasData = file.hasOwnProperty("data");
|
||||
if (fileHasData){
|
||||
//console.log("File data: ", file.data);
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS) {
|
||||
|
||||
fileData = new Blob([file.data], {type: file.type});
|
||||
objectURL = window.URL.createObjectURL(fileData);
|
||||
console.log("Image created new file", fileData, "and objectID", objectURL)
|
||||
let file = this.props.files.find(file => file.id === parseInt(this.props.widget.customProperties.file, 10));
|
||||
|
||||
if (file !== undefined) {
|
||||
if (this.state.file === undefined || (this.state.file.id !== file.id)) {
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-download',
|
||||
data: file.id,
|
||||
token: this.props.token
|
||||
});
|
||||
|
||||
this.setState({ file: file })
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Image: has data:", fileHasData);
|
||||
}
|
||||
|
||||
imageError(e){
|
||||
console.error("Image ", this.state.file.name, "cannot be displayed.");
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
let objectURL=''
|
||||
if(this.state.file !== undefined && this.state.file.objectURL !== undefined) {
|
||||
objectURL = this.state.file.objectURL
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="full">
|
||||
{file ? (
|
||||
<img className="full" alt={file.name} src={fileHasData ? objectURL : ''} onDragStart={e => e.preventDefault()} />
|
||||
{objectURL !== '' ? (
|
||||
<img onError={(e) => this.imageError(e)} className="full" alt={this.state.file.name} src={objectURL} onDragStart={e => e.preventDefault()} />
|
||||
) : (
|
||||
<img className="full" alt="No file selected." />
|
||||
)}
|
||||
|
|
|
@ -31,14 +31,27 @@ class WidgetInput extends Component {
|
|||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
let returnState = {};
|
||||
|
||||
if(props.widget.customProperties.value !== ''){
|
||||
returnState["value"] = props.widget.customProperties.value;
|
||||
}
|
||||
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
if (props.widget.customProperties.default_value && state.value === undefined && props.widget.customProperties.value === '') {
|
||||
returnState["value"] = props.widget.customProperties.default_value;
|
||||
} else { // if no default available
|
||||
if (returnState !== {}){
|
||||
return returnState;
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update value
|
||||
if (props.widget.customProperties.default_value && this.state.value === undefined) {
|
||||
if (props.widget.customProperties.default_value && this.state.value === undefined && props.widget.customProperties.value === '') {
|
||||
returnState["value"] = props.widget.customProperties.default_value;
|
||||
}
|
||||
|
||||
|
@ -59,6 +72,7 @@ class WidgetInput extends Component {
|
|||
|
||||
valueIsChanging(newValue) {
|
||||
this.setState({ value: newValue });
|
||||
this.props.widget.customProperties.value = newValue;
|
||||
}
|
||||
|
||||
valueChanged(newValue) {
|
||||
|
|
|
@ -66,8 +66,6 @@ class WidgetLamp extends Component {
|
|||
|
||||
let style = {
|
||||
backgroundColor: color,
|
||||
width: this.props.widget.width,
|
||||
height: this.props.widget.height
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -16,123 +16,80 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, FormCheck } from 'react-bootstrap';
|
||||
|
||||
import { FormGroup } from 'react-bootstrap';
|
||||
import Plot from '../widget-plot/plot';
|
||||
import PlotLegend from '../widget-plot/plot-legend';
|
||||
|
||||
class WidgetPlotTable extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
preselectedSignals: [],
|
||||
signals: []
|
||||
signals: [],
|
||||
data: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
|
||||
if (this.props.config == null) {
|
||||
return;
|
||||
}
|
||||
static getDerivedStateFromProps(props, state){
|
||||
let intersection = []
|
||||
let data = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
intersection.push(sig);
|
||||
|
||||
// Update internal selected signals state with props (different array objects)
|
||||
if (prevProps.widget.customProperties.signals !== this.props.widget.customProperties.signals) {
|
||||
this.setState( {signals: this.props.widget.customProperties.signals});
|
||||
}
|
||||
// sig is a selected signal, get data
|
||||
// determine ID of infrastructure component related to signal (via config)
|
||||
let icID = props.icIDs[sig.id]
|
||||
|
||||
// Identify if there was a change in the preselected signals
|
||||
if (JSON.stringify(prevProps.widget.customProperties.preselectedSignals) !== JSON.stringify(this.props.widget.customProperties.preselectedSignals)
|
||||
|| this.state.preselectedSignals.length === 0) {
|
||||
// Update the currently selected signals by intersecting with the preselected signalsWidget
|
||||
// Do the same with the plot values
|
||||
var intersection = this.computeIntersection(this.props.widget.customProperties.preselectedSignals, this.props.widget.customProperties.signals);
|
||||
this.setState({ signals: intersection });
|
||||
|
||||
this.updatePreselectedSignalsState(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the intersection of the lists, alternatively could be done with Sets ensuring unique values
|
||||
computeIntersection(preselectedSignals, selectedSignals) {
|
||||
return preselectedSignals.filter( s => selectedSignals.includes(s));
|
||||
}
|
||||
|
||||
updatePreselectedSignalsState(props) {
|
||||
// Create checkboxes using the signal indices from component config
|
||||
if(props.config.outputMapping){
|
||||
const preselectedSignals = props.config.outputMapping.reduce(
|
||||
// Loop through component config signals
|
||||
(accum, signal, signal_index) => {
|
||||
// Append them if they belong to the current selected type
|
||||
if (props.widget.customProperties.preselectedSignals.indexOf(signal_index) > -1) {
|
||||
accum.push(
|
||||
{
|
||||
index: signal_index,
|
||||
name: signal.name,
|
||||
type: signal.type
|
||||
// distinguish between input and output signals
|
||||
if (sig.direction === "out") {
|
||||
if (props.data[icID] != null && props.data[icID].output != null && props.data[icID].output.values != null) {
|
||||
if (props.data[icID].output.values[sig.index-1] !== undefined) {
|
||||
data.push(props.data[icID].output.values[sig.index-1]);
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if (sig.direction === "in") {
|
||||
if (props.data[icID] != null && props.data[icID].input != null && props.data[icID].input.values != null) {
|
||||
if (props.data[icID].input.values[sig.index-1] !== undefined) {
|
||||
data.push(props.data[icID].input.values[sig.index-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return accum;
|
||||
}, []);
|
||||
} // sig is selected signal
|
||||
} // loop over props.signals
|
||||
} // loop over selected signals
|
||||
|
||||
this.setState({ preselectedSignals });
|
||||
}
|
||||
return {signals: intersection, data: data}
|
||||
}
|
||||
|
||||
updateSignalSelection(signal_index, checked) {
|
||||
// Update the selected signals and propagate to parent component
|
||||
var new_widget = Object.assign({}, this.props.widget, {
|
||||
signals: checked? this.state.signals.concat(signal_index) : this.state.signals.filter( (idx) => idx !== signal_index )
|
||||
});
|
||||
this.props.onWidgetChange(new_widget);
|
||||
}
|
||||
// updateSignalSelection(signal, checked) {
|
||||
// // Update the selected signals and propagate to parent component
|
||||
// var new_widget = Object.assign({}, this.props.widget, {
|
||||
// checkedSignals: checked ? this.state.signals.concat(signal) : this.state.signals.filter((idx) => idx !== signal)
|
||||
// });
|
||||
// this.props.onWidgetChange(new_widget);
|
||||
// }
|
||||
|
||||
render() {
|
||||
let checkBoxes = [];
|
||||
let icData = [];
|
||||
let legendSignals = [];
|
||||
// Data passed to plot
|
||||
if (this.props.config) {
|
||||
|
||||
const ic = this.props.config.icID;
|
||||
|
||||
if (this.props.data[ic] != null && this.props.data[ic].output != null && this.props.data[ic].output.values != null) {
|
||||
icData = this.props.data[ic].output.values.filter((values, index) => (
|
||||
this.props.widget.customProperties.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
}
|
||||
|
||||
if (this.state.preselectedSignals && this.state.preselectedSignals.length > 0) {
|
||||
// Create checkboxes using the signal indices from component config
|
||||
checkBoxes = this.state.preselectedSignals.map( (signal) => {
|
||||
var checked = this.state.signals.indexOf(signal.index) > -1;
|
||||
var chkBxClasses = classNames({
|
||||
'btn': true,
|
||||
'btn-default': true,
|
||||
'active': checked
|
||||
});
|
||||
return <FormCheck key={signal.index} className={chkBxClasses} checked={checked} disabled={ this.props.editing } onChange={(e) => this.updateSignalSelection(signal.index, e.target.checked) } > { signal.name } </FormCheck>
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare an array with the signals to show in the legend
|
||||
legendSignals = this.state.preselectedSignals.reduce( (accum, signal, i) => {
|
||||
if (this.state.signals.includes(signal.index)) {
|
||||
accum.push({
|
||||
index: signal.index,
|
||||
name: signal.name,
|
||||
type: signal.type
|
||||
});
|
||||
}
|
||||
return accum;
|
||||
}, []);}
|
||||
|
||||
let showLegend = false;
|
||||
if(legendSignals !== []){
|
||||
if (this.state.signals.length > 0) {
|
||||
|
||||
showLegend = true;
|
||||
|
||||
// Create checkboxes using the signal indices from component config
|
||||
// checkBoxes = this.state.signals.map((signal) => {
|
||||
// let checked = this.state.signals.indexOf(signal) > -1;
|
||||
// let chkBxClasses = classNames({
|
||||
// 'btn': true,
|
||||
// 'btn-default': true,
|
||||
// 'active': checked
|
||||
// });
|
||||
// return <FormCheck key={signal.index} className={chkBxClasses} checked={checked} disabled={this.props.editing}
|
||||
// onChange={(e) => this.updateSignalSelection(signal, e.target.checked)}> {signal.name} </FormCheck>
|
||||
// });
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -140,17 +97,17 @@ class WidgetPlotTable extends Component {
|
|||
<div className="content">
|
||||
<div className="table-plot-row">
|
||||
<div className="widget-table">
|
||||
{ checkBoxes.length > 0 ? (
|
||||
{checkBoxes.length > 0 ? (
|
||||
<FormGroup className="btn-group-vertical">
|
||||
{ checkBoxes }
|
||||
{checkBoxes}
|
||||
</FormGroup>
|
||||
) : ( <small>No signal has been pre-selected.</small> )
|
||||
) : (<small>Use edit menu to change selected signals.</small>)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={icData}
|
||||
data={this.state.data}
|
||||
time={this.props.widget.customProperties.time}
|
||||
width={this.props.widget.width - 100}
|
||||
height={this.props.widget.height - 55}
|
||||
|
@ -162,13 +119,12 @@ class WidgetPlotTable extends Component {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{showLegend? (
|
||||
<PlotLegend signals={legendSignals} /> ) : (<div></div>)
|
||||
{showLegend ? (
|
||||
<PlotLegend signals={this.state.signals}/>) : (<div></div>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WidgetPlotTable;
|
||||
|
|
|
@ -26,49 +26,44 @@ class WidgetPlot extends React.Component {
|
|||
|
||||
this.state = {
|
||||
data: [],
|
||||
legend: []
|
||||
signals: []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
|
||||
if (props.config == null) {
|
||||
return{
|
||||
data: [],
|
||||
legend: [],
|
||||
};
|
||||
}
|
||||
let intersection = []
|
||||
let data = [];
|
||||
let signalID, sig;
|
||||
for (signalID of props.widget.signalIDs) {
|
||||
for (sig of props.signals) {
|
||||
if (signalID === sig.id) {
|
||||
intersection.push(sig);
|
||||
|
||||
const ic = props.config.icID;
|
||||
// sig is a selected signal, get data
|
||||
// determine ID of infrastructure component related to signal (via config)
|
||||
let icID = props.icIDs[sig.id]
|
||||
|
||||
// Proceed if a config and a IC are available
|
||||
if (ic && props.data[ic] != null && props.data[ic] != null && props.data[ic].output != null && props.data[ic].output.values != null) {
|
||||
const chosenSignals = props.widget.customProperties.signals;
|
||||
// distinguish between input and output signals
|
||||
if (sig.direction === "out") {
|
||||
if (props.data[icID] != null && props.data[icID].output != null && props.data[icID].output.values != null) {
|
||||
if (props.data[icID].output.values[sig.index-1] !== undefined) {
|
||||
data.push(props.data[icID].output.values[sig.index-1]);
|
||||
}
|
||||
}
|
||||
} else if (sig.direction === "in") {
|
||||
if (props.data[icID] != null && props.data[icID].input != null && props.data[icID].input.values != null) {
|
||||
if (props.data[icID].input.values[sig.index-1] !== undefined) {
|
||||
data.push(props.data[icID].input.values[sig.index-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // sig is selected signal
|
||||
} // loop over props.signals
|
||||
} // loop over selected signals
|
||||
|
||||
const data = props.data[ic].output.values.filter((values, index) => (
|
||||
props.widget.customProperties.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
|
||||
// Query the signals that will be displayed in the legend
|
||||
const legend = props.config.outputMapping.reduce( (accum, signal, signal_index) => {
|
||||
if (chosenSignals.includes(signal_index)) {
|
||||
accum.push({ index: signal_index, name: signal.name, type: signal.unit });
|
||||
}
|
||||
|
||||
return accum;
|
||||
}, []);
|
||||
|
||||
return{
|
||||
data: data,
|
||||
legend: legend,
|
||||
};
|
||||
} else {
|
||||
return{
|
||||
data: [],
|
||||
legend: [],
|
||||
};
|
||||
}
|
||||
return {signals: intersection, data: data}
|
||||
|
||||
}
|
||||
|
||||
|
@ -87,7 +82,7 @@ class WidgetPlot extends React.Component {
|
|||
yLabel={this.props.widget.customProperties.ylabel}
|
||||
/>
|
||||
</div>
|
||||
<PlotLegend signals={this.state.legend} />
|
||||
<PlotLegend signals={this.state.signals} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,19 +42,28 @@ class WidgetSlider extends Component {
|
|||
static getDerivedStateFromProps(props, state){
|
||||
let returnState = {};
|
||||
|
||||
if(props.widget.customProperties.value !== ''){
|
||||
returnState["value"] = props.widget.customProperties.value;
|
||||
}
|
||||
|
||||
if(props.widget.signalIDs.length === 0){
|
||||
|
||||
// set value to default
|
||||
if (props.widget.customProperties.default_value && state.value === undefined) {
|
||||
if (props.widget.customProperties.default_value && state.value === undefined && props.widget.customProperties.value === '') {
|
||||
returnState["value"] = props.widget.customProperties.default_value;
|
||||
} else { // if no default available
|
||||
return null;
|
||||
if (returnState !== {}){
|
||||
return returnState;
|
||||
}
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update value
|
||||
if (props.widget.customProperties.default_value && state.value === undefined) {
|
||||
if (props.widget.customProperties.default_value && state.value === undefined && props.widget.customProperties.value === '') {
|
||||
returnState["value"] = props.widget.customProperties.default_value;
|
||||
}
|
||||
|
||||
|
@ -96,6 +105,7 @@ class WidgetSlider extends Component {
|
|||
}
|
||||
|
||||
valueIsChanging(newValue) {
|
||||
this.props.widget.customProperties.value = newValue;
|
||||
if (this.props.widget.continous_update)
|
||||
this.valueChanged(newValue);
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ class WidgetTable extends Component {
|
|||
render() {
|
||||
|
||||
let rows = this.state.rows;
|
||||
|
||||
if(rows.length === 0){
|
||||
rows.push({
|
||||
name: "no entries"
|
||||
|
@ -109,7 +110,7 @@ class WidgetTable extends Component {
|
|||
columns.push(<TableColumn key={3} title="Unit" dataKey="unit" />)
|
||||
|
||||
return (
|
||||
<div className="table-widget">
|
||||
<div className="table-widget" style={{width: this.props.widget.width, height: this.props.widget.height, overflowY: 'auto'}}>
|
||||
<Table data={rows}>
|
||||
{ columns }
|
||||
</Table>
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
import React from 'react';
|
||||
import {UncontrolledReactSVGPanZoom} from 'react-svg-pan-zoom';
|
||||
import config from '../../config';
|
||||
import '../../styles/simple-spinner.css';
|
||||
import { cimsvg } from 'libcimsvg';
|
||||
import AppDispatcher from "../../common/app-dispatcher";
|
||||
|
||||
// Do not show Pintura's grid
|
||||
const pinturaGridStyle = {
|
||||
|
@ -62,11 +62,22 @@ function textSibling(e) {
|
|||
}
|
||||
|
||||
function show(element) {
|
||||
element.style.visibility = 'inherit';
|
||||
if(element !== undefined) {
|
||||
element.style.visibility = 'inherit';
|
||||
}
|
||||
else{
|
||||
console.log("MouseOver, show, element undefined.")
|
||||
}
|
||||
}
|
||||
|
||||
function hide(element) {
|
||||
element.style.visibility = 'hidden';
|
||||
if (element !== undefined) {
|
||||
element.style.visibility = 'hidden';
|
||||
} else {
|
||||
console.log("MouseLeave, hide, element undefined.")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// De-initialize functions
|
||||
|
@ -80,12 +91,27 @@ class WidgetTopology extends React.Component {
|
|||
super(props);
|
||||
this.svgElem = React.createRef();
|
||||
this.Viewer = null;
|
||||
this.dashboardState = 'initial'
|
||||
this.message = ''
|
||||
let file = this.props.files.find(file => file.id === parseInt(this.props.widget.customProperties.file, 10));
|
||||
|
||||
|
||||
this.state = {
|
||||
dashboardState: 'initial'
|
||||
file: file
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state){
|
||||
let file = props.files.find(file => file.id === parseInt(props.widget.customProperties.file, 10));
|
||||
|
||||
if (state.file === undefined || state.file.id !== file.id) {
|
||||
return{
|
||||
file: file
|
||||
};
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.svgElem) {
|
||||
window.onMouseLeave = function() {};
|
||||
|
@ -95,6 +121,19 @@ class WidgetTopology extends React.Component {
|
|||
window.onMouseDown = function() {};
|
||||
window.onMouseMove = function() {};
|
||||
}
|
||||
|
||||
//this.Viewer.fitToViewer();
|
||||
|
||||
// Query the file referenced by the widget
|
||||
let widgetFile = parseInt(this.props.widget.customProperties.file, 10);
|
||||
if (widgetFile !== -1 && this.state.file === undefined) {
|
||||
this.dashboardState = 'loading';
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-download',
|
||||
data: widgetFile,
|
||||
token: this.props.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -102,84 +141,80 @@ class WidgetTopology extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot: SS): void {
|
||||
const file = this.props.files.find(file => file._id === this.props.widget.customProperties.file);
|
||||
// Ensure model is requested only once or a different was selected
|
||||
if (prevProps.widget.customProperties.file !== this.props.widget.customProperties.file
|
||||
|| (prevState.dashboardState === 'initial' && file)) {
|
||||
|
||||
this.setState({'dashboardState': 'loading' });
|
||||
if (file) {
|
||||
fetch(new Request('/' + config.publicPathBase + file.path))
|
||||
.then( response => {
|
||||
if (response.status === 200) {
|
||||
this.setState({'dashboardState': 'ready' });
|
||||
return response.text().then( contents => {
|
||||
if (this.svgElem) {
|
||||
let cimsvgInstance = new cimsvg(this.svgElem.current);
|
||||
cimsvg.setCimsvg(cimsvgInstance);
|
||||
cimsvgInstance.setFileCount(1);
|
||||
cimsvgInstance.loadFile(contents);
|
||||
//cimsvgInstance.fit();
|
||||
attachComponentEvents();
|
||||
}
|
||||
else {
|
||||
console.error("The svgElem variable is not initialized before the attempt to create the cimsvg instance.");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
this.setState({
|
||||
'dashboardState': 'show_message',
|
||||
'message': 'Topology could not be loaded.'});
|
||||
if(this.state.file === undefined) {
|
||||
// No file has been selected
|
||||
this.dashboardState = 'show_message';
|
||||
this.message = 'Select a topology model first.';
|
||||
return;
|
||||
}
|
||||
|
||||
if((prevState.file === undefined && this.state.file !== undefined)
|
||||
|| (this.state.file.id !== prevState.file.id && this.state.file.id !== -1)) {
|
||||
// if file has changed, download new file
|
||||
this.dashboardState = 'loading';
|
||||
AppDispatcher.dispatch({
|
||||
type: 'files/start-download',
|
||||
data: this.state.file.id,
|
||||
token: this.props.token
|
||||
});
|
||||
} else if (this.state.file.hasOwnProperty("data") && this.dashboardState === 'loading') {
|
||||
// data of file has been newly downloaded (did not exist in previous state)
|
||||
this.dashboardState = 'ready';
|
||||
|
||||
} else if(this.state.file.hasOwnProperty("data") && this.dashboardState === 'ready'){
|
||||
if (this.svgElem) {
|
||||
let cimsvgInstance = new cimsvg(this.svgElem.current);
|
||||
cimsvg.setCimsvg(cimsvgInstance);
|
||||
cimsvgInstance.setFileCount(1);
|
||||
// transform data blob into string format
|
||||
this.state.file.data.text().then(function(content) {
|
||||
cimsvgInstance.loadFile(content);
|
||||
cimsvgInstance.fit();
|
||||
attachComponentEvents();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No file has been selected
|
||||
if (!this.props.widget.customProperties.file&& this.state.message !== 'Select a topology model first.') {
|
||||
this.setState({
|
||||
'dashboardState': 'show_message',
|
||||
'message': 'Select a topology model first.'});
|
||||
else {
|
||||
console.error("The svgElem variable is not initialized before the attempt to create the cimsvg instance.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
var markup = null;
|
||||
const miniatureProps = {
|
||||
miniaturePosition: "none",
|
||||
}
|
||||
|
||||
const toolbarProps = {
|
||||
toolbarPosition: "none"
|
||||
position: "none",
|
||||
}
|
||||
|
||||
switch(this.state.dashboardState) {
|
||||
const toolbarProps = {
|
||||
position: "right"
|
||||
}
|
||||
|
||||
switch(this.dashboardState) {
|
||||
case 'loading':
|
||||
markup = <div style={spinnerContainerStyle}><div className="loader" /></div>; break;
|
||||
case 'show_message':
|
||||
markup = <div style={msgContainerStyle}><div style={msgStyle}>{ this.state.message }</div></div>; break;
|
||||
markup = <div style={msgContainerStyle}><div style={msgStyle}>{ this.message }</div></div>; break;
|
||||
default:
|
||||
markup = (<div>
|
||||
<UncontrolledReactSVGPanZoom
|
||||
ref={Viewer => this.Viewer = Viewer}
|
||||
style={{outline: "1px solid grey"}}
|
||||
detectAutoPan={false}
|
||||
toolbarProps={toolbarProps}
|
||||
miniatureProps={miniatureProps}
|
||||
background="white"
|
||||
tool="pan"
|
||||
width={this.props.widget.width-2} height={this.props.widget.height-2} >
|
||||
<svg width={this.props.widget.width} height={this.props.widget.height}>
|
||||
<svg ref={ this.svgElem } width={this.props.widget.width} height={this.props.widget.height}>
|
||||
<rect className="backing" style={pinturaBackingStyle} />
|
||||
<g className="grid" style={pinturaGridStyle} />
|
||||
<g className="diagrams"/>
|
||||
</svg>
|
||||
</svg>
|
||||
ref={Viewer => this.Viewer = Viewer}
|
||||
style={{outline: "1px solid grey"}}
|
||||
detectAutoPan={false}
|
||||
toolbarProps={toolbarProps}
|
||||
miniatureProps={miniatureProps}
|
||||
background={"white"}
|
||||
width={this.props.widget.width-2}
|
||||
height={this.props.widget.height-2} >
|
||||
<svg width={this.props.widget.width} height={this.props.widget.height}>
|
||||
<svg ref={ this.svgElem } width={this.props.widget.width} height={this.props.widget.height}>
|
||||
<rect className="backing" style={pinturaBackingStyle} />
|
||||
<g className="grid" style={pinturaGridStyle} />
|
||||
<g className="diagrams"/>
|
||||
</svg>
|
||||
</svg>
|
||||
</UncontrolledReactSVGPanZoom>
|
||||
</div>);
|
||||
}
|
||||
|
|
|
@ -69,14 +69,14 @@ class WidgetValue extends Component {
|
|||
|
||||
render() {
|
||||
let value_to_render = Number(this.state.value);
|
||||
let value_width = this.props.widget.customProperties.textSize*0.55* (this.state.value.length +2);
|
||||
let unit_width = this.props.widget.customProperties.textSize*2;
|
||||
let value_width = this.props.widget.customProperties.textSize*(value_to_render < 1000 ? (2):(3));
|
||||
let unit_width = this.props.widget.customProperties.textSize*(this.state.unit.length + 0.7);
|
||||
return (
|
||||
<div className="single-value-widget">
|
||||
<strong style={{ fontSize: this.props.widget.customProperties.textSize + 'px'}}>{this.props.widget.name}</strong>
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px',width: value_width }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
|
||||
<strong style={{ fontSize: this.props.widget.customProperties.textSize + 'px', flex: '1 1 auto'}}>{this.props.widget.name}</strong>
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px', flex: 'none', width: value_width }}>{Number.isNaN(value_to_render) ? NaN : format('.3s')(value_to_render)}</span>
|
||||
{this.props.widget.customProperties.showUnit &&
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px', width: unit_width}}>[{this.state.unit}]</span>
|
||||
<span style={{ fontSize: this.props.widget.customProperties.textSize + 'px', flex: 'none', width: unit_width}}>[{this.state.unit}]</span>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue