Upgrade to material ui 4

Add user management and roles - TBA
Menu Label Renames - TBA
This commit is contained in:
Rick Watson 2019-05-24 12:19:27 +01:00
parent 685420aaed
commit 0c630f0f93
16 changed files with 799 additions and 469 deletions

View File

@ -890,68 +890,105 @@
"resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz",
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw=="
}, },
"@emotion/hash": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz",
"integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA=="
},
"@material-ui/core": { "@material-ui/core": {
"version": "3.9.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.9.3.tgz", "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.0.0.tgz",
"integrity": "sha512-REIj62+zEvTgI/C//YL4fZxrCVIySygmpZglsu/Nl5jPqy3CDjZv1F9ubBYorHqmRgeVPh64EghMMWqk4egmfg==", "integrity": "sha512-mLEGTuzgUALRKFI3hkRcS0gi/cB3XV0JA4F5PT3rGUt7Dc4liu8/IGiHF7iQh+p337FMk8vkEMxMVdYd9JXKMQ==",
"requires": { "requires": {
"@babel/runtime": "^7.2.0", "@babel/runtime": "^7.2.0",
"@material-ui/system": "^3.0.0-alpha.0", "@material-ui/styles": "^4.0.0",
"@material-ui/utils": "^3.0.0-alpha.2", "@material-ui/system": "^4.0.0",
"@types/jss": "^9.5.6", "@material-ui/types": "^4.0.0",
"@types/react-transition-group": "^2.0.8", "@material-ui/utils": "^4.0.0",
"brcast": "^3.0.1", "@types/react-transition-group": "^2.0.16",
"classnames": "^2.2.5", "clsx": "^1.0.2",
"convert-css-length": "^1.0.2",
"csstype": "^2.5.2", "csstype": "^2.5.2",
"debounce": "^1.1.0", "debounce": "^1.1.0",
"deepmerge": "^3.0.0", "deepmerge": "^3.0.0",
"dom-helpers": "^3.2.1",
"hoist-non-react-statics": "^3.2.1", "hoist-non-react-statics": "^3.2.1",
"is-plain-object": "^2.0.4", "is-plain-object": "^2.0.4",
"jss": "^9.8.7",
"jss-camel-case": "^6.0.0",
"jss-default-unit": "^8.0.2",
"jss-global": "^3.0.0",
"jss-nested": "^6.0.1",
"jss-props-sort": "^6.0.0",
"jss-vendor-prefixer": "^7.0.0",
"normalize-scroll-left": "^0.1.2", "normalize-scroll-left": "^0.1.2",
"popper.js": "^1.14.1", "popper.js": "^1.14.1",
"prop-types": "^15.6.0", "prop-types": "^15.7.2",
"react-event-listener": "^0.6.2", "react-event-listener": "^0.6.6",
"react-transition-group": "^2.2.1", "react-transition-group": "^4.0.0",
"recompose": "0.28.0 - 0.30.0",
"warning": "^4.0.1" "warning": "^4.0.1"
} }
}, },
"@material-ui/icons": { "@material-ui/icons": {
"version": "3.0.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-3.0.2.tgz", "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.0.0.tgz",
"integrity": "sha512-QY/3gJnObZQ3O/e6WjH+0ah2M3MOgLOzCy8HTUoUx9B6dDrS18vP7Ycw3qrDEKlB6q1KNxy6CZHm5FCauWGy2g==", "integrity": "sha512-hXoKnVLmVer+kic84ypoyG3Amym3a8q3pvDg4KYjeKW9fxGru7x/IkelBJODQL0jO+nAPz1+9RNpFWC75v35dg==",
"requires": {
"@babel/runtime": "^7.2.0"
}
},
"@material-ui/styles": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.0.0.tgz",
"integrity": "sha512-TUpmXlyZDVOl6E2//+UzsZxgi2E+2L753QY02nNkbAC6PPx8FUBqvnjYSGqX0V/BjTJ/fD4CkoS6ZpY3lHf+Gg==",
"requires": { "requires": {
"@babel/runtime": "^7.2.0", "@babel/runtime": "^7.2.0",
"recompose": "0.28.0 - 0.30.0" "@emotion/hash": "^0.7.1",
"@material-ui/types": "^4.0.0",
"@material-ui/utils": "^4.0.0",
"clsx": "^1.0.2",
"deepmerge": "^3.0.0",
"hoist-non-react-statics": "^3.2.1",
"jss": "^10.0.0-alpha.16",
"jss-plugin-camel-case": "^10.0.0-alpha.16",
"jss-plugin-default-unit": "^10.0.0-alpha.16",
"jss-plugin-global": "^10.0.0-alpha.16",
"jss-plugin-nested": "^10.0.0-alpha.16",
"jss-plugin-props-sort": "^10.0.0-alpha.16",
"jss-plugin-rule-value-function": "^10.0.0-alpha.16",
"jss-plugin-vendor-prefixer": "^10.0.0-alpha.16",
"prop-types": "^15.7.2",
"warning": "^4.0.1"
},
"dependencies": {
"jss": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
}
} }
}, },
"@material-ui/system": { "@material-ui/system": {
"version": "3.0.0-alpha.2", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-3.0.0-alpha.2.tgz", "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.0.0.tgz",
"integrity": "sha512-odmxQ0peKpP7RQBQ8koly06YhsPzcoVib1vByVPBH4QhwqBXuYoqlCjt02846fYspAqkrWzjxnWUD311EBbxOA==", "integrity": "sha512-SIsqIwjix98Mqw9LVAmRqTs10E4S/SP5n5mlBlhHVHI+2XG2c+MaCPzOF2Zxq0KdqOMgTb7/aevR3mG9UmODxg==",
"requires": { "requires": {
"@babel/runtime": "^7.2.0", "@babel/runtime": "^7.2.0",
"deepmerge": "^3.0.0", "deepmerge": "^3.0.0",
"prop-types": "^15.6.0", "prop-types": "^15.7.2",
"warning": "^4.0.1" "warning": "^4.0.1"
} }
}, },
"@material-ui/types": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-4.0.0.tgz",
"integrity": "sha512-wuiQMo8nSljZR1oWh57UQYssdtFqaU+Cbhr16uLohzzTllpCAK4LkH0slnH3n+5vCa2dgOdNlZTrmsIDDwvRJQ=="
},
"@material-ui/utils": { "@material-ui/utils": {
"version": "3.0.0-alpha.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-3.0.0-alpha.3.tgz", "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.0.0.tgz",
"integrity": "sha512-rwMdMZptX0DivkqBuC+Jdq7BYTXwqKai5G5ejPpuEDKpWzi1Oxp+LygGw329FrKpuKeiqpcymlqJTjmy+quWng==", "integrity": "sha512-gjz52hO1hkIbKPMng1diQybVgtfgCptOCrulUs4emSCHHKUoR1zfT+IUrjgOaKIpYZNOgS/CI7KDMp689+FzeQ==",
"requires": { "requires": {
"@babel/runtime": "^7.2.0", "@babel/runtime": "^7.2.0",
"prop-types": "^15.6.0", "prop-types": "^15.7.2",
"react-is": "^16.6.3" "react-is": "^16.8.0"
} }
}, },
"@mrmlnc/readdir-enhanced": { "@mrmlnc/readdir-enhanced": {
@ -1107,24 +1144,15 @@
"loader-utils": "^1.1.0" "loader-utils": "^1.1.0"
} }
}, },
"@types/jss": {
"version": "9.5.8",
"resolved": "https://registry.npmjs.org/@types/jss/-/jss-9.5.8.tgz",
"integrity": "sha512-bBbHvjhm42UKki+wZpR89j73ykSXg99/bhuKuYYePtpma3ZAnmeGnl0WxXiZhPGsIfzKwCUkpPC0jlrVMBfRxA==",
"requires": {
"csstype": "^2.0.0",
"indefinite-observable": "^1.0.1"
}
},
"@types/node": { "@types/node": {
"version": "11.13.4", "version": "11.13.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.4.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.4.tgz",
"integrity": "sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ==" "integrity": "sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ=="
}, },
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.0", "version": "15.7.1",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.0.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz",
"integrity": "sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg==" "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg=="
}, },
"@types/q": { "@types/q": {
"version": "1.5.2", "version": "1.5.2",
@ -1132,18 +1160,18 @@
"integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw=="
}, },
"@types/react": { "@types/react": {
"version": "16.8.13", "version": "16.8.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.13.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.18.tgz",
"integrity": "sha512-otJ4ntMuHGrvm67CdDJMAls4WqotmAmW0g3HmWi9LCjSWXrxoXY/nHXrtmMfvPEEmGFNm6NdgMsJmnfH820Qaw==", "integrity": "sha512-lUXdKzRqWR4FebR5tGHkLCqnvQJS4fdXKCBrNGGbglqZg2gpU+J82pMONevQODUotATs9fc9k66bx3/St8vReg==",
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^2.2.0" "csstype": "^2.2.0"
} }
}, },
"@types/react-transition-group": { "@types/react-transition-group": {
"version": "2.9.0", "version": "2.9.1",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.0.tgz", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.1.tgz",
"integrity": "sha512-hP7vUaZMVSWKxo133P8U51U6UZ7+pbY+eAQb8+p6SZ2rB1rj3mOTDgTzhhi+R2SCB4S+sWekAAGoxdiZPG0ReQ==", "integrity": "sha512-1usq4DRUVBFnxc9KGJAlJO9EpQrLZGDDEC8wDOn2+2ODSyudYo8FiIzPDRaX/hfQjHqGeeoNaNdA2bj0l35hZQ==",
"requires": { "requires": {
"@types/react": "*" "@types/react": "*"
} }
@ -2686,11 +2714,6 @@
"repeat-element": "^1.1.2" "repeat-element": "^1.1.2"
} }
}, },
"brcast": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/brcast/-/brcast-3.0.1.tgz",
"integrity": "sha512-eI3yqf9YEqyGl9PCNTR46MGvDylGtaHjalcz6Q3fAPnP/PhpKkkve52vFdfGpwp4VUvK6LUr4TQN+2stCrEwTg=="
},
"brorand": { "brorand": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
@ -2960,11 +2983,6 @@
"supports-color": "^5.3.0" "supports-color": "^5.3.0"
} }
}, },
"change-emitter": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
"integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
},
"chardet": { "chardet": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
@ -3616,11 +3634,6 @@
} }
} }
}, },
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": { "clean-css": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
@ -3679,6 +3692,11 @@
"shallow-clone": "^0.1.2" "shallow-clone": "^0.1.2"
} }
}, },
"clsx": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz",
"integrity": "sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg=="
},
"co": { "co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -3859,6 +3877,11 @@
"date-now": "^0.1.4" "date-now": "^0.1.4"
} }
}, },
"console-polyfill": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/console-polyfill/-/console-polyfill-0.1.2.tgz",
"integrity": "sha1-ls/tUcr3gYn2mVcubxgnHcN8DjA="
},
"constants-browserify": { "constants-browserify": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@ -3879,6 +3902,15 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
}, },
"convert-css-length": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/convert-css-length/-/convert-css-length-1.0.2.tgz",
"integrity": "sha512-ecV7j3hXyXN1X2XfJBzhMR0o1Obv0v3nHmn0UiS3ACENrzbxE/EknkiunS/fCwQva0U62X1GChi8GaPh4oTlLg==",
"requires": {
"console-polyfill": "^0.1.2",
"parse-unit": "^1.0.1"
}
},
"convert-source-map": { "convert-source-map": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
@ -4257,14 +4289,6 @@
"resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz",
"integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=" "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w="
}, },
"css-vendor": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz",
"integrity": "sha1-ZCHP0wNM5mT+dnOXL9ARn8KJQfo=",
"requires": {
"is-in-browser": "^1.0.2"
}
},
"css-what": { "css-what": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
@ -4449,9 +4473,9 @@
} }
}, },
"csstype": { "csstype": {
"version": "2.6.3", "version": "2.6.4",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz",
"integrity": "sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg==" "integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg=="
}, },
"cyclist": { "cyclist": {
"version": "0.2.2", "version": "0.2.2",
@ -7993,14 +8017,6 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
}, },
"indefinite-observable": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-1.0.2.tgz",
"integrity": "sha512-Mps0898zEduHyPhb7UCgNmfzlqNZknVmaFz5qzr0mm04YQ5FGLhAyK/dJ+NaRxGyR6juQXIxh5Ev0xx+qq0nYA==",
"requires": {
"symbol-observable": "1.2.0"
}
},
"indexes-of": { "indexes-of": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
@ -8230,11 +8246,6 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
}, },
"is-function": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz",
"integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU="
},
"is-generator-fn": { "is-generator-fn": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-1.0.0.tgz",
@ -9195,148 +9206,242 @@
} }
}, },
"jss": { "jss": {
"version": "9.8.7", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss/-/jss-9.8.7.tgz", "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ==", "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": { "requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3", "is-in-browser": "^1.1.3",
"symbol-observable": "^1.1.0", "tiny-warning": "^1.0.2"
"warning": "^3.0.0" }
},
"jss-plugin-camel-case": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.16.tgz",
"integrity": "sha512-nki+smHEsFyoZ0OlOYtaxVqcQA0ZHVJCE1slRnk+1TklbmxbBiO4TwITMTEaNIDv0U0Uyb0Z8wVgFgRwCCIFog==",
"requires": {
"@babel/runtime": "^7.3.1",
"hyphenate-style-name": "^1.0.3",
"jss": "10.0.0-alpha.16"
}, },
"dependencies": { "dependencies": {
"warning": { "jss": {
"version": "3.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": { "requires": {
"loose-envify": "^1.0.0" "@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
} }
} }
} }
}, },
"jss-camel-case": { "jss-plugin-compose": {
"version": "6.1.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-camel-case/-/jss-camel-case-6.1.0.tgz", "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.0.0-alpha.16.tgz",
"integrity": "sha512-HPF2Q7wmNW1t79mCqSeU2vdd/vFFGpkazwvfHMOhPlMgXrJDzdj9viA2SaHk9ZbD5pfL63a8ylp4++irYbbzMQ==", "integrity": "sha512-MeOc5RuDSqB3czoUFM32pBq370+sKKjG1K4aamVWpAUWpsphLi/YlotrFOkk/FCb2So1ga4W7/zrCc/50OeRAQ==",
"requires": { "requires": {
"hyphenate-style-name": "^1.0.2" "@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16",
"tiny-warning": "^1.0.2"
} }
}, },
"jss-compose": { "jss-plugin-default-unit": {
"version": "5.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-compose/-/jss-compose-5.0.0.tgz", "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.16.tgz",
"integrity": "sha512-YofRYuiA0+VbeOw0VjgkyO380sA4+TWDrW52nSluD9n+1FWOlDzNbgpZ/Sb3Y46+DcAbOS21W5jo6SAqUEiuwA==", "integrity": "sha512-jjGW4F/r9yKvoyUk22M8nWhdMfvoWzJw/oFO2cDRXCk2onnWFiRALfqeUsEDyocwdZbyVF9WhZbSHn4GL03kSw==",
"requires": { "requires": {
"warning": "^3.0.0" "@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16"
}, },
"dependencies": { "dependencies": {
"warning": { "jss": {
"version": "3.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": { "requires": {
"loose-envify": "^1.0.0" "@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
} }
} }
} }
}, },
"jss-default-unit": { "jss-plugin-expand": {
"version": "8.0.2", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-default-unit/-/jss-default-unit-8.0.2.tgz", "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.0.0-alpha.16.tgz",
"integrity": "sha512-WxNHrF/18CdoAGw2H0FqOEvJdREXVXLazn7PQYU7V6/BWkCV0GkmWsppNiExdw8dP4TU1ma1dT9zBNJ95feLmg==" "integrity": "sha512-Q3m0PDWGojfcmWBCkegRJxonq2q9lI6ZfixoFgvTvi+b9zKza0KXkHBUzGjeFyM36U/WRWj43SC33dajcI9jAg==",
},
"jss-expand": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/jss-expand/-/jss-expand-5.3.0.tgz",
"integrity": "sha512-NiM4TbDVE0ykXSAw6dfFmB1LIqXP/jdd0ZMnlvlGgEMkMt+weJIl8Ynq1DsuBY9WwkNyzWktdqcEW2VN0RAtQg=="
},
"jss-extend": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/jss-extend/-/jss-extend-6.2.0.tgz",
"integrity": "sha512-YszrmcB6o9HOsKPszK7NeDBNNjVyiW864jfoiHoMlgMIg2qlxKw70axZHqgczXHDcoyi/0/ikP1XaHDPRvYtEA==",
"requires": { "requires": {
"warning": "^3.0.0" "@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16"
}
},
"jss-plugin-extend": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.0.0-alpha.16.tgz",
"integrity": "sha512-nJ8H5b/dBZlqaPYCLNmcaHRQgzSlnAwhZUcIo30s0IgvhTtN/TaiRtEbrJZjfXPzatTsnFoRwZzJqs8Sakev+A==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-global": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.16.tgz",
"integrity": "sha512-B1mm2ZF9OEsWPmzkG5ZUXqV88smDqpc4unILLXhWVuj0U5JeT0DNitH+QbXFrSueDJzkWVfvqyckvWDR/0qeDg==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16"
}, },
"dependencies": { "dependencies": {
"warning": { "jss": {
"version": "3.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": { "requires": {
"loose-envify": "^1.0.0" "@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
} }
} }
} }
}, },
"jss-global": { "jss-plugin-nested": {
"version": "3.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-global/-/jss-global-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.16.tgz",
"integrity": "sha512-wxYn7vL+TImyQYGAfdplg7yaxnPQ9RaXY/cIA8hawaVnmmWxDHzBK32u1y+RAvWboa3lW83ya3nVZ/C+jyjZ5Q==" "integrity": "sha512-3l/MB6COnIpq4GOXQFae6UydoaIPa81UxhuBTEQuiAojgTeUla9L7nB3h18Q4zAhQQpjxaEsyppAKuEzIP7kPQ==",
},
"jss-nested": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jss-nested/-/jss-nested-6.0.1.tgz",
"integrity": "sha512-rn964TralHOZxoyEgeq3hXY8hyuCElnvQoVrQwKHVmu55VRDd6IqExAx9be5HgK0yN/+hQdgAXQl/GUrBbbSTA==",
"requires": { "requires": {
"warning": "^3.0.0" "@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16",
"tiny-warning": "^1.0.2"
}, },
"dependencies": { "dependencies": {
"warning": { "jss": {
"version": "3.0.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": { "requires": {
"loose-envify": "^1.0.0" "@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
}
}
},
"jss-plugin-props-sort": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.16.tgz",
"integrity": "sha512-+Yn9nugHAH58nf/d43H2uxMvlCFPDgLKRSmKO4Q4m1IGYjMbHsWt1Rk2HfC9IiCanqcqpc8hstwtzf+HG7PWFQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16"
},
"dependencies": {
"jss": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
}
}
},
"jss-plugin-rule-value-function": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.16.tgz",
"integrity": "sha512-MQap9ne6ZGZH0NlpSQTMSm6QalBTF0hYpd2uaGQwam+GlT7IKeO+sTjd46I1WgO3kyOmwb0pIY6CnuLQGXKtSA==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16"
},
"dependencies": {
"jss": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
}
}
}
},
"jss-plugin-rule-value-observable": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.0.0-alpha.16.tgz",
"integrity": "sha512-Gmj1sVKWM2KVZpG0Wn3Z+SArvskdXEtSCrww43g/OO+j8DN9O+UEV47tM/HYfdiyLICnvKHc2XGmhNz9LHcpNQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16",
"symbol-observable": "^1.2.0"
}
},
"jss-plugin-template": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.0.0-alpha.16.tgz",
"integrity": "sha512-L1epTMTDINJPUZkFuyohCXQtJDTMj1CNTBv9ysqVyMc3qjkifAvPEws6XuoRSC9jy1ZvqDTWlxPfbmoJ2r6BWg==",
"requires": {
"@babel/runtime": "^7.3.1",
"jss": "10.0.0-alpha.16",
"tiny-warning": "^1.0.2"
}
},
"jss-plugin-vendor-prefixer": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.16.tgz",
"integrity": "sha512-70yJ6QE5dN8VlPUGKld5jK2SKyrteheEL/ismexpybIufunMs6iJgkhDndbOfv8ia13yZgUVqeakMdhRKYwK1A==",
"requires": {
"@babel/runtime": "^7.3.1",
"css-vendor": "^2.0.1",
"jss": "10.0.0-alpha.16"
},
"dependencies": {
"css-vendor": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.2.tgz",
"integrity": "sha512-Xn5ZAlI00d8HaQ8/oQ8d+iBzSF//NCc77LPzsucM32X/R/yTqmXy6otVsAM0XleXk6HjPuXoVZwXsayky/fsFQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.0.2"
}
},
"jss": {
"version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==",
"requires": {
"@babel/runtime": "^7.3.1",
"is-in-browser": "^1.1.3",
"tiny-warning": "^1.0.2"
} }
} }
} }
}, },
"jss-preset-default": { "jss-preset-default": {
"version": "4.5.0", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-4.5.0.tgz", "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.0.0-alpha.16.tgz",
"integrity": "sha512-qZbpRVtHT7hBPpZEBPFfafZKWmq3tA/An5RNqywDsZQGrlinIF/mGD9lmj6jGqu8GrED2SMHZ3pPKLmjCZoiaQ==", "integrity": "sha512-YBq2XE4iJdl16klxfw0xTaKksfAIXSoC2kPZQ4dmw4n/KMFOz/A26eN30FwWixyObfDMKyZp94vwCKal7711IQ==",
"requires": { "requires": {
"jss-camel-case": "^6.1.0", "@babel/runtime": "^7.3.1",
"jss-compose": "^5.0.0", "jss": "10.0.0-alpha.16",
"jss-default-unit": "^8.0.2", "jss-plugin-camel-case": "10.0.0-alpha.16",
"jss-expand": "^5.3.0", "jss-plugin-compose": "10.0.0-alpha.16",
"jss-extend": "^6.2.0", "jss-plugin-default-unit": "10.0.0-alpha.16",
"jss-global": "^3.0.0", "jss-plugin-expand": "10.0.0-alpha.16",
"jss-nested": "^6.0.1", "jss-plugin-extend": "10.0.0-alpha.16",
"jss-props-sort": "^6.0.0", "jss-plugin-global": "10.0.0-alpha.16",
"jss-template": "^1.0.1", "jss-plugin-nested": "10.0.0-alpha.16",
"jss-vendor-prefixer": "^7.0.0" "jss-plugin-props-sort": "10.0.0-alpha.16",
} "jss-plugin-rule-value-function": "10.0.0-alpha.16",
}, "jss-plugin-rule-value-observable": "10.0.0-alpha.16",
"jss-props-sort": { "jss-plugin-template": "10.0.0-alpha.16",
"version": "6.0.0", "jss-plugin-vendor-prefixer": "10.0.0-alpha.16"
"resolved": "https://registry.npmjs.org/jss-props-sort/-/jss-props-sort-6.0.0.tgz",
"integrity": "sha512-E89UDcrphmI0LzmvYk25Hp4aE5ZBsXqMWlkFXS0EtPkunJkRr+WXdCNYbXbksIPnKlBenGB9OxzQY+mVc70S+g=="
},
"jss-template": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/jss-template/-/jss-template-1.0.1.tgz",
"integrity": "sha512-m5BqEWha17fmIVXm1z8xbJhY6GFJxNB9H68GVnCWPyGYfxiAgY9WTQyvDAVj+pYRgrXSOfN5V1T4+SzN1sJTeg==",
"requires": {
"warning": "^3.0.0"
},
"dependencies": {
"warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
"integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"jss-vendor-prefixer": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz",
"integrity": "sha512-Agd+FKmvsI0HLcYXkvy8GYOw3AAASBUpsmIRvVQheps+JWaN892uFOInTr0DRydwaD91vSSUCU4NssschvF7MA==",
"requires": {
"css-vendor": "^0.3.8"
} }
}, },
"jsx-ast-utils": { "jsx-ast-utils": {
@ -10455,6 +10560,11 @@
"json-parse-better-errors": "^1.0.1" "json-parse-better-errors": "^1.0.1"
} }
}, },
"parse-unit": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz",
"integrity": "sha1-fhu21b7zh0wo45JSaiVBFwKR7s8="
},
"parse5": { "parse5": {
"version": "5.1.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
@ -13013,6 +13123,11 @@
} }
} }
}, },
"react-display-name": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/react-display-name/-/react-display-name-0.2.4.tgz",
"integrity": "sha512-zvU6iouW+SWwHTyThwxGICjJYCMZFk/6r/+jmOdC7ntQoPlS/Pqb81MkxaMf2bHTSq9TN3K3zX2/ayMW/jCtyA=="
},
"react-dom": { "react-dom": {
"version": "16.8.6", "version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
@ -13054,22 +13169,17 @@
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
}, },
"react-jss": { "react-jss": {
"version": "8.6.1", "version": "10.0.0-alpha.16",
"resolved": "https://registry.npmjs.org/react-jss/-/react-jss-8.6.1.tgz", "resolved": "https://registry.npmjs.org/react-jss/-/react-jss-10.0.0-alpha.16.tgz",
"integrity": "sha512-SH6XrJDJkAphp602J14JTy3puB2Zxz1FkM3bKVE8wON+va99jnUTKWnzGECb3NfIn9JPR5vHykge7K3/A747xQ==", "integrity": "sha512-nGIerGVDV9V6cpRXhkJZgoV0MsoJbKMdAiCoPzCDnsdR+om6zLyhQEvVHNtd0mB16dO+pzNaovhBvElhdj/3ug==",
"requires": { "requires": {
"hoist-non-react-statics": "^2.5.0", "@babel/runtime": "^7.3.1",
"jss": "^9.7.0", "hoist-non-react-statics": "^3.2.0",
"jss-preset-default": "^4.3.0", "jss": "10.0.0-alpha.16",
"jss-preset-default": "10.0.0-alpha.16",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"theming": "^1.3.0" "theming": "^3.0.3",
}, "tiny-warning": "^1.0.2"
"dependencies": {
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
}
} }
}, },
"react-lifecycles-compat": { "react-lifecycles-compat": {
@ -13171,14 +13281,13 @@
} }
}, },
"react-transition-group": { "react-transition-group": {
"version": "2.9.0", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.0.1.tgz",
"integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", "integrity": "sha512-SsLcBYhO4afXJC9esL8XMxi/y0ZvEc7To0TvtrBELqzpjXQHPZOTxvuPh2/4EhYc0uSMfp2SExIxsyJ0pBdNzg==",
"requires": { "requires": {
"dom-helpers": "^3.4.0", "dom-helpers": "^3.4.0",
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2"
"react-lifecycles-compat": "^3.0.4"
} }
}, },
"read-pkg": { "read-pkg": {
@ -13514,26 +13623,6 @@
"util.promisify": "^1.0.0" "util.promisify": "^1.0.0"
} }
}, },
"recompose": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz",
"integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==",
"requires": {
"@babel/runtime": "^7.0.0",
"change-emitter": "^0.1.2",
"fbjs": "^0.8.1",
"hoist-non-react-statics": "^2.3.1",
"react-lifecycles-compat": "^3.0.2",
"symbol-observable": "^1.0.4"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
}
}
},
"recursive-readdir": { "recursive-readdir": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
@ -15184,14 +15273,14 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
}, },
"theming": { "theming": {
"version": "1.3.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/theming/-/theming-1.3.0.tgz", "resolved": "https://registry.npmjs.org/theming/-/theming-3.2.0.tgz",
"integrity": "sha512-ya5Ef7XDGbTPBv5ENTwrwkPUexrlPeiAg/EI9kdlUAZhNlRbCdhMKRgjNX1IcmsmiPcqDQZE6BpSaH+cr31FKw==", "integrity": "sha512-n0fSNYXkX63rcFBBeAthy14IcgPZLHp0OGkGZheaj64j7cBoP7INLd6+7HIXqWVjFn1M5cYSiZ1nszi+jo/Szg==",
"requires": { "requires": {
"brcast": "^3.0.1", "hoist-non-react-statics": "^3.3.0",
"is-function": "^1.0.1", "prop-types": "^15.5.8",
"is-plain-object": "^2.0.1", "react-display-name": "^0.2.4",
"prop-types": "^15.5.8" "tiny-warning": "^1.0.2"
} }
}, },
"throat": { "throat": {

View File

@ -3,8 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^3.9.3", "@material-ui/core": "^4.0.0",
"@material-ui/icons": "^3.0.2", "@material-ui/icons": "^4.0.0",
"compression-webpack-plugin": "^2.0.0", "compression-webpack-plugin": "^2.0.0",
"jwt-decode": "^2.2.0", "jwt-decode": "^2.2.0",
"moment": "^2.24.0", "moment": "^2.24.0",
@ -12,7 +12,7 @@
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-form-validator-core": "^0.6.2", "react-form-validator-core": "^0.6.2",
"react-jss": "^8.6.1", "react-jss": "^10.0.0-alpha.16",
"react-material-ui-form-validator": "^2.0.7", "react-material-ui-form-validator": "^2.0.7",
"react-router": "^5.0.0", "react-router": "^5.0.0",
"react-router-dom": "^5.0.0", "react-router-dom": "^5.0.0",

View File

@ -10,14 +10,12 @@ import orange from '@material-ui/core/colors/orange';
import red from '@material-ui/core/colors/red'; import red from '@material-ui/core/colors/red';
import green from '@material-ui/core/colors/green'; import green from '@material-ui/core/colors/green';
import JssProvider from 'react-jss/lib/JssProvider';
import { create } from 'jss'; import { create } from 'jss';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { import {
MuiThemeProvider, MuiThemeProvider,
createMuiTheme, createMuiTheme
createGenerateClassName,
jssPreset,
} from '@material-ui/core/styles'; } from '@material-ui/core/styles';
// Our theme // Our theme
@ -35,20 +33,17 @@ const theme = createMuiTheme({
// JSS instance // JSS instance
const jss = create(jssPreset()); const jss = create(jssPreset());
// Class name generator.
const generateClassName = createGenerateClassName();
class App extends Component { class App extends Component {
render() { render() {
return ( return (
<JssProvider jss={jss} generateClassName={generateClassName}> <StylesProvider jss={jss}>
<MuiThemeProvider theme={theme}> <MuiThemeProvider theme={theme}>
<SnackbarNotification> <SnackbarNotification>
<CssBaseline /> <CssBaseline />
<AppRouting /> <AppRouting />
</SnackbarNotification> </SnackbarNotification>
</MuiThemeProvider> </MuiThemeProvider>
</JssProvider> </StylesProvider>
) )
} }
} }

View File

@ -14,7 +14,7 @@ import NTPConfiguration from './containers/NTPConfiguration';
import OTAConfiguration from './containers/OTAConfiguration'; import OTAConfiguration from './containers/OTAConfiguration';
import APConfiguration from './containers/APConfiguration'; import APConfiguration from './containers/APConfiguration';
import SignInPage from './containers/SignInPage'; import SignInPage from './containers/SignInPage';
import UserConfiguration from './containers/UserConfiguration'; import Security from './containers/Security';
class AppRouting extends Component { class AppRouting extends Component {
@ -31,7 +31,7 @@ class AppRouting extends Component {
<AuthenticatedRoute exact path="/ap-configuration" component={APConfiguration} /> <AuthenticatedRoute exact path="/ap-configuration" component={APConfiguration} />
<AuthenticatedRoute exact path="/ntp-configuration" component={NTPConfiguration} /> <AuthenticatedRoute exact path="/ntp-configuration" component={NTPConfiguration} />
<AuthenticatedRoute exact path="/ota-configuration" component={OTAConfiguration} /> <AuthenticatedRoute exact path="/ota-configuration" component={OTAConfiguration} />
<AuthenticatedRoute exact path="/user-configuration" component={UserConfiguration} /> <AuthenticatedRoute exact path="/security" component={Security} />
<Redirect to="/" /> <Redirect to="/" />
</Switch> </Switch>
</AuthenticationWrapper> </AuthenticationWrapper>

View File

@ -22,7 +22,7 @@ import SystemUpdateIcon from '@material-ui/icons/SystemUpdate';
import AccessTimeIcon from '@material-ui/icons/AccessTime'; import AccessTimeIcon from '@material-ui/icons/AccessTime';
import AccountCircleIcon from '@material-ui/icons/AccountCircle'; import AccountCircleIcon from '@material-ui/icons/AccountCircle';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import PeopleIcon from '@material-ui/icons/People'; import LockIcon from '@material-ui/icons/Lock';
import { APP_NAME } from '../constants/App'; import { APP_NAME } from '../constants/App';
import { withAuthenticationContext } from '../authentication/Context.js'; import { withAuthenticationContext } from '../authentication/Context.js';
@ -112,31 +112,31 @@ class MenuAppBar extends React.Component {
<ListItemIcon> <ListItemIcon>
<WifiIcon /> <WifiIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="WiFi Configuration" /> <ListItemText primary="WiFi Connection" />
</ListItem> </ListItem>
<ListItem button component={Link} to='/ap-configuration'> <ListItem button component={Link} to='/ap-configuration'>
<ListItemIcon> <ListItemIcon>
<SettingsInputAntennaIcon /> <SettingsInputAntennaIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="AP Configuration" /> <ListItemText primary="Access Point" />
</ListItem> </ListItem>
<ListItem button component={Link} to='/ntp-configuration'> <ListItem button component={Link} to='/ntp-configuration'>
<ListItemIcon> <ListItemIcon>
<AccessTimeIcon /> <AccessTimeIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="NTP Configuration" /> <ListItemText primary="Network Time" />
</ListItem> </ListItem>
<ListItem button component={Link} to='/ota-configuration'> <ListItem button component={Link} to='/ota-configuration'>
<ListItemIcon> <ListItemIcon>
<SystemUpdateIcon /> <SystemUpdateIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="OTA Configuration" /> <ListItemText primary="OTA Updates" />
</ListItem> </ListItem>
<ListItem button component={Link} to='/user-configuration'> <ListItem button component={Link} to='/security'>
<ListItemIcon> <ListItemIcon>
<PeopleIcon /> <LockIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="User Configuration" /> <ListItemText primary="Security" />
</ListItem> </ListItem>
<Divider /> <Divider />
<ListItem button onClick={authenticationContext.signOut}> <ListItem button onClick={authenticationContext.signOut}>

View File

@ -16,7 +16,7 @@ function SectionContent(props) {
const { children, classes, title } = props; const { children, classes, title } = props;
return ( return (
<Paper className={classes.content}> <Paper className={classes.content}>
<Typography variant="h4"> <Typography variant="h6">
{title} {title}
</Typography> </Typography>
{children} {children}

View File

@ -7,17 +7,18 @@ import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List'; import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText'; import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar'; import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider'; import Divider from '@material-ui/core/Divider';
import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna'; import SettingsInputAntennaIcon from '@material-ui/icons/SettingsInputAntenna';
import DeviceHubIcon from '@material-ui/icons/DeviceHub'; import DeviceHubIcon from '@material-ui/icons/DeviceHub';
import ComputerIcon from '@material-ui/icons/Computer'; import ComputerIcon from '@material-ui/icons/Computer';
import {restComponent} from '../components/RestComponent'; import { restComponent } from '../components/RestComponent';
import SectionContent from '../components/SectionContent' import SectionContent from '../components/SectionContent'
import * as Highlight from '../constants/Highlight'; import * as Highlight from '../constants/Highlight';
import { AP_STATUS_ENDPOINT } from '../constants/Endpoints'; import { AP_STATUS_ENDPOINT } from '../constants/Endpoints';
const styles = theme => ({ const styles = theme => ({
["apStatus_" + Highlight.SUCCESS]: { ["apStatus_" + Highlight.SUCCESS]: {
@ -42,40 +43,48 @@ class APStatus extends Component {
this.props.loadData(); this.props.loadData();
} }
apStatusHighlight(data){ apStatusHighlight(data) {
return data.active ? Highlight.SUCCESS : Highlight.IDLE; return data.active ? Highlight.SUCCESS : Highlight.IDLE;
} }
apStatus(data){ apStatus(data) {
return data.active ? "Active" : "Inactive"; return data.active ? "Active" : "Inactive";
} }
createListItems(data, classes){ createListItems(data, classes) {
return ( return (
<Fragment> <Fragment>
<ListItem> <ListItem>
<Avatar className={classes["apStatus_" + this.apStatusHighlight(data)]}> <ListItemAvatar>
<SettingsInputAntennaIcon /> <Avatar className={classes["apStatus_" + this.apStatusHighlight(data)]}>
</Avatar> <SettingsInputAntennaIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Status" secondary={this.apStatus(data)} /> <ListItemText primary="Status" secondary={this.apStatus(data)} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar>IP</Avatar> <ListItemAvatar>
<Avatar>IP</Avatar>
</ListItemAvatar>
<ListItemText primary="IP Address" secondary={data.ip_address} /> <ListItemText primary="IP Address" secondary={data.ip_address} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<DeviceHubIcon /> <Avatar>
</Avatar> <DeviceHubIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="MAC Address" secondary={data.mac_address} /> <ListItemText primary="MAC Address" secondary={data.mac_address} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<ComputerIcon /> <Avatar>
</Avatar> <ComputerIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="AP Clients" secondary={data.station_num} /> <ListItemText primary="AP Clients" secondary={data.station_num} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
@ -83,8 +92,8 @@ class APStatus extends Component {
); );
} }
renderAPStatus(data, classes){ renderAPStatus(data, classes) {
return ( return (
<div> <div>
<List> <List>
<Fragment> <Fragment>
@ -99,30 +108,30 @@ class APStatus extends Component {
} }
render() { render() {
const { data, fetched, errorMessage, classes } = this.props; const { data, fetched, errorMessage, classes } = this.props;
return ( return (
<SectionContent title="AP Status"> <SectionContent title="AP Status">
{ {
!fetched ? !fetched ?
<div> <div>
<LinearProgress className={classes.fetching}/> <LinearProgress className={classes.fetching} />
<Typography variant="h4" className={classes.fetching}> <Typography variant="h4" className={classes.fetching}>
Loading... Loading...
</Typography> </Typography>
</div> </div>
: :
data ? this.renderAPStatus(data, classes) data ? this.renderAPStatus(data, classes)
: :
<div> <div>
<Typography variant="h4" className={classes.fetching}> <Typography variant="h4" className={classes.fetching}>
{errorMessage} {errorMessage}
</Typography> </Typography>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh Refresh
</Button> </Button>
</div> </div>
} }
</SectionContent> </SectionContent>
) )
} }

View File

@ -1,30 +1,27 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { USERS_ENDPOINT } from '../constants/Endpoints'; import { USERS_ENDPOINT } from '../constants/Endpoints';
import {restComponent} from '../components/RestComponent'; import { restComponent } from '../components/RestComponent';
import SectionContent from '../components/SectionContent';
import ManageUsersForm from '../forms/ManageUsersForm'; import ManageUsersForm from '../forms/ManageUsersForm';
class ManageUsers extends Component { class ManageUsers extends Component {
componentDidMount() { componentDidMount() {
this.props.loadData(); this.props.loadData();
} }
render() { render() {
const { data, fetched, errorMessage } = this.props; const { data, fetched, errorMessage } = this.props;
return ( return (
<SectionContent title="Manage Users"> <ManageUsersForm
<ManageUsersForm userData={data}
users={data} userDataFetched={fetched}
usersFetched={fetched} errorMessage={errorMessage}
errorMessage={errorMessage} onSubmit={this.props.saveData}
onSubmit={this.props.saveData} onReset={this.props.loadData}
onReset={this.props.loadData} setData={this.props.setData}
handleValueChange={this.props.handleValueChange} handleValueChange={this.props.handleValueChange}
handleCheckboxChange={this.props.handleCheckboxChange} />
/>
</SectionContent>
) )
} }

View File

@ -6,6 +6,7 @@ import LinearProgress from '@material-ui/core/LinearProgress';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List'; import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import ListItemText from '@material-ui/core/ListItemText'; import ListItemText from '@material-ui/core/ListItemText';
import Avatar from '@material-ui/core/Avatar'; import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider'; import Divider from '@material-ui/core/Divider';
@ -16,10 +17,10 @@ import TimerIcon from '@material-ui/icons/Timer';
import UpdateIcon from '@material-ui/icons/Update'; import UpdateIcon from '@material-ui/icons/Update';
import AvTimerIcon from '@material-ui/icons/AvTimer'; import AvTimerIcon from '@material-ui/icons/AvTimer';
import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus'; import { isSynchronized, ntpStatusHighlight, ntpStatus } from '../constants/NTPStatus';
import * as Highlight from '../constants/Highlight'; import * as Highlight from '../constants/Highlight';
import { unixTimeToTimeAndDate } from '../constants/TimeFormat'; import { unixTimeToTimeAndDate } from '../constants/TimeFormat';
import { NTP_STATUS_ENDPOINT } from '../constants/Endpoints'; import { NTP_STATUS_ENDPOINT } from '../constants/Endpoints';
import { restComponent } from '../components/RestComponent'; import { restComponent } from '../components/RestComponent';
import SectionContent from '../components/SectionContent'; import SectionContent from '../components/SectionContent';
@ -51,52 +52,61 @@ class NTPStatus extends Component {
this.props.loadData(); this.props.loadData();
} }
createListItems(data, classes){ createListItems(data, classes) {
return ( return (
<Fragment> <Fragment>
<ListItem> <ListItem >
<Avatar className={classes["ntpStatus_" + ntpStatusHighlight(data)]}> <ListItemAvatar>
<UpdateIcon /> <Avatar className={classes["ntpStatus_" + ntpStatusHighlight(data)]}>
</Avatar> <UpdateIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Status" secondary={ntpStatus(data)} /> <ListItemText primary="Status" secondary={ntpStatus(data)} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
{ isSynchronized(data) && {isSynchronized(data) &&
<Fragment> <Fragment>
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<AccessTimeIcon /> <Avatar>
</Avatar> <AccessTimeIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Time Now" secondary={unixTimeToTimeAndDate(data.now)} /> <ListItemText primary="Time Now" secondary={unixTimeToTimeAndDate(data.now)} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<SwapVerticalCircleIcon /> <Avatar>
</Avatar> <SwapVerticalCircleIcon />
<ListItemText primary="Last Sync" secondary={data.last_sync > 0 ? unixTimeToTimeAndDate(data.last_sync) : "never" } /> </Avatar></ListItemAvatar>
<ListItemText primary="Last Sync" secondary={data.last_sync > 0 ? unixTimeToTimeAndDate(data.last_sync) : "never"} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
</Fragment> </Fragment>
} }
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<DNSIcon /> <Avatar>
</Avatar> <DNSIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="NTP Server" secondary={data.server} /> <ListItemText primary="NTP Server" secondary={data.server} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<TimerIcon /> <Avatar>
</Avatar> <TimerIcon />
</Avatar></ListItemAvatar>
<ListItemText primary="Sync Interval" secondary={moment.duration(data.interval, 'seconds').humanize()} /> <ListItemText primary="Sync Interval" secondary={moment.duration(data.interval, 'seconds').humanize()} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<AvTimerIcon /> <Avatar>
</Avatar> <AvTimerIcon />
</Avatar></ListItemAvatar>
<ListItemText primary="Uptime" secondary={moment.duration(data.uptime, 'seconds').humanize()} /> <ListItemText primary="Uptime" secondary={moment.duration(data.uptime, 'seconds').humanize()} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
@ -104,8 +114,8 @@ class NTPStatus extends Component {
); );
} }
renderNTPStatus(data, classes){ renderNTPStatus(data, classes) {
return ( return (
<div> <div>
<List> <List>
{this.createListItems(data, classes)} {this.createListItems(data, classes)}
@ -118,30 +128,30 @@ class NTPStatus extends Component {
} }
render() { render() {
const { data, fetched, errorMessage, classes } = this.props; const { data, fetched, errorMessage, classes } = this.props;
return ( return (
<SectionContent title="NTP Status"> <SectionContent title="NTP Status">
{ {
!fetched ? !fetched ?
<div> <div>
<LinearProgress className={classes.fetching}/> <LinearProgress className={classes.fetching} />
<Typography variant="h4" className={classes.fetching}> <Typography variant="h4" className={classes.fetching}>
Loading... Loading...
</Typography> </Typography>
</div> </div>
: :
data ? this.renderNTPStatus(data, classes) data ? this.renderNTPStatus(data, classes)
: :
<div> <div>
<Typography variant="h4" className={classes.fetching}> <Typography variant="h4" className={classes.fetching}>
{errorMessage} {errorMessage}
</Typography> </Typography>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}> <Button variant="contained" color="secondary" className={classes.button} onClick={this.props.loadData}>
Refresh Refresh
</Button> </Button>
</div> </div>
} }
</SectionContent> </SectionContent>
) )
} }

View File

@ -2,14 +2,14 @@ import React, { Component } from 'react';
import MenuAppBar from '../components/MenuAppBar'; import MenuAppBar from '../components/MenuAppBar';
import ManageUsers from './ManageUsers'; import ManageUsers from './ManageUsers';
class UserConfiguration extends Component { class Security extends Component {
render() { render() {
return ( return (
<MenuAppBar sectionTitle="User Configuration"> <MenuAppBar sectionTitle="Security">
<ManageUsers /> <ManageUsers />
</MenuAppBar> </MenuAppBar>
) )
} }
} }
export default UserConfiguration export default Security

View File

@ -8,6 +8,7 @@ import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List'; import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem'; import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText'; import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar'; import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider'; import Divider from '@material-ui/core/Divider';
@ -63,9 +64,11 @@ class WiFiStatus extends Component {
return ( return (
<Fragment> <Fragment>
<ListItem> <ListItem>
<Avatar className={classes["wifiStatus_" + connectionStatusHighlight(data)]}> <ListItemAvatar>
<WifiIcon /> <Avatar className={classes["wifiStatus_" + connectionStatusHighlight(data)]}>
</Avatar> <WifiIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Connection Status" secondary={connectionStatus(data)} /> <ListItemText primary="Connection Status" secondary={connectionStatus(data)} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
@ -73,40 +76,52 @@ class WiFiStatus extends Component {
isConnected(data) && isConnected(data) &&
<Fragment> <Fragment>
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<SettingsInputAntennaIcon /> <Avatar>
</Avatar> <SettingsInputAntennaIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="SSID" secondary={data.ssid} /> <ListItemText primary="SSID" secondary={data.ssid} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar>IP</Avatar> <ListItemAvatar>
<Avatar>IP</Avatar>
</ListItemAvatar>
<ListItemText primary="IP Address" secondary={data.local_ip} /> <ListItemText primary="IP Address" secondary={data.local_ip} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<DeviceHubIcon /> <Avatar>
</Avatar> <DeviceHubIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="MAC Address" secondary={data.mac_address} /> <ListItemText primary="MAC Address" secondary={data.mac_address} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar>#</Avatar> <ListItemAvatar>
<Avatar>#</Avatar>
</ListItemAvatar>
<ListItemText primary="Subnet Mask" secondary={data.subnet_mask} /> <ListItemText primary="Subnet Mask" secondary={data.subnet_mask} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<SettingsInputComponentIcon /> <Avatar>
</Avatar> <SettingsInputComponentIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Gateway IP" secondary={data.gateway_ip ? data.gateway_ip : "none"} /> <ListItemText primary="Gateway IP" secondary={data.gateway_ip ? data.gateway_ip : "none"} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />
<ListItem> <ListItem>
<Avatar> <ListItemAvatar>
<DNSIcon /> <Avatar>
</Avatar> <DNSIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="DNS Server IP" secondary={this.dnsServers(data)} /> <ListItemText primary="DNS Server IP" secondary={this.dnsServers(data)} />
</ListItem> </ListItem>
<Divider variant="inset" component="li" /> <Divider variant="inset" component="li" />

View File

@ -1,23 +1,26 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { ValidatorForm } from 'react-material-ui-form-validator'; import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { withStyles } from '@material-ui/core/styles'; import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
import LinearProgress from '@material-ui/core/LinearProgress'; import LinearProgress from '@material-ui/core/LinearProgress';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import Table from '@material-ui/core/Table'; import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody'; import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell'; import TableCell from '@material-ui/core/TableCell';
import TableFooter from '@material-ui/core/TableFooter';
import TableHead from '@material-ui/core/TableHead'; import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow'; import TableRow from '@material-ui/core/TableRow';
import IconButton from '@material-ui/core/IconButton'; import Chip from '@material-ui/core/Chip';
import EditIcon from '@material-ui/icons/Edit'; import EditIcon from '@material-ui/icons/Edit';
import DeleteIcon from '@material-ui/icons/Delete'; import DeleteIcon from '@material-ui/icons/Delete';
import Chip from '@material-ui/core/Chip'; import IconButton from '@material-ui/core/IconButton';
import SectionContent from '../components/SectionContent';
import UserForm from './UserForm';
const styles = theme => ({ const styles = theme => ({
loadingSettings: { loadingSettings: {
@ -43,95 +46,198 @@ const styles = theme => ({
margin: theme.spacing.unit, margin: theme.spacing.unit,
}, },
table: { table: {
'& td, & th': {padding: theme.spacing.unit} '& td, & th': { padding: theme.spacing.unit }
} },
actions: {
color: theme.palette.text.secondary,
}
}); });
function compareUsers(a, b) {
if (a.username < b.username) {
return -1;
}
if (a.username > b.username) {
return 1;
}
return 0;
}
class ManageUsersForm extends React.Component { class ManageUsersForm extends React.Component {
render() { constructor(props) {
const { classes, users, usersFetched, errorMessage, onSubmit, onReset } = this.props; super(props);
return ( this.state = {};
<div> }
{
!usersFetched ?
createUser = () => {
this.setState({
creating: true,
user: {
username: "",
password: "",
roles: []
}
});
};
uniqueUsername = username => {
return !this.props.userData.users.find(u => u.username === username);
}
usersValid = username => {
return !!this.props.userData.users.find(u => u.roles.includes("admin"));
}
startEditingUser = user => {
this.setState({
creating: false,
user
});
};
cancelEditingUser = () => {
this.setState({
user: undefined
});
}
sortedUsers(users) {
return users.sort(compareUsers);
}
doneEditingUser = () => {
const { user } = this.state;
const { userData } = this.props;
let { users } = userData;
users = users.filter(u => u.username !== user.username);
users.push(user);
this.props.setData({ ...userData, users });
this.setState({
user: undefined
});
};
handleUserValueChange = name => event => {
const { user } = this.state;
if (user) {
this.setState({
user: {
...user, [name]: event.target.value
}
});
}
};
render() {
const { classes, userData, userDataFetched, errorMessage, onSubmit, onReset, handleValueChange } = this.props;
const { user, creating } = this.state;
return (
<SectionContent title="Manage Users">
{
!userDataFetched ?
<div className={classes.loadingSettings}> <div className={classes.loadingSettings}>
<LinearProgress className={classes.loadingSettingsDetails} /> <LinearProgress className={classes.loadingSettingsDetails} />
<Typography variant="h4" className={classes.loadingSettingsDetails}> <Typography variant="h4" className={classes.loadingSettingsDetails}>
Loading... Loading...
</Typography> </Typography>
</div> </div>
:
: users ? userData ?
user ?
<ValidatorForm onSubmit={onSubmit}> <UserForm
<Table className={classes.table}> user={user}
<TableHead> creating={creating}
<TableRow> roles={userData.roles}
<TableCell>Username</TableCell> onDoneEditing={this.doneEditingUser}
<TableCell>Password</TableCell> onCancelEditing={this.cancelEditingUser}
<TableCell align="center">Role(s)</TableCell> handleValueChange={this.handleUserValueChange}
<TableCell align="center">Action</TableCell> uniqueUsername={this.uniqueUsername}
</TableRow> />
</TableHead> :
<TableBody> <ValidatorForm onSubmit={onSubmit}>
{users.users.map(user => ( <Table className={classes.table}>
<TableRow key={user.username}> <TableHead>
<TableCell component="th" scope="row"> <TableRow>
{user.username} <TableCell>Username</TableCell>
</TableCell> <TableCell align="center">Role(s)</TableCell>
<TableCell>{user.password}</TableCell> <TableCell align="center">Action</TableCell>
<TableCell align="center"> </TableRow>
<Chip label={user.role} className={classes.chip} /> </TableHead>
<TableBody>
{this.sortedUsers(userData.users).map(user => (
<TableRow key={user.username}>
<TableCell component="th" scope="row">
{user.username}
</TableCell>
<TableCell align="center">
{
user.roles.map(role => (
<Chip label={role} className={classes.chip} />
))
}
</TableCell>
<TableCell align="center">
<IconButton aria-label="Delete">
<DeleteIcon />
</IconButton>
<IconButton aria-label="Edit" onClick={() => this.startEditingUser(user)}>
<EditIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={2}>
{
!this.usersValid() &&
<Typography variant="body1" color="error">
You must have at least one admin user configured.
</Typography>
}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
<IconButton aria-label="Delete"> <Button variant="contained" color="secondary" className={classes.button} onClick={this.createUser}>
<DeleteIcon /> Add User
</IconButton> </Button>
<IconButton aria-label="Edit">
<EditIcon />
</IconButton>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} </TableFooter>
</TableBody> </Table>
</Table> <Button variant="contained" color="primary" className={classes.button} type="submit">
Save
<Button variant="contained" color="primary" className={classes.button} type="submit"> </Button>
Save <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
</Button> Reset
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> </Button>
Reset </ValidatorForm>
</Button>
</ValidatorForm>
: :
<SectionContent title="Manage Users">
<div className={classes.loadingSettings}>
<Typography variant="h4" className={classes.loadingSettingsDetails}> <Typography variant="h4" className={classes.loadingSettingsDetails}>
{errorMessage} {errorMessage}
</Typography> </Typography>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}> <Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
Reset Reset
</Button> </Button>
</div> </SectionContent>
} }
</div> </SectionContent>
); );
} }
} }
ManageUsersForm.propTypes = { ManageUsersForm.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
users: PropTypes.object, userData: PropTypes.object,
usersFetched: PropTypes.bool.isRequired, userDataFetched: PropTypes.bool.isRequired,
errorMessage: PropTypes.string, errorMessage: PropTypes.string,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired, onReset: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired, setData: PropTypes.func.isRequired,
handleCheckboxChange: PropTypes.func.isRequired, handleValueChange: PropTypes.func.isRequired
}; };
export default withStyles(styles)(ManageUsersForm); export default withStyles(styles)(ManageUsersForm);

View File

@ -0,0 +1,112 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import PasswordValidator from '../components/PasswordValidator';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';
const styles = theme => ({
textField: {
width: "100%"
},
checkboxControl: {
width: "100%"
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
marginRight: theme.spacing.unit,
},
button: {
marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
}
});
class UserForm extends React.Component {
componentWillMount() {
ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername);
}
render() {
const { classes, user, roles, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
return (
<ValidatorForm onSubmit={onDoneEditing}>
<TextValidator
validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []}
errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []}
name="username"
label="Username"
className={classes.textField}
value={user.username}
disabled={!creating}
onChange={handleValueChange('username')}
margin="normal"
/>
<PasswordValidator
validators={['required', 'matchRegexp:^.{0,64}$']}
errorMessages={['Password is required', 'Password must be 64 characters or less']}
name="password"
label="Password"
className={classes.textField}
value={user.password}
onChange={handleValueChange('password')}
margin="normal"
/>
<FormControl className={classes.textField}>
<InputLabel htmlFor="roles">Roles</InputLabel>
<Select
multiple
value={user.roles}
onChange={handleValueChange('roles')}
input={<Input id="roles" />}
renderValue={selected => (
<div className={classes.chips}>
{selected.map(value => (
<Chip key={value} label={value} className={classes.chip} />
))}
</div>
)}
>
{roles.map(name => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
<Button variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onCancelEditing}>
Back
</Button>
</ValidatorForm>
);
}
}
UserForm.propTypes = {
classes: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
creating: PropTypes.bool.isRequired,
roles: PropTypes.array.isRequired,
onDoneEditing: PropTypes.func.isRequired,
onCancelEditing: PropTypes.func.isRequired,
uniqueUsername: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired
};
export default withStyles(styles)(UserForm);

View File

@ -7,10 +7,9 @@
; ;
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html ; http://docs.platformio.org/page/projectconf.html
[env:esp12e] [env:node32s]
platform = espressif8266 platform = espressif32
board = esp12e board = node32s
board_build.f_cpu = 160000000L
framework = arduino framework = arduino
;upload_flags = --port=8266 --auth=esp-react ;upload_flags = --port=8266 --auth=esp-react

View File

@ -1,9 +1,6 @@
#include <SecurityManager.h> #include <SecurityManager.h>
SecurityManager::SecurityManager(AsyncWebServer* server, FS* fs) : SettingsPersistence(fs, SECURITY_SETTINGS_FILE) { SecurityManager::SecurityManager(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, USERS_PATH, SECURITY_SETTINGS_FILE) {}
server->on(USERS_PATH, HTTP_GET, std::bind(&SecurityManager::fetchUsers, this, std::placeholders::_1));
}
SecurityManager::~SecurityManager() {} SecurityManager::~SecurityManager() {}
void SecurityManager::readFromJsonObject(JsonObject& root) { void SecurityManager::readFromJsonObject(JsonObject& root) {
@ -22,16 +19,19 @@ void SecurityManager::readFromJsonObject(JsonObject& root) {
// users // users
_users.clear(); _users.clear();
if (root["users"].is<JsonArray>()) { if (root["users"].is<JsonArray>()) {
JsonArray users = root["users"]; for (JsonVariant user : root["users"].as<JsonArray>()) {
for (JsonVariant user : users) { std::list<String> roles;
String username = user["username"]; if (user["roles"].is<JsonArray>()) {
String password = user["password"]; for (JsonVariant role : user["roles"].as<JsonArray>()) {
String role = user["role"]; roles.push_back(role.as<String>());
_users.push_back(User(username, password, role)); }
}
_users.push_back(User(user["username"], user["password"], roles));
} }
} }
} }
void SecurityManager::writeToJsonObject(JsonObject& root) { void SecurityManager::writeToJsonObject(JsonObject& root) {
// secret // secret
root["jwt_secret"] = _jwtSecret; root["jwt_secret"] = _jwtSecret;
@ -48,18 +48,13 @@ void SecurityManager::writeToJsonObject(JsonObject& root) {
JsonObject user = users.createNestedObject(); JsonObject user = users.createNestedObject();
user["username"] = _user.getUsername(); user["username"] = _user.getUsername();
user["password"] = _user.getPassword(); user["password"] = _user.getPassword();
user["role"] = _user.getRole(); JsonArray roles = user.createNestedArray("roles");
for (String _role : _user.getRoles()){
roles.add(_role);
}
} }
} }
void SecurityManager::fetchUsers(AsyncWebServerRequest *request) {
AsyncJsonResponse * response = new AsyncJsonResponse(MAX_USERS_SIZE);
JsonObject jsonObject = response->getRoot();
writeToJsonObject(jsonObject);
response->setLength();
request->send(response);
}
void SecurityManager::begin() { void SecurityManager::begin() {
// read config // read config
readFromFS(); readFromFS();
@ -105,7 +100,10 @@ Authentication SecurityManager::authenticate(String username, String password) {
inline void populateJWTPayload(JsonObject &payload, User *user) { inline void populateJWTPayload(JsonObject &payload, User *user) {
payload["username"] = user->getUsername(); payload["username"] = user->getUsername();
payload["role"] = user->getRole(); JsonArray roles = payload.createNestedArray("roles");
for (String _role : user->getRoles()){
roles.add(_role);
}
} }
boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) { boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) {

View File

@ -28,17 +28,17 @@ class User {
private: private:
String _username; String _username;
String _password; String _password;
String _role; std::list<String> _roles;
public: public:
User(String username, String password, String role): _username(username), _password(password), _role(role) {} User(String username, String password, std::list<String> roles): _username(username), _password(password), _roles(roles) {}
String getUsername() { String getUsername() {
return _username; return _username;
} }
String getPassword() { String getPassword() {
return _password; return _password;
} }
String getRole() { std::list<String> getRoles() {
return _role; return _roles;
} }
}; };
@ -62,7 +62,7 @@ class Authentication {
} }
}; };
class SecurityManager : public SettingsPersistence { class SecurityManager : public SettingsService {
public: public: