Add restart service for esp8266 and esp32
Add restart feature to status screen Upgrade material-ui Add icons to buttons
This commit is contained in:
		
							
								
								
									
										188
									
								
								interface/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										188
									
								
								interface/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1184,21 +1184,19 @@ | ||||
|       } | ||||
|     }, | ||||
|     "@material-ui/core": { | ||||
|       "version": "4.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.4.3.tgz", | ||||
|       "integrity": "sha512-Lz8sMFeCrtq5/pbhqClWFHpveL0huixjca0tw7uvh9xKKB7VyyYOyTu7RamSZLxb34UCSMPlobR+KK25Nqzkqw==", | ||||
|       "version": "4.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.7.0.tgz", | ||||
|       "integrity": "sha512-mwLehUo0Q9ZxjuWo7J1uy1/Grh3nRxlOAaWJ3EtKeJP2HwqlSy8bWrcvRQYlapaYIPXa5jN8zWbTwi8Pk30VQg==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.4.4", | ||||
|         "@material-ui/styles": "^4.4.3", | ||||
|         "@material-ui/system": "^4.4.3", | ||||
|         "@material-ui/styles": "^4.6.0", | ||||
|         "@material-ui/system": "^4.5.2", | ||||
|         "@material-ui/types": "^4.1.1", | ||||
|         "@material-ui/utils": "^4.4.0", | ||||
|         "@material-ui/utils": "^4.5.2", | ||||
|         "@types/react-transition-group": "^4.2.0", | ||||
|         "clsx": "^1.0.2", | ||||
|         "convert-css-length": "^2.0.1", | ||||
|         "deepmerge": "^4.0.0", | ||||
|         "hoist-non-react-statics": "^3.2.1", | ||||
|         "is-plain-object": "^3.0.0", | ||||
|         "normalize-scroll-left": "^0.2.0", | ||||
|         "popper.js": "^1.14.1", | ||||
|         "prop-types": "^15.7.2", | ||||
| @@ -1206,44 +1204,43 @@ | ||||
|       } | ||||
|     }, | ||||
|     "@material-ui/icons": { | ||||
|       "version": "4.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.4.3.tgz", | ||||
|       "integrity": "sha512-HVVvUyc/78kmaBd93LkfWyGkXMM+zOMKzUfulWXxaV/fFAZ3N0pD0oHjWUd94zrOoF3tZP9JC7EPlIpIcZSNow==", | ||||
|       "version": "4.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.5.1.tgz", | ||||
|       "integrity": "sha512-YZ/BgJbXX4a0gOuKWb30mBaHaoXRqPanlePam83JQPZ/y4kl+3aW0Wv9tlR70hB5EGAkEJGW5m4ktJwMgxQAeA==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.4.4" | ||||
|       } | ||||
|     }, | ||||
|     "@material-ui/styles": { | ||||
|       "version": "4.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.4.3.tgz", | ||||
|       "integrity": "sha512-kNUdHFWsrvWKIEPx8Xy2/qayqsGMrYmCMq+FIiJiYczVZl5hiS8j5+KayonnpVta/O+Dktk+cxWkVcgwtxMrHg==", | ||||
|       "version": "4.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.6.0.tgz", | ||||
|       "integrity": "sha512-lqqh4UEMdIYcU1Yth4pQyMTah02uAkg3NOT3MirN9FUexdL8pNA6zCHigEgDSfwmvnXyxHhxTkphfy0DRfnt9w==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.4.4", | ||||
|         "@emotion/hash": "^0.7.1", | ||||
|         "@material-ui/types": "^4.1.1", | ||||
|         "@material-ui/utils": "^4.1.0", | ||||
|         "@material-ui/utils": "^4.5.2", | ||||
|         "clsx": "^1.0.2", | ||||
|         "csstype": "^2.5.2", | ||||
|         "deepmerge": "^4.0.0", | ||||
|         "hoist-non-react-statics": "^3.2.1", | ||||
|         "jss": "10.0.0-alpha.25", | ||||
|         "jss-plugin-camel-case": "10.0.0-alpha.25", | ||||
|         "jss-plugin-default-unit": "10.0.0-alpha.25", | ||||
|         "jss-plugin-global": "10.0.0-alpha.25", | ||||
|         "jss-plugin-nested": "10.0.0-alpha.25", | ||||
|         "jss-plugin-props-sort": "10.0.0-alpha.25", | ||||
|         "jss-plugin-rule-value-function": "10.0.0-alpha.25", | ||||
|         "jss-plugin-vendor-prefixer": "10.0.0-alpha.25", | ||||
|         "jss": "^10.0.0", | ||||
|         "jss-plugin-camel-case": "^10.0.0", | ||||
|         "jss-plugin-default-unit": "^10.0.0", | ||||
|         "jss-plugin-global": "^10.0.0", | ||||
|         "jss-plugin-nested": "^10.0.0", | ||||
|         "jss-plugin-props-sort": "^10.0.0", | ||||
|         "jss-plugin-rule-value-function": "^10.0.0", | ||||
|         "jss-plugin-vendor-prefixer": "^10.0.0", | ||||
|         "prop-types": "^15.7.2" | ||||
|       } | ||||
|     }, | ||||
|     "@material-ui/system": { | ||||
|       "version": "4.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.4.3.tgz", | ||||
|       "integrity": "sha512-Cb05vLXsaCzssXD/iZKa0/qC6YOwbFWnYdnOEdkXZ3Fn2Ytz7rsnMgFejUSQV1luVhUBlEIm8DVz40N25WwW7w==", | ||||
|       "version": "4.5.2", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.5.2.tgz", | ||||
|       "integrity": "sha512-h9RWvdM9XKlHHqwiuhyvWdobptQkHli+m2jJFs7i1AI/hmGsIc4reDmS7fInhETgt/Txx7uiAIznfRNIIVHmQw==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.4.4", | ||||
|         "deepmerge": "^4.0.0", | ||||
|         "@material-ui/utils": "^4.5.2", | ||||
|         "prop-types": "^15.7.2" | ||||
|       } | ||||
|     }, | ||||
| @@ -1256,9 +1253,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "@material-ui/utils": { | ||||
|       "version": "4.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.4.0.tgz", | ||||
|       "integrity": "sha512-UXoQVwArQEQWXxf2FPs0iJGT+MePQpKr0Qh0CPoLc1OdF0GSMTmQczcqCzwZkeHxHAOq/NkIKM1Pb/ih1Avicg==", | ||||
|       "version": "4.5.2", | ||||
|       "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.5.2.tgz", | ||||
|       "integrity": "sha512-zhbNfHd1gLa8At6RPDG7uMZubHxbY+LtM6IkSfeWi6Lo4Ax80l62YaN1QmUpO1IvGCkn/j62tQX3yObiQZrJsQ==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.4.4", | ||||
|         "prop-types": "^15.7.2", | ||||
| @@ -1486,18 +1483,18 @@ | ||||
|       "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" | ||||
|     }, | ||||
|     "@types/react": { | ||||
|       "version": "16.9.4", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.4.tgz", | ||||
|       "integrity": "sha512-ItGNmJvQ0IvWt8rbk5PLdpdQhvBVxAaXI9hDlx7UMd8Ie1iMIuwMNiKeTfmVN517CdplpyXvA22X4zm4jGGZnw==", | ||||
|       "version": "16.9.13", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.13.tgz", | ||||
|       "integrity": "sha512-LikzRslbiufJYHyzbHSW0GrAiff8QYLMBFeZmSxzCYGXKxi8m/1PHX+rsVOwhr7mJNq+VIu2Dhf7U6mjFERK6w==", | ||||
|       "requires": { | ||||
|         "@types/prop-types": "*", | ||||
|         "csstype": "^2.2.0" | ||||
|       } | ||||
|     }, | ||||
|     "@types/react-transition-group": { | ||||
|       "version": "4.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.2.tgz", | ||||
|       "integrity": "sha512-YfoaTNqBwbIqpiJ5NNfxfgg5kyFP1Hqf/jqBtSWNv0E+EkkxmN+3VD6U2fu86tlQvdAc1o0SdWhnWFwcRMTn9A==", | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.3.tgz", | ||||
|       "integrity": "sha512-Hk8jiuT7iLOHrcjKP/ZVSyCNXK73wJAUz60xm0mVhiRujrdiI++j4duLiL282VGxwAgxetHQFfqA29LgEeSkFA==", | ||||
|       "requires": { | ||||
|         "@types/react": "*" | ||||
|       } | ||||
| @@ -4210,11 +4207,6 @@ | ||||
|       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", | ||||
|       "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" | ||||
|     }, | ||||
|     "deepmerge": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.0.0.tgz", | ||||
|       "integrity": "sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww==" | ||||
|     }, | ||||
|     "default-gateway": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", | ||||
| @@ -4439,12 +4431,27 @@ | ||||
|       } | ||||
|     }, | ||||
|     "dom-helpers": { | ||||
|       "version": "5.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.0.tgz", | ||||
|       "integrity": "sha512-zRRYDhpiKuAJHasOqCm7lBnsd22nrM4+OYI4ASWCxen+ocTMl7BIAKgGag97TlLiTl6rrau5aPe1VGUm9jQBng==", | ||||
|       "version": "5.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz", | ||||
|       "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.5.5", | ||||
|         "csstype": "^2.6.6" | ||||
|         "@babel/runtime": "^7.6.3", | ||||
|         "csstype": "^2.6.7" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "@babel/runtime": { | ||||
|           "version": "7.7.4", | ||||
|           "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz", | ||||
|           "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==", | ||||
|           "requires": { | ||||
|             "regenerator-runtime": "^0.13.2" | ||||
|           } | ||||
|         }, | ||||
|         "csstype": { | ||||
|           "version": "2.6.7", | ||||
|           "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", | ||||
|           "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "dom-serializer": { | ||||
| @@ -6548,14 +6555,6 @@ | ||||
|         "path-is-inside": "^1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "is-plain-object": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", | ||||
|       "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", | ||||
|       "requires": { | ||||
|         "isobject": "^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "is-promise": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", | ||||
| @@ -6630,11 +6629,6 @@ | ||||
|       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", | ||||
|       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" | ||||
|     }, | ||||
|     "isobject": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", | ||||
|       "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" | ||||
|     }, | ||||
|     "isstream": { | ||||
|       "version": "0.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", | ||||
| @@ -7889,9 +7883,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "jss": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-zqKnXv181B9vue2yYhmVhc+6ggbbxHF/33rjXfXEjaa22nOvknTI21QDfq3oZ8uCC50kcFp3Z8KU1ghUXdFvIA==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0.tgz", | ||||
|       "integrity": "sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "csstype": "^2.6.5", | ||||
| @@ -7900,13 +7894,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-camel-case": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-J5ZEGDTy9ddqdTUPAF4SJQ25u5kiG1ORP8F+ZPEZAkkiMQJp+/Aol4I7xhTS2aW1Lhg8xNxdhdRfBi5yU7wOvg==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz", | ||||
|       "integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "hyphenate-style-name": "^1.0.3", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-compose": { | ||||
| @@ -7933,12 +7927,12 @@ | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-default-unit": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-auOG459B+yEqkojgaXH02SYO9+xjmAxlmP+WbzhVpXqOFJ2CN/kaxd8P4NJZLdj3BQxHiM7WIyMVh786StE+EA==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz", | ||||
|       "integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-expand": { | ||||
| @@ -7987,40 +7981,40 @@ | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-global": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-cS98Q8X8jwltuaBZd9eYuxMXxkUL+mJGl2Ok3/nmJzH9nLzj6i7kLxSoDtuJNqsRmbP7ogIXVozJUq9lUu2hlQ==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz", | ||||
|       "integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-nested": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-7sk7/6mX1YTgXe+AyeD1zEyKTgIGbbhYtg+wWQcHJlE1flW2JHfcQ5mw84FgHcHQRQ8Dq3l9I3aEY51ev0J1Wg==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz", | ||||
|       "integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "jss": "10.0.0-alpha.25", | ||||
|         "jss": "10.0.0", | ||||
|         "tiny-warning": "^1.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-props-sort": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-8B/6QLQuUX8cIlZbXdjEm5l0jCX4EgacYMcFJhdKwDKEZYeAghpgQQrCKl0/CYHW7iFge5wim67P+uL6QxMzyw==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz", | ||||
|       "integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-rule-value-function": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-CQQtWO+/OZRGaFRBSGQUgAci9YlVtdoXcWQKBNo70tmpp+kaXKlFNCYaL3jmHbJHMiwKQYG2RYFQNIrwJ9SGmA==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz", | ||||
|       "integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-rule-value-observable": { | ||||
| @@ -8070,13 +8064,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "jss-plugin-vendor-prefixer": { | ||||
|       "version": "10.0.0-alpha.25", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.25.tgz", | ||||
|       "integrity": "sha512-5FXpB/TiwckbrkoDCmd27YsWCESl1K4hAX/oro2/geEXgnVQvDgQOf2eWCsjYO2K1lYPPXtskMfws/Q3eKmbYg==", | ||||
|       "version": "10.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz", | ||||
|       "integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==", | ||||
|       "requires": { | ||||
|         "@babel/runtime": "^7.3.1", | ||||
|         "css-vendor": "^2.0.6", | ||||
|         "jss": "10.0.0-alpha.25" | ||||
|         "jss": "10.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "jss-preset-default": { | ||||
| @@ -8940,9 +8934,9 @@ | ||||
|       "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==" | ||||
|     }, | ||||
|     "notistack": { | ||||
|       "version": "0.8.9", | ||||
|       "resolved": "https://registry.npmjs.org/notistack/-/notistack-0.8.9.tgz", | ||||
|       "integrity": "sha512-nRHQVWUfgHnvnKrjRbRX9f+YAnbyh96yRyO5bEP/FCLVLuTZcJOwUr0GZ7Xr/8wK3+hXa9JYpXUkUhSxj1K8NQ==", | ||||
|       "version": "0.9.6", | ||||
|       "resolved": "https://registry.npmjs.org/notistack/-/notistack-0.9.6.tgz", | ||||
|       "integrity": "sha512-vo1zOwhQBxwWiMxwVjeSDXNzJuaM/nfkayv4uRo+9ON9CAtaPSNt15QHeELKkbOSLH29fb7zmoZl4AlkCqhGsA==", | ||||
|       "requires": { | ||||
|         "classnames": "^2.2.6", | ||||
|         "hoist-non-react-statics": "^3.3.0", | ||||
| @@ -9526,9 +9520,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "popper.js": { | ||||
|       "version": "1.15.0", | ||||
|       "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", | ||||
|       "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" | ||||
|       "version": "1.16.0", | ||||
|       "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz", | ||||
|       "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw==" | ||||
|     }, | ||||
|     "portfinder": { | ||||
|       "version": "1.0.24", | ||||
|   | ||||
| @@ -3,12 +3,12 @@ | ||||
|   "version": "0.1.0", | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|     "@material-ui/core": "^4.4.3", | ||||
|     "@material-ui/icons": "^4.4.3", | ||||
|     "@material-ui/core": "^4.7.0", | ||||
|     "@material-ui/icons": "^4.5.1", | ||||
|     "compression-webpack-plugin": "^2.0.0", | ||||
|     "jwt-decode": "^2.2.0", | ||||
|     "moment": "^2.24.0", | ||||
|     "notistack": "^0.8.9", | ||||
|     "notistack": "^0.9.6", | ||||
|     "prop-types": "^15.7.2", | ||||
|     "react": "^16.10.1", | ||||
|     "react-dom": "^16.10.1", | ||||
|   | ||||
| @@ -2,22 +2,16 @@ import React, { Component } from 'react'; | ||||
| import { Redirect, Route, Switch } from 'react-router'; | ||||
|  | ||||
| import AppRouting from './AppRouting'; | ||||
| import { PROJECT_NAME } from './constants/Env'; | ||||
|  | ||||
| import { SnackbarProvider } from 'notistack'; | ||||
|  | ||||
| import CssBaseline from '@material-ui/core/CssBaseline'; | ||||
| import blueGrey from '@material-ui/core/colors/blueGrey'; | ||||
| import indigo from '@material-ui/core/colors/indigo'; | ||||
| import orange from '@material-ui/core/colors/orange'; | ||||
| import red from '@material-ui/core/colors/red'; | ||||
| import green from '@material-ui/core/colors/green'; | ||||
|  | ||||
| import { create } from 'jss'; | ||||
| import { StylesProvider, jssPreset } from '@material-ui/styles'; | ||||
|  | ||||
| import { | ||||
|   MuiThemeProvider, | ||||
|   createMuiTheme | ||||
| } from '@material-ui/core/styles'; | ||||
| import { CssBaseline, IconButton, MuiThemeProvider, createMuiTheme } from '@material-ui/core'; | ||||
| import { StylesProvider, jssPreset } from '@material-ui/styles'; | ||||
| import { blueGrey, indigo, orange, red, green } from '@material-ui/core/colors'; | ||||
| import CloseIcon from '@material-ui/icons/Close'; | ||||
|  | ||||
|  | ||||
| // Our theme | ||||
| const theme = createMuiTheme({ | ||||
| @@ -38,11 +32,28 @@ const jss = create(jssPreset()); | ||||
| const unauthorizedRedirect = () => <Redirect to="/" />; | ||||
|  | ||||
| class App extends Component { | ||||
|  | ||||
|   notistackRef = React.createRef(); | ||||
|  | ||||
|   componentDidMount() { | ||||
|     document.title = PROJECT_NAME; | ||||
|   } | ||||
|  | ||||
|   onClickDismiss = (key) => () => { | ||||
|     this.notistackRef.current.closeSnackbar(key); | ||||
|   } | ||||
|  | ||||
|   render() { | ||||
|     return ( | ||||
|       <StylesProvider jss={jss}> | ||||
|         <MuiThemeProvider theme={theme}> | ||||
|           <SnackbarProvider maxSnack={3}> | ||||
|           <SnackbarProvider maxSnack={3} anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} | ||||
|             ref={this.notistackRef} | ||||
|             action={(key) => ( | ||||
|               <IconButton onClick={this.onClickDismiss(key)} size="small"> | ||||
|                 <CloseIcon /> | ||||
|               </IconButton> | ||||
|             )}> | ||||
|             <CssBaseline /> | ||||
|             <Switch> | ||||
|               <Route exact path="/unauthorized" component={unauthorizedRedirect} /> | ||||
|   | ||||
| @@ -13,3 +13,4 @@ export const SYSTEM_STATUS_ENDPOINT = ENDPOINT_ROOT + "systemStatus"; | ||||
| export const SIGN_IN_ENDPOINT = ENDPOINT_ROOT + "signIn"; | ||||
| export const VERIFY_AUTHORIZATION_ENDPOINT = ENDPOINT_ROOT + "verifyAuthorization"; | ||||
| export const SECURITY_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "securitySettings"; | ||||
| export const RESTART_ENDPOINT = ENDPOINT_ROOT + "restart"; | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import Divider from '@material-ui/core/Divider'; | ||||
| import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; | ||||
| import DeviceHubIcon from '@material-ui/icons/DeviceHub'; | ||||
| import ComputerIcon from '@material-ui/icons/Computer'; | ||||
| import RefreshIcon from '@material-ui/icons/Refresh'; | ||||
|  | ||||
| import { restComponent } from '../components/RestComponent'; | ||||
| import LoadingNotification from '../components/LoadingNotification'; | ||||
| @@ -93,7 +94,7 @@ class APStatus extends Component { | ||||
|         <List> | ||||
|           {this.createListItems(data, classes)} | ||||
|         </List> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|         <Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|           Refresh | ||||
|         </Button> | ||||
|       </div> | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import DNSIcon from '@material-ui/icons/Dns'; | ||||
| import TimerIcon from '@material-ui/icons/Timer'; | ||||
| import UpdateIcon from '@material-ui/icons/Update'; | ||||
| import AvTimerIcon from '@material-ui/icons/AvTimer'; | ||||
| import RefreshIcon from '@material-ui/icons/Refresh'; | ||||
|  | ||||
| import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus'; | ||||
| import * as Highlight from '../constants/Highlight'; | ||||
| @@ -118,7 +119,7 @@ class NTPStatus extends Component { | ||||
|         <List> | ||||
|           {this.createListItems(data, classes)} | ||||
|         </List> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|         <Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|           Refresh | ||||
|         </Button> | ||||
|       </div> | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import React, { Component, Fragment } from 'react'; | ||||
| import { withSnackbar } from 'notistack'; | ||||
|  | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| @@ -8,16 +9,24 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar'; | ||||
| import ListItemText from '@material-ui/core/ListItemText'; | ||||
| import Avatar from '@material-ui/core/Avatar'; | ||||
| import Divider from '@material-ui/core/Divider'; | ||||
| import Dialog from '@material-ui/core/Dialog'; | ||||
| import DialogActions from '@material-ui/core/DialogActions'; | ||||
| import DialogTitle from '@material-ui/core/DialogTitle'; | ||||
| import DialogContent from '@material-ui/core/DialogContent'; | ||||
|  | ||||
| import DevicesIcon from '@material-ui/icons/Devices'; | ||||
| import MemoryIcon from '@material-ui/icons/Memory'; | ||||
| import ShowChartIcon from '@material-ui/icons/ShowChart'; | ||||
| import SdStorageIcon from '@material-ui/icons/SdStorage'; | ||||
| import DataUsageIcon from '@material-ui/icons/DataUsage'; | ||||
| import AutorenewIcon from '@material-ui/icons/Autorenew'; | ||||
| import RefreshIcon from '@material-ui/icons/Refresh'; | ||||
|  | ||||
| import { SYSTEM_STATUS_ENDPOINT } from '../constants/Endpoints'; | ||||
| import { SYSTEM_STATUS_ENDPOINT, RESTART_ENDPOINT } from '../constants/Endpoints'; | ||||
| import { restComponent } from '../components/RestComponent'; | ||||
| import LoadingNotification from '../components/LoadingNotification'; | ||||
| import SectionContent from '../components/SectionContent'; | ||||
| import { redirectingAuthorizedFetch } from '../authentication/Authentication'; | ||||
|  | ||||
| const styles = theme => ({ | ||||
|   button: { | ||||
| @@ -28,6 +37,16 @@ const styles = theme => ({ | ||||
|  | ||||
| class SystemStatus extends Component { | ||||
|  | ||||
|  | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
|  | ||||
|     this.state = { | ||||
|       confirmRestart: false, | ||||
|       processing: false | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   componentDidMount() { | ||||
|     this.props.loadData(); | ||||
|   } | ||||
| @@ -90,13 +109,63 @@ class SystemStatus extends Component { | ||||
|         <List> | ||||
|           {this.createListItems(data, classes)} | ||||
|         </List> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|         <Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|           Refresh | ||||
|         </Button> | ||||
|         <Button startIcon={<AutorenewIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.onRestart}> | ||||
|           Restart | ||||
|         </Button> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   onRestart = () => { | ||||
|     this.setState({ confirmRestart: true }); | ||||
|   } | ||||
|  | ||||
|   onRestartRejected = () => { | ||||
|     this.setState({ confirmRestart: false }); | ||||
|   } | ||||
|  | ||||
|   onRestartConfirmed = () => { | ||||
|     this.setState({ processing: true }); | ||||
|     redirectingAuthorizedFetch(RESTART_ENDPOINT, { method: 'POST' }) | ||||
|       .then(response => { | ||||
|         if (response.status === 200) { | ||||
|           this.props.enqueueSnackbar("Device is restarting", { variant: 'info' }); | ||||
|           this.setState({ processing: false, confirmRestart: false }); | ||||
|         } else { | ||||
|           throw Error("Invalid status code: " + response.status); | ||||
|         } | ||||
|       }) | ||||
|       .catch(error => { | ||||
|         this.props.enqueueSnackbar(error.message || "Problem restarting device", { variant: 'error' }); | ||||
|         this.setState({ processing: false, confirmRestart: false }); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   renderRestartDialog() { | ||||
|     return ( | ||||
|       <Dialog | ||||
|         open={this.state.confirmRestart} | ||||
|         onClose={this.onRestartRejected} | ||||
|       > | ||||
|         <DialogTitle>Confirm Restart</DialogTitle> | ||||
|         <DialogContent dividers={true}> | ||||
|           Are you sure you want to restart the device? | ||||
|         </DialogContent> | ||||
|         <DialogActions> | ||||
|           <Button startIcon={<AutorenewIcon />} variant="contained" onClick={this.onRestartConfirmed} disabled={this.state.processing} color="primary" autoFocus> | ||||
|             Restart | ||||
|           </Button> | ||||
|           <Button variant="contained" onClick={this.onRestartRejected} color="secondary"> | ||||
|             Cancel | ||||
|           </Button> | ||||
|         </DialogActions> | ||||
|       </Dialog> | ||||
|     ) | ||||
|   } | ||||
|  | ||||
|   render() { | ||||
|     const { data, fetched, errorMessage, loadData, classes } = this.props; | ||||
|     return ( | ||||
| @@ -109,9 +178,11 @@ class SystemStatus extends Component { | ||||
|             () => this.renderSystemStatus(data, classes) | ||||
|           } | ||||
|         /> | ||||
|         {this.renderRestartDialog()} | ||||
|       </SectionContent> | ||||
|     ) | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| export default restComponent(SYSTEM_STATUS_ENDPOINT, withStyles(styles)(SystemStatus)); | ||||
| export default withSnackbar(restComponent(SYSTEM_STATUS_ENDPOINT, withStyles(styles)(SystemStatus))); | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import DNSIcon from '@material-ui/icons/Dns'; | ||||
| import SettingsInputComponentIcon from '@material-ui/icons/SettingsInputComponent'; | ||||
| import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; | ||||
| import DeviceHubIcon from '@material-ui/icons/DeviceHub'; | ||||
| import RefreshIcon from '@material-ui/icons/Refresh'; | ||||
|  | ||||
| import SectionContent from '../components/SectionContent'; | ||||
| import { WIFI_STATUS_ENDPOINT } from '../constants/Endpoints'; | ||||
| @@ -130,7 +131,7 @@ class WiFiStatus extends Component { | ||||
|         <List> | ||||
|           {this.createListItems(data, classes)} | ||||
|         </List> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|         <Button startIcon={<RefreshIcon />} variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> | ||||
|           Refresh | ||||
|         </Button> | ||||
|       </div> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import PasswordValidator from '../components/PasswordValidator'; | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import MenuItem from '@material-ui/core/MenuItem'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| const styles = theme => ({ | ||||
|   textField: { | ||||
| @@ -61,7 +62,7 @@ class APSettingsForm extends React.Component { | ||||
|             /> | ||||
|           </Fragment> | ||||
|         } | ||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|           Save | ||||
|         </Button> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -18,6 +18,8 @@ import DeleteIcon from '@material-ui/icons/Delete'; | ||||
| import CloseIcon from '@material-ui/icons/Close'; | ||||
| import CheckIcon from '@material-ui/icons/Check'; | ||||
| import IconButton from '@material-ui/core/IconButton'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
| import PersonAddIcon from '@material-ui/icons/PersonAdd'; | ||||
|  | ||||
| import UserForm from './UserForm'; | ||||
| import { withAuthenticationContext } from '../authentication/Context'; | ||||
| @@ -161,7 +163,7 @@ class ManageUsersForm extends React.Component { | ||||
|               <TableRow> | ||||
|                 <TableCell colSpan={2} /> | ||||
|                 <TableCell align="center"> | ||||
|                   <Button variant="contained" color="secondary" onClick={this.createUser}> | ||||
|                   <Button startIcon={<PersonAddIcon />} variant="contained" color="secondary" onClick={this.createUser}> | ||||
|                     Add User | ||||
|                   </Button> | ||||
|                 </TableCell> | ||||
| @@ -176,7 +178,7 @@ class ManageUsersForm extends React.Component { | ||||
|               </Box> | ||||
|             </Typography> | ||||
|           } | ||||
|           <Button variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}> | ||||
|           <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit" disabled={this.noAdminConfigured()}> | ||||
|             Save | ||||
|           </Button> | ||||
|           <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; | ||||
|  | ||||
| import { withStyles } from '@material-ui/core/styles'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| import isIP from '../validators/isIP'; | ||||
| import isHostname from '../validators/isHostname'; | ||||
| @@ -50,7 +51,7 @@ class NTPSettingsForm extends React.Component { | ||||
|           onChange={handleValueChange('interval')} | ||||
|           margin="normal" | ||||
|         /> | ||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|           Save | ||||
|         </Button> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import Button from '@material-ui/core/Button'; | ||||
| import Switch from '@material-ui/core/Switch'; | ||||
| import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; | ||||
| import FormControlLabel from '@material-ui/core/FormControlLabel'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| import isIP from '../validators/isIP'; | ||||
| import isHostname from '../validators/isHostname'; | ||||
| @@ -69,7 +70,7 @@ class OTASettingsForm extends React.Component { | ||||
|           onChange={handleValueChange('password')} | ||||
|           margin="normal" | ||||
|         /> | ||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|           Save | ||||
|         </Button> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import { withStyles } from '@material-ui/core/styles'; | ||||
| import Button from '@material-ui/core/Button'; | ||||
| import Typography from '@material-ui/core/Typography'; | ||||
| import Box from '@material-ui/core/Box'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| import PasswordValidator from '../components/PasswordValidator'; | ||||
| import { withAuthenticationContext } from '../authentication/Context'; | ||||
| @@ -46,7 +47,7 @@ class SecuritySettingsForm extends React.Component { | ||||
|             If you modify the JWT Secret, all users will be logged out. | ||||
|           </Box> | ||||
|         </Typography> | ||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|           Save | ||||
|         </Button> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import Badge from '@material-ui/core/Badge'; | ||||
| import WifiIcon from '@material-ui/icons/Wifi'; | ||||
| import LockIcon from '@material-ui/icons/Lock'; | ||||
| import LockOpenIcon from '@material-ui/icons/LockOpen'; | ||||
| import PermScanWifiIcon from '@material-ui/icons/PermScanWifi'; | ||||
|  | ||||
| import { isNetworkOpen, networkSecurityMode } from '../constants/WiFiSecurityModes'; | ||||
|  | ||||
| @@ -86,7 +87,7 @@ class WiFiNetworkSelector extends Component { | ||||
|           </div> | ||||
|         } | ||||
|  | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={requestNetworkScan} disabled={scanningForNetworks}> | ||||
|         <Button startIcon={<PermScanWifiIcon />} variant="contained" color="secondary" className={classes.button} onClick={requestNetworkScan} disabled={scanningForNetworks}> | ||||
|           Scan again... | ||||
|         </Button> | ||||
|       </div> | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import IconButton from '@material-ui/core/IconButton'; | ||||
| import LockIcon from '@material-ui/icons/Lock'; | ||||
| import LockOpenIcon from '@material-ui/icons/LockOpen'; | ||||
| import DeleteIcon from '@material-ui/icons/Delete'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator'; | ||||
| import { isNetworkOpen, networkSecurityMode } from '../constants/WiFiSecurityModes'; | ||||
| @@ -175,7 +176,7 @@ class WiFiSettingsForm extends React.Component { | ||||
|             /> | ||||
|           </Fragment> | ||||
|         } | ||||
|         <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|           Save | ||||
|         </Button> | ||||
|         <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import Button from '@material-ui/core/Button'; | ||||
| import Typography from '@material-ui/core/Typography'; | ||||
| import Slider from '@material-ui/core/Slider'; | ||||
| import { makeStyles } from '@material-ui/core/styles'; | ||||
| import SaveIcon from '@material-ui/icons/Save'; | ||||
|  | ||||
| export const DEMO_SETTINGS_ENDPOINT = ENDPOINT_ROOT + "demoSettings"; | ||||
|  | ||||
| @@ -69,7 +70,7 @@ function DemoControllerForm(props) { | ||||
|         max={255} | ||||
|         onChange={handleSliderChange('blink_speed')} | ||||
|       /> | ||||
|       <Button variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|       <Button startIcon={<SaveIcon />} variant="contained" color="primary" className={classes.button} type="submit"> | ||||
|         Save | ||||
|       </Button> | ||||
|       <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user