Stian Aune 5 years ago
parent
commit
d82bbf53f8
  1. 501
      webui/package-lock.json
  2. 12
      webui/package.json
  3. 3
      webui/proxy.json
  4. 13
      webui/src/Actions/lightActions.js
  5. 4
      webui/src/App.css
  6. 48
      webui/src/App.js
  7. 24
      webui/src/Components/Header.jsx
  8. 30
      webui/src/Components/IndexPage.jsx
  9. 42
      webui/src/Components/LoginForm.jsx
  10. 37
      webui/src/Helpers/fetcher.js
  11. 92
      webui/src/Helpers/groups.js
  12. 7
      webui/src/Helpers/keys.js
  13. 7
      webui/src/Helpers/null.js
  14. 12
      webui/src/Helpers/random.js
  15. 65
      webui/src/Hooks/auth.js
  16. 20
      webui/src/Hooks/lights.js
  17. 36
      webui/src/Reducers/authReducer.js
  18. 10
      webui/src/Reducers/index.js
  19. 21
      webui/src/Reducers/lightReducer.js
  20. 10
      webui/src/index.js
  21. 15
      webui/src/setupProxy.js

501
webui/package-lock.json

@ -885,6 +885,12 @@
"loader-utils": "^1.1.0"
}
},
"@types/node": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.0.tgz",
"integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg==",
"dev": true
},
"@types/prop-types": {
"version": "15.5.8",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz",
@ -906,14 +912,14 @@
"csstype": "^2.2.0"
}
},
"@types/react-redux": {
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-6.0.13.tgz",
"integrity": "sha512-69lPYT69CSvzQ75Ut4MfT+oQU9TAjCycdL6BQOrPztqbzdhzSe1d4Qj8vrMkTOm454Xz151zh+YT6S4t2OBopw==",
"@types/reactstrap": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@types/reactstrap/-/reactstrap-7.1.3.tgz",
"integrity": "sha512-WUYfvKLIOfJG/bZI5WmWLbrVk5Q1usloszE/+tmW/B0SQD8ZaiUkT9X1HIY54ysGvt6CTRoqxj5GMlFi5pPWYA==",
"dev": true,
"requires": {
"@types/react": "*",
"redux": "^4.0.0"
"popper.js": "^1.14.1"
}
},
"@types/tapable": {
@ -6309,6 +6315,28 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
"history": {
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
"integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
"requires": {
"invariant": "^2.2.1",
"loose-envify": "^1.2.0",
"resolve-pathname": "^2.2.0",
"value-equal": "^0.4.0",
"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"
}
}
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@ -6325,12 +6353,9 @@
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
},
"hoist-non-react-statics": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.1.tgz",
"integrity": "sha512-TFsu3TV3YLY+zFTZDrN8L2DTFanObwmBLpWvJs1qfUuEQ5bTAdFcwfx2T/bsCXfM9QHSLvjfP+nihEl0yvozxw==",
"requires": {
"react-is": "^16.3.2"
}
"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=="
},
"home-or-tmp": {
"version": "2.0.0",
@ -6507,30 +6532,34 @@
}
},
"http-proxy-middleware": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
"integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
"integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
"dev": true,
"requires": {
"http-proxy": "^1.16.2",
"http-proxy": "^1.17.0",
"is-glob": "^4.0.0",
"lodash": "^4.17.5",
"micromatch": "^3.1.9"
"lodash": "^4.17.11",
"micromatch": "^3.1.10"
},
"dependencies": {
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
"dev": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
@ -6548,6 +6577,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -6558,6 +6588,7 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
@ -6566,6 +6597,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
"integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
"dev": true,
"requires": {
"debug": "^2.3.3",
"define-property": "^0.2.5",
@ -6580,6 +6612,7 @@
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
"integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
"dev": true,
"requires": {
"is-descriptor": "^0.1.0"
}
@ -6588,6 +6621,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -6596,6 +6630,7 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"dev": true,
"requires": {
"kind-of": "^3.0.2"
},
@ -6604,6 +6639,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"requires": {
"is-buffer": "^1.1.5"
}
@ -6614,6 +6650,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"dev": true,
"requires": {
"kind-of": "^3.0.2"
},
@ -6622,6 +6659,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"requires": {
"is-buffer": "^1.1.5"
}
@ -6632,6 +6670,7 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
@ -6641,7 +6680,8 @@
"kind-of": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
"dev": true
}
}
},
@ -6649,6 +6689,7 @@
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
"dev": true,
"requires": {
"array-unique": "^0.3.2",
"define-property": "^1.0.0",
@ -6664,6 +6705,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
"integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
"dev": true,
"requires": {
"is-descriptor": "^1.0.0"
}
@ -6672,6 +6714,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -6682,6 +6725,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
@ -6693,6 +6737,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -6703,6 +6748,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
}
@ -6711,6 +6757,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
}
@ -6719,6 +6766,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
"is-data-descriptor": "^1.0.0",
@ -6728,12 +6776,14 @@
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
},
"is-glob": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
"integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
@ -6742,6 +6792,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"dev": true,
"requires": {
"kind-of": "^3.0.2"
},
@ -6750,6 +6801,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"requires": {
"is-buffer": "^1.1.5"
}
@ -6759,12 +6811,14 @@
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
},
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
"requires": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
@ -6784,7 +6838,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
@ -11542,11 +11597,6 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.2.tgz",
"integrity": "sha512-7kEBKwU9R8fKnZJBRa5RSIfay4KJwnYvKB6gODGicUmDSAhQJ7Tdnll5S0RLtYrzRfMVXlqYw61rzrSpP4ThLQ=="
},
"react-is": {
"version": "16.7.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.7.0.tgz",
"integrity": "sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@ -11561,34 +11611,48 @@
"prop-types": "^15.6.1"
}
},
"react-redux": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.0.tgz",
"integrity": "sha512-EmbC3uLl60pw2VqSSkj6HpZ6jTk12RMrwXMBdYtM6niq0MdEaRq9KYCwpJflkOZj349BLGQm1MI/JO1W96kLWQ==",
"react-router": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
"integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==",
"requires": {
"@babel/runtime": "^7.2.0",
"hoist-non-react-statics": "^3.2.1",
"history": "^4.7.2",
"hoist-non-react-statics": "^2.5.0",
"invariant": "^2.2.4",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.3"
"loose-envify": "^1.3.1",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.1",
"warning": "^4.0.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
"integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"requires": {
"regenerator-runtime": "^0.12.0"
"isarray": "0.0.1"
}
},
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}
}
},
"react-router-dom": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz",
"integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==",
"requires": {
"history": "^4.7.2",
"invariant": "^2.2.4",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.1",
"react-router": "^4.3.1",
"warning": "^4.0.1"
}
},
"react-scripts": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.3.tgz",
@ -11645,9 +11709,9 @@
}
},
"react-transition-group": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.2.tgz",
"integrity": "sha512-vwHP++S+f6KL7rg8V1mfs62+MBKtbMeZDR8KiNmD7v98Gs3UPGsDZDahPJH2PVprFW5YHJfh6cbNim3zPndaSQ==",
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz",
"integrity": "sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg==",
"requires": {
"dom-helpers": "^3.3.1",
"loose-envify": "^1.4.0",
@ -11655,10 +11719,18 @@
"react-lifecycles-compat": "^3.0.4"
}
},
"reactn": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reactn/-/reactn-0.2.2.tgz",
"integrity": "sha512-8yjBMXg3V1RsIqM9aAHbjRQyL46pwSpd3JgFD5XVr/zsbUommhIMT9HHiDEWhpUTmKriKegy+ZFaKWJRhDjJoQ==",
"requires": {
"use-force-update": "^1.0.0"
}
},
"reactstrap": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-7.0.2.tgz",
"integrity": "sha512-VVa/1tqCiKa63YPwAIWrn4za806TVva/GEx7yP/2U+xmq1iKIzGo0UHmFL/6XpZKqx/ZzP/olkTorP/2o/2pTg==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-7.1.0.tgz",
"integrity": "sha512-wtc4RkgnGn1TsZ0AxOZ2OqT+b8YmCWZj/tErPujWLepxzlEEhveZGC+uDerdaHVSAzJUP2DTk605iper7hutQQ==",
"requires": {
"@babel/runtime": "^7.2.0",
"classnames": "^2.2.3",
@ -11672,9 +11744,9 @@
},
"dependencies": {
"@babel/runtime": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
"integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.1.tgz",
"integrity": "sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==",
"requires": {
"regenerator-runtime": "^0.12.0"
}
@ -12022,15 +12094,6 @@
"minimatch": "3.0.4"
}
},
"redux": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
"integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
"requires": {
"loose-envify": "^1.4.0",
"symbol-observable": "^1.2.0"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@ -12314,6 +12377,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
"resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
"integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@ -13565,11 +13633,6 @@
"util.promisify": "~1.0.0"
}
},
"symbol-observable": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
},
"symbol-tree": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
@ -14218,6 +14281,11 @@
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
},
"use-force-update": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/use-force-update/-/use-force-update-1.0.2.tgz",
"integrity": "sha512-/DOz91GCBRg/xMWx9+23JJvo4jLUiksnL4d+OFbbeJEm7BV5Ckn+y+1Z527niwhK8xcHh4zFgkPBtzWyrMkEyg=="
},
"util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
@ -14264,6 +14332,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
"value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
"integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -14318,6 +14391,14 @@
"makeerror": "1.0.x"
}
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"watch": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
@ -14710,6 +14791,43 @@
"yargs": "12.0.2"
},
"dependencies": {
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
},
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"requires": {
"arr-flatten": "^1.1.0",
"array-unique": "^0.3.2",
"extend-shallow": "^2.0.1",
"fill-range": "^4.0.0",
"isobject": "^3.0.1",
"repeat-element": "^1.1.2",
"snapdragon": "^0.8.1",
"snapdragon-node": "^2.0.1",
"split-string": "^3.0.2",
"to-regex": "^3.0.1"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"camelcase": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
@ -14753,6 +14871,156 @@
"strip-eof": "^1.0.0"
}
},
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
"integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
"requires": {
"debug": "^2.3.3",
"define-property": "^0.2.5",
"extend-shallow": "^2.0.1",
"posix-character-classes": "^0.1.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"define-property": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
"integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
"requires": {
"is-descriptor": "^0.1.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
"requires": {
"is-accessor-descriptor": "^0.1.6",
"is-data-descriptor": "^0.1.4",
"kind-of": "^5.0.0"
}
},
"kind-of": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
"requires": {
"array-unique": "^0.3.2",
"define-property": "^1.0.0",
"expand-brackets": "^2.1.4",
"extend-shallow": "^2.0.1",
"fragment-cache": "^0.2.1",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.1"
},
"dependencies": {
"define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
"integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
"requires": {
"is-descriptor": "^1.0.0"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
"requires": {
"extend-shallow": "^2.0.1",
"is-number": "^3.0.0",
"repeat-string": "^1.6.1",
"to-regex-range": "^2.1.0"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
@ -14769,6 +15037,17 @@
"pump": "^3.0.0"
}
},
"http-proxy-middleware": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
"integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
"requires": {
"http-proxy": "^1.16.2",
"is-glob": "^4.0.0",
"lodash": "^4.17.5",
"micromatch": "^3.1.9"
}
},
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@ -14783,6 +15062,68 @@
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA=="
},
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"requires": {
"kind-of": "^6.0.0"
}
},
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"requires": {
"kind-of": "^6.0.0"
}
},
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"requires": {
"is-accessor-descriptor": "^1.0.0",
"is-data-descriptor": "^1.0.0",
"kind-of": "^6.0.2"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-glob": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
"integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
"requires": {
"is-extglob": "^2.1.1"
}
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
"requires": {
"kind-of": "^3.0.2"
},
"dependencies": {
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"requires": {
"is-buffer": "^1.1.5"
}
}
}
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
@ -14810,6 +15151,26 @@
"p-is-promise": "^1.1.0"
}
},
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"requires": {
"arr-diff": "^4.0.0",
"array-unique": "^0.3.2",
"braces": "^2.3.1",
"define-property": "^2.0.2",
"extend-shallow": "^3.0.2",
"extglob": "^2.0.4",
"fragment-cache": "^0.2.1",
"kind-of": "^6.0.2",
"nanomatch": "^1.2.9",
"object.pick": "^1.3.0",
"regex-not": "^1.0.0",
"snapdragon": "^0.8.1",
"to-regex": "^3.0.2"
}
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",

12
webui/package.json

@ -6,15 +6,17 @@
"bootstrap": "^4.2.1",
"react": "^16.8.1",
"react-dom": "^16.8.1",
"react-redux": "^6.0.0",
"react-scripts": "2.1.3",
"reactstrap": "^7.0.2",
"redux": "^4.0.1"
"reactstrap": "^7.1.0",
"reactn": "^0.2.2",
"react-router-dom": "^4.3.1",
"react-router": "^4.3.1"
},
"devDependencies": {
"@types/react": "^16.8.1",
"@types/react-redux": "^6.0.0",
"@types/reactstrap": "^7.0.2"
"@types/reactstrap": "^7.1.0",
"http-proxy-middleware": "latest",
"@types/node": "^11.9.0"
},
"scripts": {
"start": "react-scripts start",

3
webui/proxy.json

@ -0,0 +1,3 @@
{
"url": "http://10.32.7.1:8000/"
}

13
webui/src/Actions/lightActions.js

@ -1,13 +0,0 @@
import store from "../Reducers";
import {fetchGet} from "../Helpers/fetcher";
import {lightsReceivedEvent} from "../Reducers/lightReducer";
const dispatch = store.dispatch;
export function getLights() {
fetchGet("/light/").then(res => {
if (res !== null) {
dispatch(lightsReceivedEvent(res));
}
});
}

4
webui/src/App.css

@ -2,6 +2,6 @@
cursor: pointer;
}
button {
a, button {
cursor: pointer;
}
}

48
webui/src/App.js

@ -1,20 +1,44 @@
import React, {Component} from 'react';
import React, {useEffect, useState} from 'react';
import './App.css';
import Header from "./Components/Header";
import LoginForm from "./Components/LoginForm";
import {Container} from "reactstrap";
import {Container, Spinner} from "reactstrap";
import {BrowserRouter} from "react-router-dom";
import {Route} from "react-router";
import {setGlobal} from "reactn";
import useAuth from "./Hooks/auth";
import IndexPage from "./Components/IndexPage";
class App extends Component {
render() {
return (
<div>
setGlobal({
"auth/login": false,
"auth/checked": false,
});
export default function App() {
const [hasStarted, setHasStarted] = useState(false);
const {isLoggedIn, isChecked, verify} = useAuth();
useEffect(() => {
if (!hasStarted) {
verify();
setHasStarted(true);
}
});
return (
<BrowserRouter>
<>
<Header/>
<Container>
<LoginForm/>
{!isChecked && <Spinner color="primary" style={{width: "4rem", height: "4rem"}}/>}
{isChecked && !isLoggedIn && <LoginForm/>}
{isChecked && isLoggedIn && (
<>
<Route exact path="/" component={IndexPage} />
</>
)}
</Container>
</div>
);
}
</>
</BrowserRouter>
);
}
export default App;

24
webui/src/Components/Header.jsx

@ -1,29 +1,31 @@
import React, {useState} from "react"
import {Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink} from "reactstrap";
import {connect} from "react-redux";
import useAuth from "../Hooks/auth";
import {Link} from "react-router-dom";
import {onEnter} from "../Helpers/keys";
function Header({isLoggedIn}) {
export default function Header() {
const [showMenu, setShowMenu] = useState(false);
const {isLoggedIn, logout} = useAuth();
return (
<Navbar color="dark" dark expand="md">
<NavbarBrand href="/">Lucifer</NavbarBrand>
<NavbarBrand tag={Link} to="/">Lucifer</NavbarBrand>
<NavbarToggler onClick={() => setShowMenu(!showMenu)}/>
<Collapse isOpen={showMenu} navbar>
{isLoggedIn && (
<Nav className="ml-auto" navbar>
<NavItem>
<NavLink href="/lights">Grupper</NavLink>
<NavLink tag={Link} to="/lights">Grupper</NavLink>
</NavItem>
<NavItem>
<NavLink tabIndex={0}
onClick={() => logout()}
onKeyDown={onEnter(() => logout())}>Logg ut</NavLink>
</NavItem>
</Nav>
)}
</Collapse>
</Navbar>
);
}
const mapStateToProps = (state) => ({
isLoggedIn: state.auth.isLoggedIn,
});
export default connect(mapStateToProps)(Header);
}

30
webui/src/Components/IndexPage.jsx

@ -0,0 +1,30 @@
import React from "react";
import useGroups from "../Hooks/lights";
import {Button, Card, CardFooter, CardHeader, Spinner} from "reactstrap";
function IndexPage() {
const groups = useGroups();
if (groups === null) {
return (
<div>
<Spinner color="primary" style={{width: "4rem", height: "4rem"}}/>
</div>
);
}
return (
<div>
{groups.map(group => (
<Card className="mt-3">
<CardHeader>{group.name}</CardHeader>
<CardFooter>
<Button color="secondary">Endre</Button>
</CardFooter>
</Card>
))}
</div>
);
}
export default IndexPage;

42
webui/src/Components/LoginForm.jsx

@ -1,24 +1,27 @@
import React, {useState} from "react";
import {connect} from "react-redux";
import {
Button,
Card,
CardBody,
CardFooter,
CardHeader,
Col,
Form,
FormGroup,
Input,
Label,
Nav,
NavItem,
NavLink,
CardFooter,
Nav,
TabPane
} from "reactstrap";
import TabContent from "reactstrap/es/TabContent";
import useAuth from "../Hooks/auth";
import {onEnter} from "../Helpers/keys";
export default function LoginForm() {
const {login, register} = useAuth();
function LoginForm(props) {
const [tab, setTab] = useState(1);
const [loginUser, setLoginUser] = useState("");
const [loginPass, setLoginPass] = useState("");
@ -34,6 +37,8 @@ function LoginForm(props) {
<NavItem>
<NavLink className={tab === 1 ? "active" : ""}
onClick={() => setTab(1)}
onKeyDown={onEnter(() => setTab(1))}
tabIndex={0}
>
Innlogging
</NavLink>
@ -41,6 +46,8 @@ function LoginForm(props) {
<NavItem>
<NavLink className={tab === 2 ? "active" : ""}
onClick={() => setTab(2)}
onKeyDown={onEnter(() => setTab(2))}
tabIndex={0}
>
Registrering
</NavLink>
@ -108,24 +115,27 @@ function LoginForm(props) {
</TabContent>
</CardBody>
<CardFooter>
{tab === 1 && <Button color="primary">Logg inn</Button>}
{tab === 1 && <Button color="primary" onClick={() => login(loginUser, loginPass)}>Logg inn</Button>}
{tab === 2 && (
<>
<Button color="primary">Registrer</Button>
<Button color="primary"
onClick={() => register(regUser, regPass1, regPass2)}
>
Registrer
</Button>
{" "}
<Button color="secondary" onClick={() => {
setRegUser("");
setRegPass1("");
setRegPass2("");
setTab(1);
}}>Avbryt</Button>
<Button color="secondary"
onClick={() => {
setRegUser("");
setRegPass1("");
setRegPass2("");
setTab(1);
}}>
Avbryt
</Button>
</>
)}
</CardFooter>
</Card>
);
}
const mapStateToProps = (state) => ({});
export default connect(mapStateToProps)(LoginForm);

37
webui/src/Helpers/fetcher.js

@ -1,10 +1,5 @@
import store from "../Reducers";
import {verificationFailedEvent} from "../Reducers/authReducer";
const PREFIX = "/api";
const dispatch = store.dispatch;
function formatUrl(url, params = {}) {
const urlWithPrefix = PREFIX + url;
@ -22,22 +17,27 @@ function formatUrl(url, params = {}) {
/**
* @param {Response} res
* @return {Promise<array<object, object>>}
*/
function authCheck(res) {
if (res.ok) {
return Promise.resolve(res.json());
} else {
if (res.status === 403) {
dispatch(verificationFailedEvent());
// Allow the promise to sort of fizzle out
return Promise.resolve(null);
return res.json().then(json => {
if (typeof json.data === "undefined") {
json.data = null;
}
return Promise.reject({ status: res.status, text: res.statusText });
}
if (typeof json.error === "undefined") {
json.error = null;
}
return Promise.resolve(json);
})
}
/**
* @param {string} url
* @param {object} params
* @returns {Promise<object|null>}
*/
export function fetchGet(url, params = {}) {
return fetch(formatUrl(url, params), {
method: "GET",
@ -45,7 +45,12 @@ export function fetchGet(url, params = {}) {
}).then(authCheck);
}
export function fetchPost(url, data) {
/**
* @param {string} url
* @param {object} data
* @returns {Promise<object|null>}
*/
export function fetchPost(url, data = {}) {
return fetch(formatUrl(url), {
method: "POST",
credentials: "include",

92
webui/src/Helpers/groups.js

@ -0,0 +1,92 @@
import {randId} from "./random";
import {fetchGet} from "./fetcher";
import {notNullish} from "./null";
const localData = {};
const callbacks = [];
export function subscribeToGroup(groupId, callback) {
const callbackId = randId();
callbacks.push({callbackId, groupId, callback});
if (groupId >= 0) {
if (notNullish(localData[groupId])) {
dispatch(localData[groupId]);
}
fetchOne(groupId);
} else {
const list = [];
for (let key in localData) {
if (localData.hasOwnProperty(key) && notNullish(localData[key])) {
list.push(localData[key]);
}
}
dispatch(list);
fetchAll();
}
console.log("subscribed");
console.dir(callbacks);
return callbackId;
}
export function unsubscribeFromGroup(callbackId) {
const callback = callbacks.find(c => c !== null && c.callbackId === callbackId);
const index = callbacks.indexOf(callback);
callbacks[index] = null;
console.log("unsubscribed");
console.dir(callbacks);
}
function fetchAll() {
fetchGet("/group/").then(({data, error}) => {
if (error === null) {
handleGroups(data);
}
});
}
function fetchOne(id) {
fetchGet(`/group/${id}`).then(({data, error}) => {
if (error === null) {
handleGroup(data);
}
});
}
function handleGroups(groups) {
groups.forEach(g => handleGroup(g));
for (let key in localData) {
if (localData.hasOwnProperty(key) && !groups.hasOwnProperty(key)) {
delete localData[key];
}
}
dispatch(groups);
}
function handleGroup(group) {
localData[group.id] = group;
dispatch(group);
}
function dispatch(data) {
if (Array.isArray(data)) {
callbacks
.filter(c => c !== null)
.filter(c => c.groupId === -1)
.forEach(c => c.callback(data));
} else {
callbacks
.filter(c => c !== null)
.filter(c => c.groupId === data.id)
.forEach(c => c.callback(data));
}
}

7
webui/src/Helpers/keys.js

@ -0,0 +1,7 @@
export function onEnter(callback) {
return event => {
if (event.keyCode === 13) {
callback();
}
}
}

7
webui/src/Helpers/null.js

@ -0,0 +1,7 @@
export function nullish(value) {
return typeof value === "undefined" || value === null;
}
export function notNullish(value) {
return !nullish(value);
}

12
webui/src/Helpers/random.js

@ -0,0 +1,12 @@
const POSSIBLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const POSSIBLE_LENGHTH = POSSIBLE.length;
export function randId() {
let text = "";
for (let i = 0; i < 16; i++) {
text += POSSIBLE.charAt(Math.floor(Math.random() * POSSIBLE_LENGHTH));
}
return text;
}

65
webui/src/Hooks/auth.js

@ -0,0 +1,65 @@
import {useGlobal} from "reactn";
import {fetchGet, fetchPost} from "../Helpers/fetcher";
export default function useAuth() {
const [isLoggedIn, setIsLoggedIn] = useGlobal("auth/login");
const [isChecked, setIsChecked] = useGlobal("auth/checked");
function verify() {
setIsChecked(false);
fetchGet("/user/session").then(({data, error}) => {
setIsLoggedIn(error === null && data.loggedIn);
setIsChecked(true);
})
}
function login(username, password) {
setIsChecked(false);
fetchPost("/user/login", {username, password}).then(({data, error}) => {
setIsChecked(true);
console.dir({data, error});
if (error !== null) {
setIsLoggedIn(false);
return;
}
setIsLoggedIn(true);
});
}
function logout() {
setIsChecked(false);
fetchPost("/user/logout").then(({data, error}) => {
setIsChecked(true);
if (error !== null) {
setIsLoggedIn(false);
return;
// TODO: Show errors ¿?
}
setIsLoggedIn(false);
});
}
function register(username, password, repeated) {
setIsChecked(false);
if (password !== repeated) {
alert("Passordene er ikke like");
return;
}
fetchPost("/user/register", {username, password}).then(({data, error}) => {
if (error !== null) {
login(username, password);
}
})
}
return {isLoggedIn, isChecked, verify, login, logout, register};
}

20
webui/src/Hooks/lights.js

@ -0,0 +1,20 @@
import {useEffect, useState} from "react";
import {subscribeToGroup, unsubscribeFromGroup} from "../Helpers/groups";
export default function useGroups(id = -1) {
const [group, setGroup] = useState(null);
function onChange(group) {
setGroup(group);
}
useEffect(() => {
const cbId = subscribeToGroup(id, onChange);
return function cleanup() {
unsubscribeFromGroup(cbId);
};
}, []);
return group;
}

36
webui/src/Reducers/authReducer.js

@ -1,36 +0,0 @@
const initialState = {
isChecked: false,
isLoggedIn: false,
};
const VERIFICATION_STARTED = "auth/verification/started";
const VERIFICATION_SUCCESS = "auth/verification/changed";
const VERIFICATION_FAILED = "auth/verification/failed";
const authReducer = (state = initialState, {type, payload} = {}) => {
switch (type) {
case VERIFICATION_STARTED:
return {
isChecked: false,
isLoggedIn: false,
};
case VERIFICATION_SUCCESS:
return {
isChecked: true,
isLoggedIn: true,
};
case VERIFICATION_FAILED:
return {
isChecked: true,
isLoggedIn: false,
};
default:
return state;
}
};
export const verificationStartedEvent = () => ({ type: VERIFICATION_STARTED });
export const verificationSucceededEvent = () => ({ type: VERIFICATION_SUCCESS });
export const verificationFailedEvent = () => ({ type: VERIFICATION_FAILED });
export default authReducer;

10
webui/src/Reducers/index.js

@ -1,10 +0,0 @@
import {combineReducers, createStore} from "redux";
import authReducer from "./authReducer";
const rootReducer = combineReducers({
auth: authReducer,
});
const store = createStore(rootReducer);
export default store;

21
webui/src/Reducers/lightReducer.js

@ -1,21 +0,0 @@
const initialState = {
lights: [],
};
const LIGHTS_RECEIVED = "LightReducer/Received";
const lightReducer = (state = initialState, {type, payload} = {}) => {
switch (type) {
case LIGHTS_RECEIVED:
return {
...state,
lights: payload,
};
default:
return state;
}
};
export const lightsReceivedEvent = (lights) => ({type: LIGHTS_RECEIVED, payload: lights});
export default lightReducer;

10
webui/src/index.js

@ -3,13 +3,5 @@ import ReactDOM from "react-dom";
import App from "./App";
import "bootstrap/dist/css/bootstrap.min.css";
import {Provider} from "react-redux";
import store from "./Reducers";
ReactDOM.render(
(
<Provider store={store}>
<App/>
</Provider>
),
document.getElementById("root"));
ReactDOM.render(<App/>, document.getElementById("root"));

15
webui/src/setupProxy.js

@ -0,0 +1,15 @@
const fs = require("fs");
const proxy = require("http-proxy-middleware");
let config = {url: "http://10.12.121.228:8100/"};
try {
const data = fs.readFileSync("./proxy.json", "utf-8");
config = JSON.parse(data)
} catch(err) {
console.error("Failed to load proxy.json in project root");
}
module.exports = function(app) {
app.use(proxy('/api/ws', { target: config.url, ws: true }));
app.use(proxy('/api', { target: config.url, ws: false }));
};
Loading…
Cancel
Save