diff --git a/package-lock.json b/package-lock.json index dd5985d95d4755be3da55aac36d492980d05e37f..2f82cfde55d7b612eeb8d49aecfeb70617b1ab15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,21 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/react": { + "version": "15.6.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-15.6.7.tgz", + "integrity": "sha512-HMfRuwiTp7/MfjPOsVlvlduouJH3haDzjc0oXqZy3ZMn3OTl3i4gGgbxsqzA/u9gNyl/oKkwOrU2oVR6vG5SAw==", + "optional": true + }, + "@types/react-dom": { + "version": "0.14.23", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-0.14.23.tgz", + "integrity": "sha1-zs/PrXVLTCdl/l0puBswGImtbC4=", + "optional": true, + "requires": { + "@types/react": "15.6.7" + } + }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", @@ -305,6 +320,11 @@ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, + "array.prototype.fill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.fill/-/array.prototype.fill-1.0.2.tgz", + "integrity": "sha1-qzMgfyHVfRqy9/DRzxItNBnDjvU=" + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -1299,6 +1319,44 @@ "babel-types": "6.26.0" } }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "6.26.0", + "core-js": "2.5.1", + "regenerator-runtime": "0.10.5" + }, + "dependencies": { + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" + } + } + }, + "core-js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" + }, + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + } + } + }, "babel-preset-env": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", @@ -1468,7 +1526,6 @@ "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=", - "dev": true, "requires": { "core-js": "2.5.1", "regenerator-runtime": "0.10.5" @@ -1477,14 +1534,12 @@ "core-js": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" }, "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" } } }, @@ -2267,6 +2322,11 @@ "color-name": "1.1.3" } }, + "colorbrewer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorbrewer/-/colorbrewer-1.1.0.tgz", + "integrity": "sha512-DO9gYp/qU4HwKD+IkpeCnqIZ8n1h8M1NbHnu1PzEUFAtIi/LOlIdaEP5fLr46id+Y7C/67qqiDHYGPCpMKoegg==" + }, "colormin": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", @@ -2666,6 +2726,11 @@ } } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -2764,6 +2829,32 @@ } } }, + "css-to-react-native": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.0.4.tgz", + "integrity": "sha1-z0zEB1WLNHTUuovhos07bOcTEBs=", + "requires": { + "css-color-keywords": "1.0.0", + "fbjs": "0.8.16", + "postcss-value-parser": "3.3.0" + }, + "dependencies": { + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.1.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" + } + } + } + }, "css-what": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", @@ -2922,6 +3013,11 @@ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==" }, + "d3-axis": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", + "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=" + }, "d3-collection": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", @@ -2932,6 +3028,16 @@ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=" }, + "d3-dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", + "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" + }, + "d3-ease": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", + "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" + }, "d3-format": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.0.tgz", @@ -2964,6 +3070,19 @@ "d3-time-format": "2.1.0" } }, + "d3-scale-chromatic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.1.1.tgz", + "integrity": "sha1-gRQG6OCdq3iknaxKMgR9XT7dDEQ=", + "requires": { + "d3-interpolate": "1.1.5" + } + }, + "d3-selection": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.2.0.tgz", + "integrity": "sha512-xW2Pfcdzh1gOaoI+LGpPsLR2VpBQxuFoxvrvguK8ZmrJbPIVvfNG6pU6GNfK41D6Qz15sj61sbW/AFYuukwaLQ==" + }, "d3-shape": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.0.4.tgz", @@ -2985,6 +3104,24 @@ "d3-time": "1.0.7" } }, + "d3-timer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", + "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==" + }, + "d3-transition": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", + "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", + "requires": { + "d3-color": "1.0.3", + "d3-dispatch": "1.0.3", + "d3-ease": "1.0.3", + "d3-interpolate": "1.1.5", + "d3-selection": "1.2.0", + "d3-timer": "1.0.7" + } + }, "damerau-levenshtein": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", @@ -4258,6 +4395,16 @@ "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", "dev": true }, + "flexbox-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/flexbox-react/-/flexbox-react-4.4.0.tgz", + "integrity": "sha1-NevT6xCLvaGydg30loqrxwYfXzM=", + "requires": { + "@types/react": "15.6.7", + "@types/react-dom": "0.14.23", + "styled-components": "2.2.4" + } + }, "font-awesome": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", @@ -5940,8 +6087,7 @@ "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, "ignore": { "version": "3.3.7", @@ -5960,6 +6106,11 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" }, + "immutable-devtools": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/immutable-devtools/-/immutable-devtools-0.0.4.tgz", + "integrity": "sha1-Hn6H8sek8FM5VbxMKSLRJL+RKd0=" + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6262,6 +6413,11 @@ "number-is-nan": "1.0.1" } }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" + }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", @@ -6322,6 +6478,21 @@ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", "dev": true }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, "is-posix-bracket": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", @@ -7527,8 +7698,7 @@ "merge": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", - "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", - "dev": true + "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=" }, "merge-descriptors": { "version": "1.0.1", @@ -7637,6 +7807,16 @@ "minimist": "0.0.8" } }, + "moment": { + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.3.tgz", + "integrity": "sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=" + }, + "moment-duration-format": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/moment-duration-format/-/moment-duration-format-1.3.0.tgz", + "integrity": "sha1-VBdxtfh6BJzGVUBHXTrZZnN9aQg=" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -8342,6 +8522,19 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "pondjs": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/pondjs/-/pondjs-0.8.8.tgz", + "integrity": "sha1-b13kHP32U15xmM974ZmCQ/uUlHM=", + "requires": { + "babel-polyfill": "6.26.0", + "babel-runtime": "6.23.0", + "immutable": "3.8.2", + "immutable-devtools": "0.0.4", + "moment": "2.19.3", + "underscore": "1.8.3" + } + }, "portfinder": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", @@ -9606,8 +9799,7 @@ "postcss-value-parser": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" }, "postcss-zindex": { "version": "2.2.0", @@ -9726,7 +9918,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", "integrity": "sha1-SJZUxpJha4qlWwck+oCbt9tJxb8=", - "dev": true, "requires": { "asap": "2.0.6" } @@ -10608,6 +10799,58 @@ "tether": "1.4.0" } }, + "react-timeseries-charts": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/react-timeseries-charts/-/react-timeseries-charts-0.12.8.tgz", + "integrity": "sha1-SFS974LRKTyGJM9BXGvi4GbZOEU=", + "requires": { + "array.prototype.fill": "1.0.2", + "babel-runtime": "6.23.0", + "colorbrewer": "1.1.0", + "d3-axis": "1.0.8", + "d3-ease": "1.0.3", + "d3-format": "1.2.0", + "d3-interpolate": "1.1.5", + "d3-scale": "1.0.7", + "d3-scale-chromatic": "1.1.1", + "d3-selection": "1.2.0", + "d3-shape": "1.2.0", + "d3-time": "1.0.7", + "d3-time-format": "2.1.0", + "d3-transition": "1.1.1", + "flexbox-react": "4.4.0", + "invariant": "2.2.2", + "merge": "1.2.0", + "moment": "2.19.3", + "moment-duration-format": "1.3.0", + "prop-types": "15.6.0", + "underscore": "1.8.3" + }, + "dependencies": { + "d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "requires": { + "d3-array": "1.2.1", + "d3-collection": "1.0.4", + "d3-color": "1.0.3", + "d3-format": "1.2.0", + "d3-interpolate": "1.1.5", + "d3-time": "1.0.7", + "d3-time-format": "2.1.0" + } + }, + "d3-shape": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", + "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", + "requires": { + "d3-path": "1.0.5" + } + } + } + }, "react-tooltip": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-3.4.0.tgz", @@ -11895,6 +12138,65 @@ "loader-utils": "1.1.0" } }, + "styled-components": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-2.2.4.tgz", + "integrity": "sha512-GOIRYeVHBpPi7u+fYYV6HWP7AvdbvJkzmrgTJ31W9goRfc/vi5lBbKVm7K1nLogeR1Q0RfDjiEDWl1XsQtwtBw==", + "requires": { + "buffer": "5.0.8", + "css-to-react-native": "2.0.4", + "fbjs": "0.8.16", + "hoist-non-react-statics": "1.2.0", + "is-function": "1.0.1", + "is-plain-object": "2.0.4", + "prop-types": "15.6.0", + "stylis": "3.4.5", + "supports-color": "3.2.3" + }, + "dependencies": { + "buffer": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", + "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.1.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "stylis": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.4.5.tgz", + "integrity": "sha512-xxfO3FlxEKcNL1gTX4Tb/tyDLOlUcWCQopceIoQe7sBsX81Na83PNba7DFqMqgb9Rn1VjHkSAWdS9uhL/NVo+Q==" + }, "superagent": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.0.tgz", @@ -12448,6 +12750,11 @@ "integrity": "sha1-7Mo6A+VrmvFzhbqsgSrIO5lKli8=", "dev": true }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", diff --git a/package.json b/package.json index 7a20fd1bbcc27286355db43722c5334a43fa97ba..1c037f09c2836f5f5b39b61bc6a50735cf8bfac8 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "highcharts-3d": "^0.1.2", "leaflet": "^1.1.0", "lodash.debounce": "^4.0.8", + "pondjs": "^0.8.8", "prop-types": "^15.5.10", "react": "^15.6.1", "react-autocomplete": "^1.7.2", @@ -86,6 +87,7 @@ "react-router": "^3.0.0", "react-router-redux": "^4.0.7", "react-table": "^6.7.4", + "react-timeseries-charts": "^0.12.8", "react-tooltip": "^3.3.0", "reactstrap": "^4.8.0", "recharts": "^0.21.2", diff --git a/src/components/LinkBarDetail/index.js b/src/components/LinkBarDetail/index.js index e1ffa9b3510cb29894d56dfb6bc922c45d1853b2..6a8224d5cd2b4d199ccaade7ec36ff6126ddbfce 100644 --- a/src/components/LinkBarDetail/index.js +++ b/src/components/LinkBarDetail/index.js @@ -23,13 +23,25 @@ const generateBreadcrumbs = (rootURL, breadcrumbs) => { }; const generateLinks = (rootURL, links) => { - const linkList = links.map(val => ( - ( - + const linkList = links.map((val) => { + if (val.internalLink) { + return ( + + {val.name} + + ); + } + return ( + {val.name} - ) - )); + ); + }); return linkList; }; diff --git a/src/containers/Sensors/SensorGraph.js b/src/containers/Sensors/SensorGraph.js new file mode 100644 index 0000000000000000000000000000000000000000..659acee18550cc8d4618a7aff4f2c3960796e099 --- /dev/null +++ b/src/containers/Sensors/SensorGraph.js @@ -0,0 +1,242 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import debounce from 'lodash.debounce'; +import { + Charts, + ChartContainer, + ChartRow, + YAxis, + LineChart, + Baseline, + styler, + Legend, +} from 'react-timeseries-charts'; +import { TimeSeries, TimeRange } from 'pondjs'; +import { loadAllNodeData } from './actions'; +import { subtractDaysFromNow } from '../../utils/date'; +import { GRAPH_COLORS } from './colors'; + + +class Sensors extends Component { + NUM_DAYS = 4; // eslint-disable-line + + constructor(props) { + super(props); + const today = new Date(); + + this.state = { + timeseries: new TimeSeries(), + timerange: new TimeRange([subtractDaysFromNow(this.NUM_DAYS - 1), today]), + showChart: false, + from: subtractDaysFromNow(this.NUM_DAYS), + today, + lineStyles: {}, + }; + } + + componentDidMount() { + this.props.loadAllNodeData({ + building_id: this.props.buildingId, + nodes: '', + data: '', + from: this.state.from.toUTCString(), + }); + } + + componentWillReceiveProps(nextProps) { + if (nextProps === this.props) { + return; + } + this.generateData(nextProps); + } + + sensorDataLoading = (props) => ( + props.sensors.loading || props.sensors.sensorData.length === 0 + ) + + generateData = (props) => { + if (this.sensorDataLoading(props)) { + return; + } + + const { sensorData } = props.sensors; + let nodes = []; + sensorData.forEach(gateway => { + nodes = [...nodes, ...gateway.nodes.map(oneNode => this.generateTimeSeries(oneNode))]; + }); + + if (nodes.length === 0) { + return; + } + + const styles = {}; + nodes.forEach((line, index) => { + styles[line.name()] = this.generateLineStyle(line, index); + }); + + this.setState({ + timeseries: nodes, + showChart: true, + lineStyles: styles, + cat: this.generateCategories(nodes), + }); + } + + generateTimeSeries = (sensorNode) => { + const points = sensorNode.data.reduce((acc, i) => { + if (i.name === 'Temperature') { + acc.push([new Date(i.ts), i.value]); + } + + return acc; + }, []); + + return new TimeSeries({ + name: `node ${sensorNode.id}`, + columns: ['time', `node ${sensorNode.id}`], + points, + }); + } + + generateLineStyle = (line, index) => ({ + key: line.name(), + color: GRAPH_COLORS[index % GRAPH_COLORS.length], + width: 1, + }) + + generateCategories = (nodes) => { + const stuff = nodes.map(i => ({ key: i.name(), label: i.name() })); + return stuff; + } + + fetchData = debounce((from) => { + this.props.loadAllNodeData({ + building_id: this.props.buildingId, + nodes: '', + data: '', + from: from.toUTCString(), + }); + }, 500); + + updateTimeRange = (timerange) => { + if (!this.state.showChart || this.state.timeseries.length === 0) { + return; + } + + const endDate = timerange.end() > this.state.today ? this.state.today : timerange.end(); + + if (timerange.begin() < this.state.timeseries[0].begin()) { + this.fetchData(timerange.begin()); + } + + this.setState({ + timerange: new TimeRange([timerange.begin(), endDate]), + from: timerange.begin(), + }); + } + + renderLineGraph = () => { + if (!this.state.showChart) { + return ( + + ); + } + + const lineGraph = this.state.timeseries.map(line => ( + + )); + + return lineGraph; + } + + render() { + return ( +
+
+
+ {this.state.showChart ? + ({ key: i.name(), label: i.name() }))} + style={styler(Object.keys(this.state.lineStyles).map(key => ( + this.state.lineStyles[key])) + )} + type="line" + /> : + null + } +
+
+
+
+ this.setState({ tracker })} + onTimeRangeChanged={timerange => this.updateTimeRange(timerange)} + enablePanZoom + > + + + + {this.renderLineGraph()} + + + + +
+
+
+ ); + } +} + +Sensors.propTypes = { + buildingId: PropTypes.string, + className: PropTypes.string, + loadAllNodeData: PropTypes.func, + sensors: PropTypes.object, // eslint-disable-line +}; + +Sensors.defaultProps = { + className: '', +}; + +const mapStateToProps = state => ( + { + sensors: state.sensors, + } +); + +const mapDispatchToProps = dispatch => ( + bindActionCreators({ + loadAllNodeData, + }, dispatch) +); + +export default connect(mapStateToProps, mapDispatchToProps)(Sensors); diff --git a/src/containers/Sensors/Sensors.js b/src/containers/Sensors/Sensors.js index 1e2839d215c939c2759460b4ebff0366a453a50c..054decfe6d1eec00c95a77307d90cfbe2969468f 100644 --- a/src/containers/Sensors/Sensors.js +++ b/src/containers/Sensors/Sensors.js @@ -1,8 +1,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { Link } from 'react-router'; import LinkBarDetail from '../../components/LinkBarDetail'; +import SensorGraph from './SensorGraph'; class Sensors extends Component { @@ -17,15 +17,17 @@ class Sensors extends Component { { name: 'Building Overview', url: '' }, { name: 'Sensors', url: 'null' }, ]} - links={[]} + links={[{ + name: 'Install', + url: `/buildings/${this.props.buildingId}/sensors/install`, + tags: '', + internalLink: true, + }]} + /> + -
-
-
- Install -
-
-
); } diff --git a/src/containers/Sensors/actions.js b/src/containers/Sensors/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..9f80b02cdf509c9822b33f6c5e017e769cfc6ad6 --- /dev/null +++ b/src/containers/Sensors/actions.js @@ -0,0 +1,10 @@ +import { makeActionCreator } from '../../utils/reduxHelpers'; +import { + FETCH_ALL_NODE_DATA_REQUESTED, + FETCH_ALL_NODE_DATA_SUCCEEDED, + FETCH_ALL_NODE_DATA_FAILED, +} from './constants'; + +export const loadAllNodeData = makeActionCreator(FETCH_ALL_NODE_DATA_REQUESTED, 'filters'); +export const allNodeDataLoaded = makeActionCreator(FETCH_ALL_NODE_DATA_SUCCEEDED, 'payload'); +export const allNodeDataFailed = makeActionCreator(FETCH_ALL_NODE_DATA_FAILED, 'error'); diff --git a/src/containers/Sensors/colors.js b/src/containers/Sensors/colors.js new file mode 100644 index 0000000000000000000000000000000000000000..a6a2cd825e1b538b6150dd8239791ded817c3682 --- /dev/null +++ b/src/containers/Sensors/colors.js @@ -0,0 +1,11 @@ +export const GRAPH_COLORS = [ + '#4D4D4D', // (gray) + '#5DA5DA', // (blue) + '#FAA43A', // (orange) + '#60BD68', // (green) + '#F17CB0', // (pink) + '#B2912F', // (brown) + '#B276B2', // (purple) + '#DECF3F', // (yellow) + '#F15854', // (red) +]; diff --git a/src/containers/Sensors/constants.js b/src/containers/Sensors/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..71b4be66fbdd99e0098de757c54ff74d23de985e --- /dev/null +++ b/src/containers/Sensors/constants.js @@ -0,0 +1,3 @@ +export const FETCH_ALL_NODE_DATA_REQUESTED = 'FETCH_ALL_NODE_DATA_REQUESTED'; +export const FETCH_ALL_NODE_DATA_SUCCEEDED = 'FETCH_ALL_NODE_DATA_SUCCEEDED'; +export const FETCH_ALL_NODE_DATA_FAILED = 'FETCH_ALL_NODE_DATA_FAILED'; diff --git a/src/containers/Sensors/reducer.js b/src/containers/Sensors/reducer.js new file mode 100644 index 0000000000000000000000000000000000000000..3c9b16c2850b50bd6bca192973948eb8bc33ce01 --- /dev/null +++ b/src/containers/Sensors/reducer.js @@ -0,0 +1,42 @@ +import { + FETCH_ALL_NODE_DATA_REQUESTED, + FETCH_ALL_NODE_DATA_SUCCEEDED, + FETCH_ALL_NODE_DATA_FAILED, +} from './constants'; + +const sensorInitialState = { + loading: false, + error: false, + sensorData: [], +}; + +export default (state = sensorInitialState, action) => { + switch (action.type) { + case FETCH_ALL_NODE_DATA_REQUESTED: + return { + ...state, + loading: true, + error: false, + sensorData: [], + }; + + case FETCH_ALL_NODE_DATA_SUCCEEDED: + return { + ...state, + loading: false, + error: false, + sensorData: action.payload.data, + }; + + case FETCH_ALL_NODE_DATA_FAILED: + return { + ...state, + loading: false, + error: action.error, + sensorData: [], + }; + + default: + return state; + } +}; diff --git a/src/containers/Sensors/sagas.js b/src/containers/Sensors/sagas.js new file mode 100644 index 0000000000000000000000000000000000000000..ca6398fae1d5944ccf8418368c44691aa0c27052 --- /dev/null +++ b/src/containers/Sensors/sagas.js @@ -0,0 +1,23 @@ +import { takeEvery } from 'redux-saga/effects'; +import { SagaRequests } from '../../utils'; +import { gatewayURL } from '../../utils/restServices'; + +import { + FETCH_ALL_NODE_DATA_REQUESTED, +} from './constants'; + +import { + allNodeDataLoaded, + allNodeDataFailed, +} from './actions'; + + +function* fetchAllData(action) { + yield SagaRequests.get(action, gatewayURL, allNodeDataLoaded, allNodeDataFailed); +} + +function* sensorWatcher() { + yield takeEvery(FETCH_ALL_NODE_DATA_REQUESTED, fetchAllData); +} + +export default sensorWatcher; diff --git a/src/reducers.js b/src/reducers.js index 0998932d839d962e71120797cdfe2db326adb572..b259a6f089e441aadb81403612194375b17e6c5f 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -12,6 +12,8 @@ import buildingReport from './containers/BuildingReports/reducer'; import report from './containers/Reports/reducer'; import bGroup from './containers/BGroup/reducer'; import user from './containers/User/reducer'; +import sensors from './containers/Sensors/reducer'; + export default combineReducers({ routing: routerReducer, @@ -26,4 +28,5 @@ export default combineReducers({ report, bGroup, user, + sensors, }); diff --git a/src/sagas.js b/src/sagas.js index dd5e50b98763380079d470cd8a80a2c87c5126ba..448d59cb5acbf47e640d2bd12c09353ec68adfc9 100644 --- a/src/sagas.js +++ b/src/sagas.js @@ -9,6 +9,8 @@ import envelopeSaga from './containers/Envelope/sagas'; import buildingReportsSaga from './containers/BuildingReports/sagas'; import bGroupSaga from './containers/BGroup/sagas'; import userSaga from './containers/User/sagas'; +import sensorsSaga from './containers/Sensors/sagas'; + export default function* rootSaga() { yield [ @@ -23,5 +25,6 @@ export default function* rootSaga() { buildingReportsSaga(), bGroupSaga(), userSaga(), + sensorsSaga(), ]; } diff --git a/src/utils/date.js b/src/utils/date.js new file mode 100644 index 0000000000000000000000000000000000000000..ca35e653fb91c4c023ecd110594291d7ee387c0b --- /dev/null +++ b/src/utils/date.js @@ -0,0 +1,5 @@ +export function subtractDaysFromNow(numDays) { + const d = new Date(); + d.setDate(d.getDate() - numDays); + return d; +} diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000000000000000000000000000000000000..9ed882c652484000b833581d824679ebc4964e60 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,11 @@ +import { makeActionCreator } from './reduxHelpers'; +import SagaRequests from './sagaRequests'; +import request from './request'; +import { getHeaders } from './restServices'; + +export { + makeActionCreator, + SagaRequests, + request, + getHeaders, +}; diff --git a/src/utils/restServices.js b/src/utils/restServices.js index a32cf205c86e7011387ae7515a18de3f789959f6..836e3dbefd585368b350929f33c47d66ef284ad9 100644 --- a/src/utils/restServices.js +++ b/src/utils/restServices.js @@ -14,6 +14,7 @@ const iotService = process.env.REACT_APP_IOT_SERVICE; const userService = process.env.REACT_APP_USER_SERVICE; export const buildingsURL = `${buildingService}/building/`; + export const turkURL = `${buildingService}/turkhit/`; export const documentURL = `${documentService}/document/`; export const folderURL = `${documentService}/folder/`; @@ -22,15 +23,20 @@ export const projectFolderURL = `${documentService}/projectfolder/`; export const noteURL = `${documentService}/note/`; export const structureURL = `${documentService}/structure/`; export const boxAccessTokenURL = `${documentService}/boxusertoken/`; + export const accountURL = `${utilityService}/account/`; export const scrapeURL = `${utilityService}/scrape/`; export const disaggregateURL = `${utilityService}/disaggregate/`; + export const projectURL = `${projectService}/project/`; export const contactsURL = `${projectService}/contact/`; + export const projectDocumentURL = `${projectService}/project/document/`; export const lightingReportURL = `${reportService}/kissflowlighting/`; + export const gatewayURL = `${iotService}/gateway/`; export const sensewareNodeURL = `${iotService}/sensewarenode/`; export const sensorImageURL = `${iotService}/sensorimage/`; + export const userURL = `${userService}/user/`; export const userGroupsURL = `${userService}/usergroup/`;