feat: notifications rework
This commit is contained in:
parent
b6e7d98b72
commit
ffa8713ee4
77 changed files with 2281 additions and 3269 deletions
|
|
@ -11,6 +11,8 @@ low(adapter).then(it => {
|
||||||
|
|
||||||
function saveConfig (config) {
|
function saveConfig (config) {
|
||||||
const currentState = db.getState()
|
const currentState = db.getState()
|
||||||
|
// TODO this should be _.assign
|
||||||
|
// change after flattening of schema
|
||||||
const newState = _.mergeWith((objValue, srcValue) => {
|
const newState = _.mergeWith((objValue, srcValue) => {
|
||||||
if (_.isArray(objValue)) {
|
if (_.isArray(objValue)) {
|
||||||
return srcValue
|
return srcValue
|
||||||
|
|
|
||||||
541
new-lamassu-admin/package-lock.json
generated
541
new-lamassu-admin/package-lock.json
generated
|
|
@ -1840,7 +1840,8 @@
|
||||||
"@emotion/hash": {
|
"@emotion/hash": {
|
||||||
"version": "0.7.3",
|
"version": "0.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz",
|
||||||
"integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw=="
|
"integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"@emotion/is-prop-valid": {
|
"@emotion/is-prop-valid": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
|
|
@ -2226,48 +2227,40 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@material-ui/core": {
|
"@material-ui/core": {
|
||||||
"version": "4.5.0",
|
"version": "4.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.8.tgz",
|
||||||
"integrity": "sha512-UHVAjU+1uDtA+OMBNBHb4RlCZOu514XeYPafNJv+GTdXBDr1SCPK7yqRE6TV1/bulxlDusTgu5Q6BAUgpmO4MA==",
|
"integrity": "sha512-4cslpG6oLoPWUfwPkX+hvbak4hAGiOfgXOu/UIYeeMrtsTEebC0Mirjoby7zhS4ny86YI3rXEFW6EZDmlj5n5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.4.4",
|
"@babel/runtime": "^7.4.4",
|
||||||
"@material-ui/styles": "^4.5.0",
|
"@material-ui/styles": "^4.9.6",
|
||||||
"@material-ui/system": "^4.5.0",
|
"@material-ui/system": "^4.9.6",
|
||||||
"@material-ui/types": "^4.1.1",
|
"@material-ui/types": "^5.0.0",
|
||||||
"@material-ui/utils": "^4.4.0",
|
"@material-ui/utils": "^4.9.6",
|
||||||
"@types/react-transition-group": "^4.2.0",
|
"@types/react-transition-group": "^4.2.0",
|
||||||
"clsx": "^1.0.2",
|
"clsx": "^1.0.2",
|
||||||
"convert-css-length": "^2.0.1",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"deepmerge": "^4.0.0",
|
|
||||||
"hoist-non-react-statics": "^3.2.1",
|
|
||||||
"is-plain-object": "^3.0.0",
|
|
||||||
"normalize-scroll-left": "^0.2.0",
|
|
||||||
"popper.js": "^1.14.1",
|
"popper.js": "^1.14.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0",
|
||||||
"react-transition-group": "^4.3.0"
|
"react-transition-group": "^4.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-plain-object": {
|
"hoist-non-react-statics": {
|
||||||
"version": "3.0.0",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
"integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"isobject": "^4.0.0"
|
"react-is": "^16.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"isobject": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
|
|
||||||
},
|
|
||||||
"react-transition-group": {
|
"react-transition-group": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
|
||||||
|
|
@ -2280,134 +2273,176 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@material-ui/icons": {
|
"@material-ui/icons": {
|
||||||
"version": "4.4.3",
|
"version": "4.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz",
|
||||||
"integrity": "sha512-HVVvUyc/78kmaBd93LkfWyGkXMM+zOMKzUfulWXxaV/fFAZ3N0pD0oHjWUd94zrOoF3tZP9JC7EPlIpIcZSNow==",
|
"integrity": "sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.4.4"
|
"@babel/runtime": "^7.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@material-ui/lab": {
|
||||||
|
"version": "4.0.0-alpha.47",
|
||||||
|
"resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.47.tgz",
|
||||||
|
"integrity": "sha512-+WC3O0M/769D3nO9Rqupusc+lob7tQMe5/DnOjAhZ0bpXlJbhZb7N84WkEk4JgQLj6ydP8e9Jhqd1lG+mGj+xw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.4.4",
|
||||||
|
"@material-ui/utils": "^4.9.6",
|
||||||
|
"clsx": "^1.0.4",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"react-is": "^16.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": {
|
||||||
|
"version": "7.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
|
"requires": {
|
||||||
|
"regenerator-runtime": "^0.13.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"regenerator-runtime": {
|
||||||
|
"version": "0.13.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@material-ui/styles": {
|
"@material-ui/styles": {
|
||||||
"version": "4.5.0",
|
"version": "4.9.6",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.6.tgz",
|
||||||
"integrity": "sha512-O0NSAECHK9f3DZK6wy56PZzp8b/7KSdfpJs8DSC7vnXUAoMPCTtchBKLzMtUsNlijiJFeJjSxNdQfjWXgyur5A==",
|
"integrity": "sha512-ijgwStEkw1OZ6gCz18hkjycpr/3lKs1hYPi88O/AUn4vMuuGEGAIrqKVFq/lADmZUNF3DOFIk8LDkp7zmjPxtA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.4.4",
|
"@babel/runtime": "^7.4.4",
|
||||||
"@emotion/hash": "^0.7.1",
|
"@emotion/hash": "^0.8.0",
|
||||||
"@material-ui/types": "^4.1.1",
|
"@material-ui/types": "^5.0.0",
|
||||||
"@material-ui/utils": "^4.1.0",
|
"@material-ui/utils": "^4.9.6",
|
||||||
"clsx": "^1.0.2",
|
"clsx": "^1.0.2",
|
||||||
"csstype": "^2.5.2",
|
"csstype": "^2.5.2",
|
||||||
"deepmerge": "^4.0.0",
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
"hoist-non-react-statics": "^3.2.1",
|
"jss": "^10.0.3",
|
||||||
"jss": "^10.0.0",
|
"jss-plugin-camel-case": "^10.0.3",
|
||||||
"jss-plugin-camel-case": "^10.0.0",
|
"jss-plugin-default-unit": "^10.0.3",
|
||||||
"jss-plugin-default-unit": "^10.0.0",
|
"jss-plugin-global": "^10.0.3",
|
||||||
"jss-plugin-global": "^10.0.0",
|
"jss-plugin-nested": "^10.0.3",
|
||||||
"jss-plugin-nested": "^10.0.0",
|
"jss-plugin-props-sort": "^10.0.3",
|
||||||
"jss-plugin-props-sort": "^10.0.0",
|
"jss-plugin-rule-value-function": "^10.0.3",
|
||||||
"jss-plugin-rule-value-function": "^10.0.0",
|
"jss-plugin-vendor-prefixer": "^10.0.3",
|
||||||
"jss-plugin-vendor-prefixer": "^10.0.0",
|
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@emotion/hash": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||||
|
},
|
||||||
|
"hoist-non-react-statics": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@material-ui/system": {
|
"@material-ui/system": {
|
||||||
"version": "4.5.0",
|
"version": "4.9.6",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.6.tgz",
|
||||||
"integrity": "sha512-vR0PbMTzLnuuVCoYNQ13zyhLa/4s/UA9P9JbNuHBOOkfrHn53ShINiG0v05EgfwizfULLtc7mNvsGAgIyyp/hQ==",
|
"integrity": "sha512-QtfoAePyqXoZ2HUVSwGb1Ro0kucMCvVjbI0CdYIR21t0Opgfm1Oer6ni9P5lfeXA39xSt0wCierw37j+YES48Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.4.4",
|
"@babel/runtime": "^7.4.4",
|
||||||
"deepmerge": "^4.0.0",
|
"@material-ui/utils": "^4.9.6",
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@material-ui/types": {
|
"@material-ui/types": {
|
||||||
"version": "4.1.1",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.0.0.tgz",
|
||||||
"integrity": "sha512-AN+GZNXytX9yxGi0JOfxHrRTbhFybjUJ05rnsBVjcB+16e466Z0Xe5IxawuOayVZgTBNDxmPKo5j4V6OnMtaSQ==",
|
"integrity": "sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg=="
|
||||||
"requires": {
|
|
||||||
"@types/react": "*"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"@material-ui/utils": {
|
"@material-ui/utils": {
|
||||||
"version": "4.4.0",
|
"version": "4.9.6",
|
||||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.6.tgz",
|
||||||
"integrity": "sha512-UXoQVwArQEQWXxf2FPs0iJGT+MePQpKr0Qh0CPoLc1OdF0GSMTmQczcqCzwZkeHxHAOq/NkIKM1Pb/ih1Avicg==",
|
"integrity": "sha512-gqlBn0JPPTUZeAktn1rgMcy9Iczrr74ecx31tyZLVGdBGGzsxzM6PP6zeS7FuoLS6vG4hoZP7hWnOoHtkR0Kvw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.4.4",
|
"@babel/runtime": "^7.4.4",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-is": "^16.8.6"
|
"react-is": "^16.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-is": {
|
|
||||||
"version": "16.10.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz",
|
|
||||||
"integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA=="
|
|
||||||
},
|
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -2509,6 +2544,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
|
||||||
"integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==",
|
"integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3925,9 +3966,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-transition-group": {
|
"@types/react-transition-group": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz",
|
||||||
"integrity": "sha512-Hk8jiuT7iLOHrcjKP/ZVSyCNXK73wJAUz60xm0mVhiRujrdiI++j4duLiL282VGxwAgxetHQFfqA29LgEeSkFA==",
|
"integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
|
|
@ -6906,7 +6947,8 @@
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
|
@ -6927,12 +6969,14 @@
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
|
@ -6947,17 +6991,20 @@
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
@ -7074,7 +7121,8 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
|
@ -7086,6 +7134,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -7100,6 +7149,7 @@
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
|
@ -7107,12 +7157,14 @@
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
|
@ -7131,6 +7183,7 @@
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
|
@ -7211,7 +7264,8 @@
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
|
@ -7223,6 +7277,7 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
|
@ -7308,7 +7363,8 @@
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
|
@ -7344,6 +7400,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
|
@ -7363,6 +7420,7 @@
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -7406,12 +7464,14 @@
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -7991,11 +8051,6 @@
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"convert-css-length": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/convert-css-length/-/convert-css-length-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-iGpbcvhLPRKUbBc0Quxx7w/bV14AC3ItuBEGMahA5WTYqB8lq9jH0kTXFheCBASsYnqeMFZhiTruNxr1N59Axg=="
|
|
||||||
},
|
|
||||||
"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",
|
||||||
|
|
@ -8464,17 +8519,17 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.6.3",
|
"version": "7.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
|
||||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
"integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"regenerator-runtime": "^0.13.2"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.3",
|
"version": "0.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -8762,11 +8817,6 @@
|
||||||
"integrity": "sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==",
|
"integrity": "sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"deepmerge": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-+qO5WbNBKBaZez95TffdUDnGIo4+r5kmsX8aOb7PDHvXsTbghAmleuxjs6ytNaf5Eg4FGBXDS5vqO61TRi6BMg=="
|
|
||||||
},
|
|
||||||
"default-gateway": {
|
"default-gateway": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
||||||
|
|
@ -11291,18 +11341,18 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"formik": {
|
"formik": {
|
||||||
"version": "2.0.3",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/formik/-/formik-2.1.4.tgz",
|
||||||
"integrity": "sha512-kYBvcxlsYSncY8OiJHD49C0UmoWXbgmIc9V1g3N1WwBJ7SMLk34QpcJDgroYd42K1cH+mSJlXhB7PlgTXTzlWg==",
|
"integrity": "sha512-oKz8S+yQBzuQVSEoxkqqJrKQS5XJASWGVn6mrs+oTWrBoHgByVwwI1qHiVc9GKDpZBU9vAxXYAKz2BvujlwunA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"deepmerge": "^2.1.1",
|
"deepmerge": "^2.1.1",
|
||||||
"hoist-non-react-statics": "^3.3.0",
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
"lodash": "^4.17.14",
|
"lodash": "^4.17.14",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
"react-fast-compare": "^2.0.1",
|
"react-fast-compare": "^2.0.1",
|
||||||
"scheduler": "^0.14.0",
|
"scheduler": "^0.18.0",
|
||||||
"tiny-warning": "^1.0.2",
|
"tiny-warning": "^1.0.2",
|
||||||
"tslib": "^1.9.3"
|
"tslib": "^1.10.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": {
|
"deepmerge": {
|
||||||
|
|
@ -11311,13 +11361,18 @@
|
||||||
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
||||||
},
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"version": "0.14.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
|
||||||
"integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==",
|
"integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"object-assign": "^4.1.1"
|
"object-assign": "^4.1.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
|
||||||
|
"integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -14690,22 +14745,48 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-camel-case": {
|
"jss-plugin-camel-case": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz",
|
||||||
"integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==",
|
"integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"hyphenate-style-name": "^1.0.3",
|
"hyphenate-style-name": "^1.0.3",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-default-unit": {
|
"jss-plugin-default-unit": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz",
|
||||||
"integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==",
|
"integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-extend": {
|
"jss-plugin-extend": {
|
||||||
|
|
@ -14719,50 +14800,115 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-global": {
|
"jss-plugin-global": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz",
|
||||||
"integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==",
|
"integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-nested": {
|
"jss-plugin-nested": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz",
|
||||||
"integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==",
|
"integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"jss": "10.0.0",
|
"jss": "10.1.1",
|
||||||
"tiny-warning": "^1.0.2"
|
"tiny-warning": "^1.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-props-sort": {
|
"jss-plugin-props-sort": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz",
|
||||||
"integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==",
|
"integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-rule-value-function": {
|
"jss-plugin-rule-value-function": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz",
|
||||||
"integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==",
|
"integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss-plugin-vendor-prefixer": {
|
"jss-plugin-vendor-prefixer": {
|
||||||
"version": "10.0.0",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz",
|
||||||
"integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==",
|
"integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"css-vendor": "^2.0.6",
|
"css-vendor": "^2.0.7",
|
||||||
"jss": "10.0.0"
|
"jss": "10.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jss": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.3.1",
|
||||||
|
"csstype": "^2.6.5",
|
||||||
|
"is-in-browser": "^1.1.3",
|
||||||
|
"tiny-warning": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jsx-ast-utils": {
|
"jsx-ast-utils": {
|
||||||
|
|
@ -16348,11 +16494,6 @@
|
||||||
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
|
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"normalize-scroll-left": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-t5oCENZJl8TGusJKoCJm7+asaSsPuNmK6+iEjrZ5TyBj2f02brCRsd4c83hwtu+e5d4LCSBZ0uoDlMjBo+A8yA=="
|
|
||||||
},
|
|
||||||
"normalize-url": {
|
"normalize-url": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
|
||||||
|
|
@ -22170,6 +22311,12 @@
|
||||||
"psl": "^1.1.24",
|
"psl": "^1.1.24",
|
||||||
"punycode": "^1.4.1"
|
"punycode": "^1.4.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -23393,6 +23540,12 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"websocket-driver": ">=0.5.1"
|
"websocket-driver": ">=0.5.1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -24956,10 +25109,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "3.3.2",
|
"version": "7.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
|
||||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
|
"integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"v8-compile-cache": {
|
"v8-compile-cache": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
|
|
@ -25267,7 +25419,8 @@
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
|
@ -25295,6 +25448,7 @@
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
|
@ -25309,7 +25463,8 @@
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
|
@ -25320,7 +25475,8 @@
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
@ -25437,7 +25593,8 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
|
@ -25449,6 +25606,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -25463,6 +25621,7 @@
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
|
@ -25470,12 +25629,14 @@
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
|
@ -25494,6 +25655,7 @@
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
|
@ -25583,7 +25745,8 @@
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
|
@ -25595,6 +25758,7 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
|
@ -25680,7 +25844,8 @@
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
|
@ -25716,6 +25881,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
|
@ -25735,6 +25901,7 @@
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -25778,12 +25945,14 @@
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -25879,6 +26048,14 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-colors": "^3.0.0",
|
"ansi-colors": "^3.0.0",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webpack-manifest-plugin": {
|
"webpack-manifest-plugin": {
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
"license": "unlicense",
|
"license": "unlicense",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/react-hooks": "^3.1.3",
|
"@apollo/react-hooks": "^3.1.3",
|
||||||
"@material-ui/core": "4.5.0",
|
"@material-ui/core": "4.9.8",
|
||||||
"@material-ui/icons": "4.4.3",
|
"@material-ui/icons": "4.9.1",
|
||||||
|
"@material-ui/lab": "^4.0.0-alpha.47",
|
||||||
"@use-hooks/axios": "1.3.0",
|
"@use-hooks/axios": "1.3.0",
|
||||||
"apollo-boost": "^0.4.7",
|
"apollo-boost": "^0.4.7",
|
||||||
"axios": "0.19.0",
|
"axios": "0.19.0",
|
||||||
|
|
@ -13,7 +14,7 @@
|
||||||
"classnames": "2.2.6",
|
"classnames": "2.2.6",
|
||||||
"downshift": "3.3.4",
|
"downshift": "3.3.4",
|
||||||
"file-saver": "2.0.2",
|
"file-saver": "2.0.2",
|
||||||
"formik": "2.0.3",
|
"formik": "2.1.4",
|
||||||
"fuse.js": "^3.4.6",
|
"fuse.js": "^3.4.6",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
"jss-plugin-extend": "^10.0.0",
|
"jss-plugin-extend": "^10.0.0",
|
||||||
|
|
@ -27,6 +28,7 @@
|
||||||
"react-virtualized": "^9.21.2",
|
"react-virtualized": "^9.21.2",
|
||||||
"sanctuary": "^2.0.1",
|
"sanctuary": "^2.0.1",
|
||||||
"slugify": "^1.3.6",
|
"slugify": "^1.3.6",
|
||||||
|
"uuid": "^7.0.2",
|
||||||
"yup": "0.27.0"
|
"yup": "0.27.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { ApolloProvider } from '@apollo/react-hooks'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
import {
|
import {
|
||||||
StylesProvider,
|
StylesProvider,
|
||||||
|
|
@ -5,13 +6,12 @@ import {
|
||||||
MuiThemeProvider,
|
MuiThemeProvider,
|
||||||
makeStyles
|
makeStyles
|
||||||
} from '@material-ui/core/styles'
|
} from '@material-ui/core/styles'
|
||||||
|
import ApolloClient from 'apollo-boost'
|
||||||
import { setAutoFreeze } from 'immer'
|
import { setAutoFreeze } from 'immer'
|
||||||
import { create } from 'jss'
|
import { create } from 'jss'
|
||||||
import extendJss from 'jss-plugin-extend'
|
import extendJss from 'jss-plugin-extend'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom'
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
import ApolloClient from 'apollo-boost'
|
|
||||||
import { ApolloProvider } from '@apollo/react-hooks'
|
|
||||||
|
|
||||||
import Header from './components/Header'
|
import Header from './components/Header'
|
||||||
import { tree, Routes } from './routing/routes'
|
import { tree, Routes } from './routing/routes'
|
||||||
|
|
@ -19,8 +19,23 @@ import global from './styling/global'
|
||||||
import theme from './styling/theme'
|
import theme from './styling/theme'
|
||||||
import { backgroundColor, mainWidth } from './styling/variables'
|
import { backgroundColor, mainWidth } from './styling/variables'
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
watchQuery: {
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
errorPolicy: 'ignore'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
fetchPolicy: 'no-cache',
|
||||||
|
errorPolicy: 'all'
|
||||||
|
},
|
||||||
|
mutate: {
|
||||||
|
errorPolicy: 'all'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const client = new ApolloClient({
|
const client = new ApolloClient({
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
|
defaultOptions,
|
||||||
uri:
|
uri:
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development'
|
||||||
? 'https://localhost:8070/graphql/'
|
? 'https://localhost:8070/graphql/'
|
||||||
|
|
@ -29,7 +44,7 @@ const client = new ApolloClient({
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
||||||
whyDidYouRender(React, { include: [/Logs/] })
|
whyDidYouRender(React)
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable immer autofreeze for performance
|
// disable immer autofreeze for performance
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ export const ConfirmDialog = memo(
|
||||||
autoFocus
|
autoFocus
|
||||||
id="confirm-input"
|
id="confirm-input"
|
||||||
type="text"
|
type="text"
|
||||||
large
|
size="lg"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={value}
|
value={value}
|
||||||
touched={{}}
|
touched={{}}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import React, { useState, memo } from 'react'
|
import React, { useState, memo } from 'react'
|
||||||
|
|
||||||
import { H4 } from 'src/components/typography'
|
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import { RadioGroup } from 'src/components/inputs'
|
import { RadioGroup } from 'src/components/inputs'
|
||||||
import { Table, TableBody, TableRow, TableCell } from 'src/components/table'
|
import { Table, TableBody, TableRow, TableCell } from 'src/components/table'
|
||||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
import BooleanCell from 'src/components/tables/BooleanCell'
|
||||||
|
import { H4 } from 'src/components/typography'
|
||||||
import { ReactComponent as EditIconDisabled } from 'src/styling/icons/action/edit/disabled.svg'
|
import { ReactComponent as EditIconDisabled } from 'src/styling/icons/action/edit/disabled.svg'
|
||||||
import { ReactComponent as TrueIcon } from 'src/styling/icons/table/true.svg'
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||||
import { ReactComponent as FalseIcon } from 'src/styling/icons/table/false.svg'
|
|
||||||
|
|
||||||
import { booleanPropertiesTableStyles } from './BooleanPropertiesTable.styles'
|
import { booleanPropertiesTableStyles } from './BooleanPropertiesTable.styles'
|
||||||
|
|
||||||
|
|
@ -82,7 +81,7 @@ const BooleanPropertiesTable = memo(
|
||||||
<TableRow key={idx} size="sm" className={classes.tableRow}>
|
<TableRow key={idx} size="sm" className={classes.tableRow}>
|
||||||
<TableCell className={classes.tableCell}>
|
<TableCell className={classes.tableCell}>
|
||||||
{element.display}
|
{element.display}
|
||||||
{editing ? (
|
{editing && (
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
options={radioButtonOptions}
|
options={radioButtonOptions}
|
||||||
value={element.value}
|
value={element.value}
|
||||||
|
|
@ -94,11 +93,8 @@ const BooleanPropertiesTable = memo(
|
||||||
}
|
}
|
||||||
className={classes.radioButtons}
|
className={classes.radioButtons}
|
||||||
/>
|
/>
|
||||||
) : element.value ? (
|
|
||||||
<TrueIcon />
|
|
||||||
) : (
|
|
||||||
<FalseIcon />
|
|
||||||
)}
|
)}
|
||||||
|
{!editing && <BooleanCell value={element.value} />}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { makeStyles } from '@material-ui/core/styles'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import React, { memo } from 'react'
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { ReactComponent as AddIcon } from 'src/styling/icons/button/add/zodiac.svg'
|
import { ReactComponent as AddIcon } from 'src/styling/icons/button/add/zodiac.svg'
|
||||||
import { zircon, zircon2, comet, fontColor, white } from 'src/styling/variables'
|
import { zircon, zircon2, comet, fontColor, white } from 'src/styling/variables'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { p } = typographyStyles
|
const { p } = typographyStyles
|
||||||
|
|
||||||
|
|
|
||||||
27
new-lamassu-admin/src/components/buttons/IconButton.js
Normal file
27
new-lamassu-admin/src/components/buttons/IconButton.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { makeStyles, IconButton as IconB, SvgIcon } from '@material-ui/core'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
root: {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'inherit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const IconButton = ({ children, onClick, ...props }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<IconB
|
||||||
|
{...props}
|
||||||
|
classes={{ root: classes.root }}
|
||||||
|
disableRipple
|
||||||
|
onClick={onClick}>
|
||||||
|
<SvgIcon>{children}</SvgIcon>
|
||||||
|
</IconB>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IconButton
|
||||||
|
|
@ -6,23 +6,25 @@ import styles from './Link.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const Link = memo(({ submit, className, children, color, size, ...props }) => {
|
const Link = memo(
|
||||||
const classes = useStyles()
|
({ submit, className, children, color = 'primary', ...props }) => {
|
||||||
const classNames = {
|
const classes = useStyles()
|
||||||
[classes.link]: true,
|
const classNames = {
|
||||||
[classes.primary]: color === 'primary',
|
[classes.link]: true,
|
||||||
[classes.secondary]: color === 'secondary',
|
[classes.primary]: color === 'primary',
|
||||||
[classes.noColor]: color === 'noColor'
|
[classes.secondary]: color === 'secondary',
|
||||||
}
|
[classes.noColor]: color === 'noColor'
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type={submit ? 'submit' : 'button'}
|
type={submit ? 'submit' : 'button'}
|
||||||
className={classnames(classNames, className)}
|
className={classnames(classNames, className)}
|
||||||
{...props}>
|
{...props}>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export default Link
|
export default Link
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { fade } from '@material-ui/core/styles/colorManipulator'
|
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||||
|
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import {
|
import {
|
||||||
white,
|
white,
|
||||||
linkPrimaryColor,
|
linkPrimaryColor,
|
||||||
linkSecondaryColor
|
linkSecondaryColor
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { h4 } = typographyStyles
|
const { h4 } = typographyStyles
|
||||||
|
|
||||||
|
|
@ -24,7 +24,8 @@ export default {
|
||||||
border: 'none',
|
border: 'none',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
padding: '0'
|
padding: '0',
|
||||||
|
height: '100%'
|
||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
extend: color(linkPrimaryColor)
|
extend: color(linkPrimaryColor)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import AddButton from './AddButton'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
import FeatureButton from './FeatureButton'
|
import FeatureButton from './FeatureButton'
|
||||||
import IDButton from './IDButton'
|
import IDButton from './IDButton'
|
||||||
|
import IconButton from './IconButton'
|
||||||
import Link from './Link'
|
import Link from './Link'
|
||||||
import SimpleButton from './SimpleButton'
|
import SimpleButton from './SimpleButton'
|
||||||
|
|
||||||
|
|
@ -12,6 +13,7 @@ export {
|
||||||
SimpleButton,
|
SimpleButton,
|
||||||
ActionButton,
|
ActionButton,
|
||||||
FeatureButton,
|
FeatureButton,
|
||||||
|
IconButton,
|
||||||
IDButton,
|
IDButton,
|
||||||
AddButton
|
AddButton
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,18 @@ const DataTable = memo(({ elements, data }) => {
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<THead>
|
<THead>
|
||||||
{elements.map(({ size, className, textAlign, header }, idx) => (
|
{elements.map(
|
||||||
<Th
|
({ width, size, className, textAlign, header }, idx) => (
|
||||||
key={idx}
|
<Th
|
||||||
size={size}
|
key={idx}
|
||||||
className={className}
|
size={size}
|
||||||
textAlign={textAlign}>
|
width={width}
|
||||||
{header}
|
className={className}
|
||||||
</Th>
|
textAlign={textAlign}>
|
||||||
))}
|
{header}
|
||||||
|
</Th>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</THead>
|
</THead>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: '1 1 auto' }}>
|
<div style={{ flex: '1 1 auto' }}>
|
||||||
|
|
@ -52,8 +55,8 @@ const DataTable = memo(({ elements, data }) => {
|
||||||
{elements.map(
|
{elements.map(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
header,
|
|
||||||
size,
|
size,
|
||||||
|
width,
|
||||||
className,
|
className,
|
||||||
textAlign,
|
textAlign,
|
||||||
view = it => it?.toString()
|
view = it => it?.toString()
|
||||||
|
|
@ -63,6 +66,7 @@ const DataTable = memo(({ elements, data }) => {
|
||||||
<Td
|
<Td
|
||||||
key={idx}
|
key={idx}
|
||||||
size={size}
|
size={size}
|
||||||
|
width={width}
|
||||||
className={className}
|
className={className}
|
||||||
textAlign={textAlign}>
|
textAlign={textAlign}>
|
||||||
{view(data[index])}
|
{view(data[index])}
|
||||||
|
|
|
||||||
35
new-lamassu-admin/src/components/editableTable/Header.js
Normal file
35
new-lamassu-admin/src/components/editableTable/Header.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { Td, THead } from 'src/components/fake-table/Table'
|
||||||
|
import { startCase } from 'src/utils/string'
|
||||||
|
|
||||||
|
import { ACTION_COL_SIZE, DEFAULT_COL_SIZE } from './consts'
|
||||||
|
|
||||||
|
const Header = ({ elements, enableEdit, enableDelete }) => {
|
||||||
|
const actionColSize =
|
||||||
|
enableDelete && enableEdit ? ACTION_COL_SIZE / 2 : ACTION_COL_SIZE
|
||||||
|
|
||||||
|
return (
|
||||||
|
<THead>
|
||||||
|
{elements.map(
|
||||||
|
({ name, width = DEFAULT_COL_SIZE, header, textAlign }, idx) => (
|
||||||
|
<Td header key={idx} width={width} textAlign={textAlign}>
|
||||||
|
{header || startCase(name)}
|
||||||
|
</Td>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{enableEdit && (
|
||||||
|
<Td header width={actionColSize} textAlign="right">
|
||||||
|
Edit
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
{enableDelete && (
|
||||||
|
<Td header width={actionColSize} textAlign="right">
|
||||||
|
Delete
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
</THead>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
|
@ -1,250 +1,150 @@
|
||||||
import React, { memo } from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import { Form, Formik, Field, useFormikContext } from 'formik'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import { Field, useFormikContext } from 'formik'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link, IconButton } from 'src/components/buttons'
|
||||||
import { Td, Tr, CellDoubleLevel } from 'src/components/fake-table/Table'
|
import { Td, Tr } from 'src/components/fake-table/Table'
|
||||||
import { TextInputDisplay } from 'src/components/inputs/base/TextInput'
|
import { TL2 } from 'src/components/typography'
|
||||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
|
||||||
import { ReactComponent as DisabledDeleteIcon } from 'src/styling/icons/action/delete/disabled.svg'
|
import { ReactComponent as DisabledDeleteIcon } from 'src/styling/icons/action/delete/disabled.svg'
|
||||||
// import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||||
// import { ReactComponent as DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.svg'
|
import { ReactComponent as DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.svg'
|
||||||
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||||
|
|
||||||
const styles = {
|
import styles from './Row.styles'
|
||||||
button: {
|
import { ACTION_COL_SIZE } from './consts'
|
||||||
border: 'none',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
outline: 0,
|
|
||||||
cursor: 'pointer'
|
|
||||||
},
|
|
||||||
actionCol: {
|
|
||||||
display: 'flex',
|
|
||||||
marginLeft: 'auto'
|
|
||||||
},
|
|
||||||
actionColDisplayMode: {
|
|
||||||
justifyContent: 'center'
|
|
||||||
},
|
|
||||||
actionColEditMode: {
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
'& > :first-child': {
|
|
||||||
marginRight: 16
|
|
||||||
}
|
|
||||||
},
|
|
||||||
textInput: {
|
|
||||||
'& > .MuiInputBase-input': {
|
|
||||||
width: 282
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// doubleLevelRow: {
|
|
||||||
// '& > div': {
|
|
||||||
// marginRight: 72
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
const ERow = memo(
|
const ActionCol = ({
|
||||||
({ elements, editing, setEditing, disableAction, action }) => {
|
editing,
|
||||||
const classes = useStyles()
|
setEditing,
|
||||||
|
enableEdit,
|
||||||
|
disabled,
|
||||||
|
onDelete,
|
||||||
|
enableDelete
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const { values, submitForm, resetForm } = useFormikContext()
|
||||||
|
|
||||||
const Cell = ({
|
const actionColSize =
|
||||||
name,
|
enableDelete && enableEdit ? ACTION_COL_SIZE / 2 : ACTION_COL_SIZE
|
||||||
input,
|
|
||||||
type,
|
return (
|
||||||
display,
|
<>
|
||||||
className,
|
{editing && (
|
||||||
size,
|
<Td textAlign="center" width={ACTION_COL_SIZE}>
|
||||||
textAlign,
|
<Link
|
||||||
inputProps,
|
className={classes.cancelButton}
|
||||||
editing
|
color="secondary"
|
||||||
}) => {
|
onClick={resetForm}>
|
||||||
return (
|
Cancel
|
||||||
<Td size={size} textAlign={textAlign}>
|
</Link>
|
||||||
{editing && (
|
<Link color="primary" onClick={submitForm}>
|
||||||
<Field
|
Save
|
||||||
id={name}
|
</Link>
|
||||||
name={name}
|
|
||||||
component={input}
|
|
||||||
className={className}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!editing && type === 'text' && (
|
|
||||||
<TextInputDisplay display={display} {...inputProps} />
|
|
||||||
)}
|
|
||||||
</Td>
|
</Td>
|
||||||
)
|
)}
|
||||||
}
|
{!editing && enableEdit && (
|
||||||
|
<Td textAlign="right" width={actionColSize}>
|
||||||
const actionCol = R.last(elements)
|
<IconButton
|
||||||
const { values, errors } = useFormikContext()
|
disabled={disabled}
|
||||||
|
className={classes.editButton}
|
||||||
const actionColClasses = {
|
onClick={() => setEditing && setEditing(values.id)}>
|
||||||
[classes.actionCol]: true,
|
{disabled ? <DisabledEditIcon /> : <EditIcon />}
|
||||||
[classes.actionColDisplayMode]: !editing,
|
</IconButton>
|
||||||
[classes.actionColEditMode]: editing
|
|
||||||
}
|
|
||||||
|
|
||||||
const icon = (action, disabled) => {
|
|
||||||
if (action === 'delete' && !disabled) return <DeleteIcon />
|
|
||||||
if (action === 'delete' && disabled) return <DisabledDeleteIcon />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tr
|
|
||||||
error={errors && errors.length}
|
|
||||||
errorMessage={errors && errors.toString()}>
|
|
||||||
{R.init(elements).map((element, idx) => {
|
|
||||||
const colClasses = {
|
|
||||||
[classes.textInput]: true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(element)) {
|
|
||||||
return (
|
|
||||||
<CellDoubleLevel key={idx} className={classes.doubleLevelRow}>
|
|
||||||
{R.map(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
input,
|
|
||||||
size,
|
|
||||||
textAlign,
|
|
||||||
type,
|
|
||||||
view = it => it?.toString(),
|
|
||||||
inputProps
|
|
||||||
},
|
|
||||||
idx
|
|
||||||
) => (
|
|
||||||
<Cell
|
|
||||||
key={name}
|
|
||||||
name={name}
|
|
||||||
input={input}
|
|
||||||
type={type}
|
|
||||||
display={view(values[name])}
|
|
||||||
className={classnames(colClasses)}
|
|
||||||
size={size}
|
|
||||||
textAlign={textAlign}
|
|
||||||
inputProps={inputProps}
|
|
||||||
editing={editing}
|
|
||||||
/>
|
|
||||||
// <Td size={sizes.cashOut1} textAlign="right">
|
|
||||||
// <Field
|
|
||||||
// editing={editing}
|
|
||||||
// field={fields[CASSETTE_1_KEY]}
|
|
||||||
// displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
// decoration="%"
|
|
||||||
// className={classes.eRowField}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// </Td>
|
|
||||||
)
|
|
||||||
)(R.tail(element))}
|
|
||||||
</CellDoubleLevel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
input,
|
|
||||||
size,
|
|
||||||
textAlign,
|
|
||||||
type,
|
|
||||||
view = it => it?.toString(),
|
|
||||||
inputProps
|
|
||||||
} = element
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Cell
|
|
||||||
key={idx}
|
|
||||||
name={name}
|
|
||||||
input={input}
|
|
||||||
type={type}
|
|
||||||
display={view(values[name])}
|
|
||||||
className={classnames(colClasses)}
|
|
||||||
size={size}
|
|
||||||
textAlign={textAlign}
|
|
||||||
inputProps={inputProps}
|
|
||||||
editing={editing}
|
|
||||||
/>
|
|
||||||
// <Td key={idx} size={size} textAlign={textAlign}>
|
|
||||||
// {editing && (
|
|
||||||
// <Field
|
|
||||||
// id={name}
|
|
||||||
// name={name}
|
|
||||||
// component={input}
|
|
||||||
// className={classnames(colClasses)}
|
|
||||||
// {...inputProps}
|
|
||||||
// />
|
|
||||||
// )}
|
|
||||||
// {!editing && type === 'text' && (
|
|
||||||
// <TextInputDisplay
|
|
||||||
// display={view(values[name])}
|
|
||||||
// {...inputProps}
|
|
||||||
// />
|
|
||||||
// )}
|
|
||||||
// </Td>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Td size={actionCol.size} className={classnames(actionColClasses)}>
|
|
||||||
{!editing && !disableAction && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={classes.button}
|
|
||||||
onClick={() => action(values)}>
|
|
||||||
{icon(actionCol.name, disableAction)}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{!editing && disableAction && (
|
|
||||||
<div>{icon(actionCol.name, disableAction)}</div>
|
|
||||||
)}
|
|
||||||
{editing && (
|
|
||||||
<>
|
|
||||||
<Link color="secondary" type="reset">
|
|
||||||
Cancel
|
|
||||||
</Link>
|
|
||||||
<Link color="primary" type="submit">
|
|
||||||
Save
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
)}
|
||||||
)
|
{!editing && enableDelete && (
|
||||||
}
|
<Td textAlign="right" width={actionColSize}>
|
||||||
)
|
<IconButton disabled={disabled} onClick={() => onDelete(values.id)}>
|
||||||
|
{disabled ? <DisabledDeleteIcon /> : <DeleteIcon />}
|
||||||
|
</IconButton>
|
||||||
|
</Td>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const ERowWithFormik = memo(
|
const ECol = ({ editing, config }) => {
|
||||||
({
|
const {
|
||||||
initialValues,
|
name,
|
||||||
validationSchema,
|
input,
|
||||||
save,
|
size,
|
||||||
reset,
|
bold,
|
||||||
action,
|
width,
|
||||||
elements,
|
textAlign,
|
||||||
editing,
|
suffix,
|
||||||
disableAction
|
view = it => it?.toString(),
|
||||||
}) => {
|
inputProps = {}
|
||||||
return (
|
} = config
|
||||||
<Formik
|
|
||||||
enableReinitialize
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
onSubmit={save}
|
|
||||||
onReset={reset}>
|
|
||||||
<Form>
|
|
||||||
<ERow
|
|
||||||
elements={elements}
|
|
||||||
editing={editing}
|
|
||||||
disableAction={disableAction}
|
|
||||||
action={action}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export default ERowWithFormik
|
const { values } = useFormikContext()
|
||||||
|
const classes = useStyles({ textAlign, size })
|
||||||
|
|
||||||
|
const viewClasses = {
|
||||||
|
[classes.bold]: bold,
|
||||||
|
[classes.size]: true
|
||||||
|
}
|
||||||
|
|
||||||
|
const iProps = {
|
||||||
|
fullWidth: true,
|
||||||
|
size,
|
||||||
|
bold,
|
||||||
|
textAlign,
|
||||||
|
...inputProps
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autocomplete
|
||||||
|
if (iProps.options && !iProps.getLabel) {
|
||||||
|
iProps.getLabel = view
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Td
|
||||||
|
className={{ [classes.withSuffix]: suffix }}
|
||||||
|
width={width}
|
||||||
|
size={size}
|
||||||
|
textAlign={textAlign}>
|
||||||
|
{editing && <Field name={name} component={input} {...iProps} />}
|
||||||
|
{!editing && values && (
|
||||||
|
<div className={classnames(viewClasses)}>{view(values[name])}</div>
|
||||||
|
)}
|
||||||
|
{suffix && <TL2 className={classes.suffix}>{suffix}</TL2>}
|
||||||
|
</Td>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ERow = ({
|
||||||
|
elements,
|
||||||
|
enableEdit,
|
||||||
|
enableDelete,
|
||||||
|
onDelete,
|
||||||
|
editing,
|
||||||
|
setEditing,
|
||||||
|
disabled
|
||||||
|
}) => {
|
||||||
|
const { errors } = useFormikContext()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tr
|
||||||
|
error={errors && errors.length}
|
||||||
|
errorMessage={errors && errors.toString()}>
|
||||||
|
{elements.map((it, idx) => (
|
||||||
|
<ECol key={idx} config={it} editing={editing} />
|
||||||
|
))}
|
||||||
|
{(enableEdit || enableDelete) && (
|
||||||
|
<ActionCol
|
||||||
|
disabled={disabled}
|
||||||
|
editing={editing}
|
||||||
|
setEditing={setEditing}
|
||||||
|
onDelete={onDelete}
|
||||||
|
enableEdit={enableEdit}
|
||||||
|
enableDelete={enableDelete}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ERow
|
||||||
|
|
|
||||||
17
new-lamassu-admin/src/components/editableTable/Row.styles.js
Normal file
17
new-lamassu-admin/src/components/editableTable/Row.styles.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { bySize, bold } from 'src/styling/helpers'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
cancelButton: {
|
||||||
|
marginRight: 20
|
||||||
|
},
|
||||||
|
withSuffix: ({ textAlign }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
justifyContent: textAlign === 'right' && 'end'
|
||||||
|
}),
|
||||||
|
suffix: {
|
||||||
|
marginLeft: 7
|
||||||
|
},
|
||||||
|
size: ({ size }) => bySize(size),
|
||||||
|
bold
|
||||||
|
}
|
||||||
|
|
@ -1,124 +1,152 @@
|
||||||
import React, { memo } from 'react'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import { Form, Formik } from 'formik'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { v4 } from 'uuid'
|
||||||
|
|
||||||
import {
|
import Link from 'src/components/buttons/Link.js'
|
||||||
Th,
|
import { AddButton } from 'src/components/buttons/index.js'
|
||||||
ThDoubleLevel,
|
import { TBody, Table } from 'src/components/fake-table/Table'
|
||||||
THead,
|
import { Info2 } from 'src/components/typography'
|
||||||
TBody,
|
|
||||||
Table,
|
|
||||||
TDoubleLevelHead
|
|
||||||
} from 'src/components/fake-table/Table'
|
|
||||||
import { startCase } from 'src/utils/string'
|
|
||||||
|
|
||||||
|
import Header from './Header'
|
||||||
import ERow from './Row'
|
import ERow from './Row'
|
||||||
|
import styles from './Table.styles'
|
||||||
|
import { DEFAULT_COL_SIZE, ACTION_COL_SIZE } from './consts'
|
||||||
|
|
||||||
const ETHead = memo(({ elements, className }) => {
|
const useStyles = makeStyles(styles)
|
||||||
const action = R.last(elements)
|
|
||||||
|
|
||||||
return (
|
const getWidth = R.compose(
|
||||||
<THead className={className?.root}>
|
R.reduce(R.add)(0),
|
||||||
{R.init(elements).map(({ name, size, display, textAlign }, idx) => (
|
R.map(it => it.width ?? DEFAULT_COL_SIZE)
|
||||||
<Th
|
|
||||||
id={name}
|
|
||||||
key={idx}
|
|
||||||
size={size}
|
|
||||||
textAlign={textAlign}
|
|
||||||
className={className?.cell}>
|
|
||||||
{display}
|
|
||||||
</Th>
|
|
||||||
))}
|
|
||||||
<Th size={action.size} action>
|
|
||||||
{startCase(action.name)}
|
|
||||||
</Th>
|
|
||||||
</THead>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const ETDoubleHead = memo(({ elements, className }) => {
|
|
||||||
const action = R.last(elements)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TDoubleLevelHead className={className?.root}>
|
|
||||||
{R.init(elements).map((element, idx) => {
|
|
||||||
if (Array.isArray(element)) {
|
|
||||||
return (
|
|
||||||
<ThDoubleLevel
|
|
||||||
key={idx}
|
|
||||||
title={element[0].display}
|
|
||||||
className={className?.cell}>
|
|
||||||
{R.map(({ name, size, display, textAlign }) => (
|
|
||||||
<Th key={name} id={name} size={size} textAlign={textAlign}>
|
|
||||||
{display}
|
|
||||||
</Th>
|
|
||||||
))(R.tail(element))}
|
|
||||||
</ThDoubleLevel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name, size, display, textAlign } = element
|
|
||||||
return (
|
|
||||||
<Th id={idx} key={name} size={size} textAlign={textAlign}>
|
|
||||||
{display}
|
|
||||||
</Th>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Th size={action.size} action>
|
|
||||||
{startCase(action.name)}
|
|
||||||
</Th>
|
|
||||||
</TDoubleLevelHead>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const ETable = memo(
|
|
||||||
({
|
|
||||||
elements = [],
|
|
||||||
data = [],
|
|
||||||
save,
|
|
||||||
reset,
|
|
||||||
action,
|
|
||||||
initialValues,
|
|
||||||
validationSchema,
|
|
||||||
editing,
|
|
||||||
addingRow,
|
|
||||||
disableAction,
|
|
||||||
className,
|
|
||||||
double
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<Table className={className}>
|
|
||||||
{!double && <ETHead elements={elements} />}
|
|
||||||
{double && (
|
|
||||||
<ETDoubleHead elements={elements} className={className?.head} />
|
|
||||||
)}
|
|
||||||
<TBody>
|
|
||||||
{addingRow && (
|
|
||||||
<ERow
|
|
||||||
elements={elements}
|
|
||||||
initialValues={initialValues}
|
|
||||||
save={save}
|
|
||||||
reset={reset}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
editing
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{data.map((it, idx) => (
|
|
||||||
<ERow
|
|
||||||
key={idx}
|
|
||||||
initialValues={it}
|
|
||||||
elements={elements}
|
|
||||||
save={save}
|
|
||||||
reset={it => reset(it)}
|
|
||||||
action={action}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
disableAction={disableAction}
|
|
||||||
editing={editing[idx]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</TBody>
|
|
||||||
</Table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ETable = ({
|
||||||
|
name,
|
||||||
|
title,
|
||||||
|
elements = [],
|
||||||
|
data = [],
|
||||||
|
save,
|
||||||
|
validationSchema,
|
||||||
|
enableCreate,
|
||||||
|
forceDisable,
|
||||||
|
disableAdd,
|
||||||
|
enableDelete,
|
||||||
|
initialValues,
|
||||||
|
enableEdit,
|
||||||
|
setEditing,
|
||||||
|
createText = 'Add override'
|
||||||
|
}) => {
|
||||||
|
const [editingId, setEditingId] = useState(null)
|
||||||
|
const [adding, setAdding] = useState(false)
|
||||||
|
|
||||||
|
const innerSave = async it => {
|
||||||
|
const index = R.findIndex(R.propEq('id', it.id))(data)
|
||||||
|
const list = index !== -1 ? R.update(index, it, data) : R.prepend(it, data)
|
||||||
|
|
||||||
|
// no response means the save failed
|
||||||
|
const response = await save({ [name]: list })
|
||||||
|
if (!response) return
|
||||||
|
setAdding(false)
|
||||||
|
setEditingId(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDelete = id => {
|
||||||
|
const list = R.reject(it => it.id === id, data)
|
||||||
|
return save({ [name]: list })
|
||||||
|
}
|
||||||
|
|
||||||
|
const onReset = () => {
|
||||||
|
setAdding(false)
|
||||||
|
setEditingId(null)
|
||||||
|
setEditing && setEditing(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEdit = it => {
|
||||||
|
setEditingId(it)
|
||||||
|
setEditing && setEditing(it, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addField = () => setAdding(true)
|
||||||
|
|
||||||
|
const actionSize = enableEdit || enableDelete ? ACTION_COL_SIZE : 0
|
||||||
|
const width = getWidth(elements) + actionSize
|
||||||
|
const classes = useStyles({ width })
|
||||||
|
|
||||||
|
const showButtonOnEmpty = !data.length && enableCreate && !adding
|
||||||
|
const canAdd = !forceDisable && !editingId && !disableAdd && !adding
|
||||||
|
const showTable = adding || data.length !== 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
{showButtonOnEmpty && (
|
||||||
|
<AddButton disabled={!canAdd} onClick={addField}>
|
||||||
|
{createText}
|
||||||
|
</AddButton>
|
||||||
|
)}
|
||||||
|
{showTable && (
|
||||||
|
<>
|
||||||
|
<div className={classes.outerHeader}>
|
||||||
|
{title && <Info2 className={classes.title}>{title}</Info2>}
|
||||||
|
{enableCreate && canAdd && (
|
||||||
|
<Link className={classes.addLink} onClick={addField}>
|
||||||
|
{createText}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Table>
|
||||||
|
<Header
|
||||||
|
elements={elements}
|
||||||
|
enableEdit={enableEdit}
|
||||||
|
enableDelete={enableDelete}
|
||||||
|
/>
|
||||||
|
<TBody>
|
||||||
|
{adding && (
|
||||||
|
<Formik
|
||||||
|
initialValues={{ id: v4(), ...initialValues }}
|
||||||
|
onReset={onReset}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={innerSave}>
|
||||||
|
<Form>
|
||||||
|
<ERow
|
||||||
|
editing={true}
|
||||||
|
disabled={forceDisable}
|
||||||
|
enableEdit={enableEdit}
|
||||||
|
enableDelete={enableDelete}
|
||||||
|
elements={elements}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
)}
|
||||||
|
{data.map((it, idx) => (
|
||||||
|
<Formik
|
||||||
|
key={it.id ?? idx}
|
||||||
|
enableReinitialize
|
||||||
|
initialValues={it}
|
||||||
|
onReset={onReset}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={innerSave}>
|
||||||
|
<Form>
|
||||||
|
<ERow
|
||||||
|
editing={editingId === it.id}
|
||||||
|
disabled={
|
||||||
|
forceDisable || (editingId && editingId !== it.id)
|
||||||
|
}
|
||||||
|
setEditing={onEdit}
|
||||||
|
onDelete={onDelete}
|
||||||
|
enableEdit={enableEdit}
|
||||||
|
enableDelete={enableDelete}
|
||||||
|
elements={elements}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
))}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default ETable
|
export default ETable
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { offColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrapper: ({ width }) => ({
|
||||||
|
width: width
|
||||||
|
}),
|
||||||
|
addLink: {
|
||||||
|
marginLeft: 'auto'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
margin: 0,
|
||||||
|
color: offColor
|
||||||
|
},
|
||||||
|
outerHeader: {
|
||||||
|
minHeight: 16,
|
||||||
|
marginBottom: 24,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center'
|
||||||
|
}
|
||||||
|
}
|
||||||
4
new-lamassu-admin/src/components/editableTable/consts.js
Normal file
4
new-lamassu-admin/src/components/editableTable/consts.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
const ACTION_COL_SIZE = 175
|
||||||
|
const DEFAULT_COL_SIZE = 100
|
||||||
|
|
||||||
|
export { ACTION_COL_SIZE, DEFAULT_COL_SIZE }
|
||||||
|
|
@ -8,10 +8,10 @@ import {
|
||||||
CellMeasurerCache
|
CellMeasurerCache
|
||||||
} from 'react-virtualized'
|
} from 'react-virtualized'
|
||||||
|
|
||||||
|
import { THead, Tr, Td, Th } from 'src/components/fake-table/Table'
|
||||||
import { ReactComponent as ExpandClosedIcon } from 'src/styling/icons/action/expand/closed.svg'
|
import { ReactComponent as ExpandClosedIcon } from 'src/styling/icons/action/expand/closed.svg'
|
||||||
import { ReactComponent as ExpandOpenIcon } from 'src/styling/icons/action/expand/open.svg'
|
import { ReactComponent as ExpandOpenIcon } from 'src/styling/icons/action/expand/open.svg'
|
||||||
import { mainWidth } from 'src/styling/variables'
|
import { mainWidth } from 'src/styling/variables'
|
||||||
import { THead, Tr, Td, Th } from 'src/components/fake-table/Table'
|
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
expandButton: {
|
expandButton: {
|
||||||
|
|
@ -48,25 +48,19 @@ const ExpRow = ({
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.map(
|
.map(
|
||||||
(
|
(
|
||||||
{
|
{ width, className, textAlign, view = it => it?.toString() },
|
||||||
header,
|
|
||||||
size,
|
|
||||||
className,
|
|
||||||
textAlign,
|
|
||||||
view = it => it?.toString()
|
|
||||||
},
|
|
||||||
idx
|
idx
|
||||||
) => (
|
) => (
|
||||||
<Td
|
<Td
|
||||||
key={idx}
|
key={idx}
|
||||||
size={size}
|
width={width}
|
||||||
className={className}
|
className={className}
|
||||||
textAlign={textAlign}>
|
textAlign={textAlign}>
|
||||||
{view(data)}
|
{view(data)}
|
||||||
</Td>
|
</Td>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
<Td size={elements[elements.length - 1].size}>
|
<Td width={elements[elements.length - 1].width}>
|
||||||
<button
|
<button
|
||||||
onClick={() => expandRow(id)}
|
onClick={() => expandRow(id)}
|
||||||
className={classes.expandButton}>
|
className={classes.expandButton}>
|
||||||
|
|
@ -77,7 +71,7 @@ const ExpRow = ({
|
||||||
</Tr>
|
</Tr>
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<Tr className={classes.detailsRow}>
|
<Tr className={classes.detailsRow}>
|
||||||
<Td size={mainWidth}>
|
<Td width={mainWidth}>
|
||||||
<Details it={data} />
|
<Details it={data} />
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
|
@ -86,8 +80,8 @@ const ExpRow = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rows = [{ columns = [{ name, value, className, textAlign, size }], details, className, error, errorMessage }]
|
/* rows = [{ columns = [{ name, value, className, textAlign, width }], details, className, error, errorMessage }]
|
||||||
* Don't forget to include the size of the last (expand button) column!
|
* Don't forget to include the width of the last (expand button) column!
|
||||||
*/
|
*/
|
||||||
const ExpTable = ({
|
const ExpTable = ({
|
||||||
elements = [],
|
elements = [],
|
||||||
|
|
@ -133,10 +127,10 @@ const ExpTable = ({
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<THead>
|
<THead>
|
||||||
{elements.map(({ size, className, textAlign, header }, idx) => (
|
{elements.map(({ width, className, textAlign, header }, idx) => (
|
||||||
<Th
|
<Th
|
||||||
key={idx}
|
key={idx}
|
||||||
size={size}
|
width={width}
|
||||||
className={className}
|
className={className}
|
||||||
textAlign={textAlign}>
|
textAlign={textAlign}>
|
||||||
{header}
|
{header}
|
||||||
|
|
|
||||||
|
|
@ -5,103 +5,10 @@ import classnames from 'classnames'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Link } from 'src/components/buttons'
|
import { Link } from 'src/components/buttons'
|
||||||
import {
|
|
||||||
tableHeaderColor,
|
|
||||||
tableHeaderHeight,
|
|
||||||
tableErrorColor,
|
|
||||||
spacer,
|
|
||||||
white,
|
|
||||||
tableDoubleHeaderHeight,
|
|
||||||
offColor
|
|
||||||
} from 'src/styling/variables'
|
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { tl2, p, label1 } = typographyStyles
|
import styles from './Table.styles'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles(styles)
|
||||||
body: {
|
|
||||||
borderSpacing: [[0, 4]]
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
extend: tl2,
|
|
||||||
backgroundColor: tableHeaderColor,
|
|
||||||
height: tableHeaderHeight,
|
|
||||||
textAlign: 'left',
|
|
||||||
color: white,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
doubleHeader: {
|
|
||||||
extend: tl2,
|
|
||||||
backgroundColor: tableHeaderColor,
|
|
||||||
height: tableDoubleHeaderHeight,
|
|
||||||
color: white,
|
|
||||||
display: 'table-row'
|
|
||||||
},
|
|
||||||
thDoubleLevel: {
|
|
||||||
padding: [[0, spacer * 2]],
|
|
||||||
display: 'table-cell',
|
|
||||||
'& > :first-child': {
|
|
||||||
extend: label1,
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: offColor,
|
|
||||||
color: white,
|
|
||||||
borderRadius: [[0, 0, 8, 8]],
|
|
||||||
height: 28
|
|
||||||
},
|
|
||||||
'& > :last-child': {
|
|
||||||
display: 'table-cell',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
height: tableDoubleHeaderHeight - 28,
|
|
||||||
'& > div': {
|
|
||||||
display: 'inline-block'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cellDoubleLevel: {
|
|
||||||
display: 'flex',
|
|
||||||
padding: [[0, spacer * 2]]
|
|
||||||
},
|
|
||||||
td: {
|
|
||||||
padding: [[0, spacer * 3]]
|
|
||||||
},
|
|
||||||
tdHeader: {
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
display: 'table-cell',
|
|
||||||
padding: [[0, spacer * 3]]
|
|
||||||
},
|
|
||||||
trError: {
|
|
||||||
backgroundColor: tableErrorColor
|
|
||||||
},
|
|
||||||
mainContent: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
minHeight: 54
|
|
||||||
},
|
|
||||||
// mui-overrides
|
|
||||||
cardContentRoot: {
|
|
||||||
// display: 'flex',
|
|
||||||
margin: 0,
|
|
||||||
padding: 0,
|
|
||||||
'&:last-child': {
|
|
||||||
padding: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
card: {
|
|
||||||
extend: p,
|
|
||||||
'&:before': {
|
|
||||||
height: 0
|
|
||||||
},
|
|
||||||
margin: [[4, 0]],
|
|
||||||
width: '100%',
|
|
||||||
boxShadow: [[0, 0, 4, 0, 'rgba(0, 0, 0, 0.08)']]
|
|
||||||
},
|
|
||||||
actionCol: {
|
|
||||||
marginLeft: 'auto'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const Table = ({ children, className, ...props }) => (
|
const Table = ({ children, className, ...props }) => (
|
||||||
<div className={classnames(className)} {...props}>
|
<div className={classnames(className)} {...props}>
|
||||||
|
|
@ -129,21 +36,25 @@ const TBody = ({ children, className }) => {
|
||||||
return <div className={classnames(className, classes.body)}>{children}</div>
|
return <div className={classnames(className, classes.body)}>{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const Td = ({ children, header, className, size = 100, textAlign, action }) => {
|
const Td = ({
|
||||||
const classes = useStyles()
|
children,
|
||||||
|
header,
|
||||||
|
className,
|
||||||
|
width = 100,
|
||||||
|
size,
|
||||||
|
textAlign,
|
||||||
|
action
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({ textAlign, width })
|
||||||
const classNames = {
|
const classNames = {
|
||||||
[classes.td]: true,
|
[classes.td]: true,
|
||||||
[classes.tdHeader]: header,
|
[classes.tdHeader]: header,
|
||||||
[classes.actionCol]: action
|
[classes.actionCol]: action,
|
||||||
|
[classes.large]: size === 'lg' && !header,
|
||||||
|
[classes.md]: size === 'md' && !header
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <div className={classnames(className, classNames)}>{children}</div>
|
||||||
<div
|
|
||||||
className={classnames(className, classNames)}
|
|
||||||
style={{ width: size, textAlign }}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Th = ({ children, ...props }) => {
|
const Th = ({ children, ...props }) => {
|
||||||
|
|
|
||||||
105
new-lamassu-admin/src/components/fake-table/Table.styles.js
Normal file
105
new-lamassu-admin/src/components/fake-table/Table.styles.js
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
|
import {
|
||||||
|
tableHeaderColor,
|
||||||
|
tableHeaderHeight,
|
||||||
|
tableErrorColor,
|
||||||
|
spacer,
|
||||||
|
white,
|
||||||
|
tableDoubleHeaderHeight,
|
||||||
|
offColor
|
||||||
|
} from 'src/styling/variables'
|
||||||
|
|
||||||
|
const { tl1, info2, tl2, p, label1 } = typographyStyles
|
||||||
|
|
||||||
|
export default {
|
||||||
|
body: {
|
||||||
|
borderSpacing: [[0, 4]]
|
||||||
|
},
|
||||||
|
large: {
|
||||||
|
extend: tl1
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
extend: info2
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
extend: tl2,
|
||||||
|
backgroundColor: tableHeaderColor,
|
||||||
|
height: tableHeaderHeight,
|
||||||
|
textAlign: 'left',
|
||||||
|
color: white,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
doubleHeader: {
|
||||||
|
extend: tl2,
|
||||||
|
backgroundColor: tableHeaderColor,
|
||||||
|
height: tableDoubleHeaderHeight,
|
||||||
|
color: white,
|
||||||
|
display: 'table-row'
|
||||||
|
},
|
||||||
|
thDoubleLevel: {
|
||||||
|
padding: [[0, spacer * 2]],
|
||||||
|
display: 'table-cell',
|
||||||
|
'& > :first-child': {
|
||||||
|
extend: label1,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: offColor,
|
||||||
|
color: white,
|
||||||
|
borderRadius: [[0, 0, 8, 8]],
|
||||||
|
height: 28
|
||||||
|
},
|
||||||
|
'& > :last-child': {
|
||||||
|
display: 'table-cell',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
height: tableDoubleHeaderHeight - 28,
|
||||||
|
'& > div': {
|
||||||
|
display: 'inline-block'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cellDoubleLevel: {
|
||||||
|
display: 'flex',
|
||||||
|
padding: [[0, spacer * 2]]
|
||||||
|
},
|
||||||
|
td: ({ textAlign, width }) => ({
|
||||||
|
width,
|
||||||
|
padding: [[1, spacer * 3, 0, spacer * 3]],
|
||||||
|
textAlign
|
||||||
|
}),
|
||||||
|
tdHeader: {
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'table-cell',
|
||||||
|
padding: [[0, spacer * 3]]
|
||||||
|
},
|
||||||
|
trError: {
|
||||||
|
backgroundColor: tableErrorColor
|
||||||
|
},
|
||||||
|
mainContent: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
minHeight: 54
|
||||||
|
},
|
||||||
|
// mui-overrides
|
||||||
|
cardContentRoot: {
|
||||||
|
// display: 'flex',
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
'&:last-child': {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
extend: p,
|
||||||
|
'&:before': {
|
||||||
|
height: 0
|
||||||
|
},
|
||||||
|
margin: [[4, 0]],
|
||||||
|
width: '100%',
|
||||||
|
boxShadow: [[0, 0, 4, 0, 'rgba(0, 0, 0, 0.08)']]
|
||||||
|
},
|
||||||
|
actionCol: {
|
||||||
|
marginLeft: 'auto'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
import Paper from '@material-ui/core/Paper'
|
|
||||||
import Popper from '@material-ui/core/Popper'
|
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
import Downshift from 'downshift'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import React, { memo, useState } from 'react'
|
|
||||||
|
|
||||||
import {
|
|
||||||
renderInput,
|
|
||||||
renderSuggestion,
|
|
||||||
filterSuggestions,
|
|
||||||
styles
|
|
||||||
} from './commons'
|
|
||||||
|
|
||||||
const Autocomplete = memo(
|
|
||||||
({
|
|
||||||
suggestions,
|
|
||||||
classes,
|
|
||||||
placeholder,
|
|
||||||
label,
|
|
||||||
itemToString,
|
|
||||||
code = 'code',
|
|
||||||
display = 'display',
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const { name, value, onBlur } = props.field
|
|
||||||
const { touched, errors, setFieldValue } = props.form
|
|
||||||
|
|
||||||
const [popperNode, setPopperNode] = useState(null)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Downshift
|
|
||||||
id={name}
|
|
||||||
itemToString={it => {
|
|
||||||
if (itemToString) return itemToString(it)
|
|
||||||
if (it) return it[display]
|
|
||||||
return undefined
|
|
||||||
}}
|
|
||||||
onChange={it => setFieldValue(name, it)}
|
|
||||||
defaultHighlightedIndex={0}
|
|
||||||
selectedItem={value}>
|
|
||||||
{({
|
|
||||||
getInputProps,
|
|
||||||
getItemProps,
|
|
||||||
getMenuProps,
|
|
||||||
isOpen,
|
|
||||||
inputValue: inputValue2,
|
|
||||||
selectedItem: selectedItem2,
|
|
||||||
highlightedIndex,
|
|
||||||
inputValue,
|
|
||||||
toggleMenu,
|
|
||||||
clearSelection
|
|
||||||
}) => (
|
|
||||||
<div className={classes.container}>
|
|
||||||
{renderInput({
|
|
||||||
name,
|
|
||||||
fullWidth: true,
|
|
||||||
error:
|
|
||||||
(touched[`${name}-input`] || touched[name]) && errors[name],
|
|
||||||
success:
|
|
||||||
(touched[`${name}-input`] || touched[name] || value) &&
|
|
||||||
!errors[name],
|
|
||||||
InputProps: getInputProps({
|
|
||||||
value: inputValue2 || '',
|
|
||||||
placeholder,
|
|
||||||
onBlur,
|
|
||||||
onClick: event => {
|
|
||||||
setPopperNode(event.currentTarget.parentElement)
|
|
||||||
toggleMenu()
|
|
||||||
},
|
|
||||||
onChange: it => {
|
|
||||||
if (it.target.value === '') {
|
|
||||||
clearSelection()
|
|
||||||
}
|
|
||||||
inputValue = it.target.value
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
label
|
|
||||||
})}
|
|
||||||
<Popper
|
|
||||||
open={isOpen}
|
|
||||||
anchorEl={popperNode}
|
|
||||||
modifiers={{ flip: { enabled: true } }}>
|
|
||||||
<div
|
|
||||||
{...(isOpen
|
|
||||||
? getMenuProps({}, { suppressRefError: true })
|
|
||||||
: {})}>
|
|
||||||
<Paper
|
|
||||||
square
|
|
||||||
style={{
|
|
||||||
minWidth: popperNode ? popperNode.clientWidth + 2 : null
|
|
||||||
}}>
|
|
||||||
{filterSuggestions(
|
|
||||||
suggestions,
|
|
||||||
inputValue2,
|
|
||||||
value ? R.of(value) : [],
|
|
||||||
code,
|
|
||||||
display
|
|
||||||
).map((suggestion, index) =>
|
|
||||||
renderSuggestion({
|
|
||||||
suggestion,
|
|
||||||
index,
|
|
||||||
itemProps: getItemProps({ item: suggestion }),
|
|
||||||
highlightedIndex,
|
|
||||||
selectedItem: selectedItem2,
|
|
||||||
code,
|
|
||||||
display
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
</Popper>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Downshift>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export default withStyles(styles)(Autocomplete)
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import Paper from '@material-ui/core/Paper'
|
|
||||||
import Popper from '@material-ui/core/Popper'
|
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
import Downshift from 'downshift'
|
|
||||||
import React, { useState, memo } from 'react'
|
|
||||||
|
|
||||||
import Chip from 'src/components/Chip'
|
|
||||||
|
|
||||||
import {
|
|
||||||
renderInput,
|
|
||||||
renderSuggestion,
|
|
||||||
filterSuggestions,
|
|
||||||
styles
|
|
||||||
} from './commons'
|
|
||||||
|
|
||||||
const AutocompleteMultiple = memo(
|
|
||||||
({ suggestions, classes, placeholder, label, ...props }) => {
|
|
||||||
const { name, value, onBlur } = props.field
|
|
||||||
const { touched, errors, setFieldValue } = props.form
|
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState('')
|
|
||||||
const [popperNode, setPopperNode] = useState(null)
|
|
||||||
|
|
||||||
const onDelete = item => {
|
|
||||||
const selectedItem = (value || []).slice()
|
|
||||||
const index = selectedItem.indexOf(item)
|
|
||||||
if (index === -1) return
|
|
||||||
|
|
||||||
selectedItem.splice(selectedItem.indexOf(item), 1)
|
|
||||||
setFieldValue(name, selectedItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleKeyDown = event => {
|
|
||||||
if (value.length && !inputValue.length && event.key === 'Backspace') {
|
|
||||||
onDelete(value[value.length - 1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Downshift
|
|
||||||
id={name}
|
|
||||||
inputValue={inputValue}
|
|
||||||
itemToString={it => it && it.display}
|
|
||||||
defaultHighlightedIndex={0}
|
|
||||||
onChange={it => {
|
|
||||||
setInputValue('')
|
|
||||||
let selectedItem = (value || []).slice()
|
|
||||||
|
|
||||||
if (selectedItem.indexOf(it) === -1) {
|
|
||||||
selectedItem = [...selectedItem, it]
|
|
||||||
}
|
|
||||||
|
|
||||||
setFieldValue(name, selectedItem)
|
|
||||||
}}
|
|
||||||
selectedItem={value}>
|
|
||||||
{({
|
|
||||||
getInputProps,
|
|
||||||
getItemProps,
|
|
||||||
getMenuProps,
|
|
||||||
isOpen,
|
|
||||||
inputValue: inputValue2,
|
|
||||||
selectedItem: selectedItem2,
|
|
||||||
highlightedIndex,
|
|
||||||
toggleMenu
|
|
||||||
}) => (
|
|
||||||
<div className={classes.container}>
|
|
||||||
{renderInput({
|
|
||||||
id: name,
|
|
||||||
fullWidth: true,
|
|
||||||
classes,
|
|
||||||
error:
|
|
||||||
(touched[`${name}-input`] || touched[name]) && errors[name],
|
|
||||||
success:
|
|
||||||
(touched[`${name}-input`] || touched[name] || value) &&
|
|
||||||
!errors[name],
|
|
||||||
InputProps: getInputProps({
|
|
||||||
startAdornment: (value || []).map(item => (
|
|
||||||
<Chip key={item.code} tabIndex={-1} label={item.code} />
|
|
||||||
)),
|
|
||||||
onBlur,
|
|
||||||
onChange: it => setInputValue(it.target.value),
|
|
||||||
onClick: () => toggleMenu(),
|
|
||||||
onKeyDown: handleKeyDown,
|
|
||||||
placeholder
|
|
||||||
}),
|
|
||||||
ref: node => {
|
|
||||||
setPopperNode(node)
|
|
||||||
},
|
|
||||||
label
|
|
||||||
})}
|
|
||||||
<Popper open={isOpen} anchorEl={popperNode}>
|
|
||||||
<div
|
|
||||||
{...(isOpen
|
|
||||||
? getMenuProps({}, { suppressRefError: true })
|
|
||||||
: {})}>
|
|
||||||
<Paper
|
|
||||||
className={classes.paper}
|
|
||||||
square
|
|
||||||
style={{
|
|
||||||
marginTop: 8,
|
|
||||||
minWidth: popperNode ? popperNode.clientWidth : null
|
|
||||||
}}>
|
|
||||||
{filterSuggestions(suggestions, inputValue2, value).map(
|
|
||||||
(suggestion, index) =>
|
|
||||||
renderSuggestion({
|
|
||||||
suggestion,
|
|
||||||
index,
|
|
||||||
itemProps: getItemProps({ item: suggestion }),
|
|
||||||
highlightedIndex,
|
|
||||||
selectedItem: selectedItem2
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
</Popper>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Downshift>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export default withStyles(styles)(AutocompleteMultiple)
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
import MenuItem from '@material-ui/core/MenuItem'
|
|
||||||
import Fuse from 'fuse.js'
|
|
||||||
import React from 'react'
|
|
||||||
import slugify from 'slugify'
|
|
||||||
import { withStyles } from '@material-ui/core/styles'
|
|
||||||
|
|
||||||
import {
|
|
||||||
fontColor,
|
|
||||||
inputFontSize,
|
|
||||||
inputFontWeight,
|
|
||||||
zircon
|
|
||||||
} from 'src/styling/variables'
|
|
||||||
import S from 'src/utils/sanctuary'
|
|
||||||
|
|
||||||
import { TextInput } from '../base'
|
|
||||||
|
|
||||||
function renderInput({ InputProps, error, name, success, ...props }) {
|
|
||||||
const { onChange, onBlur, value } = InputProps
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TextInput
|
|
||||||
name={name}
|
|
||||||
onChange={onChange}
|
|
||||||
onBlur={onBlur}
|
|
||||||
value={value}
|
|
||||||
error={!!error}
|
|
||||||
InputProps={InputProps}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSuggestion({
|
|
||||||
suggestion,
|
|
||||||
index,
|
|
||||||
itemProps,
|
|
||||||
highlightedIndex,
|
|
||||||
selectedItem,
|
|
||||||
code,
|
|
||||||
display
|
|
||||||
}) {
|
|
||||||
const isHighlighted = highlightedIndex === index
|
|
||||||
|
|
||||||
const StyledMenuItem = withStyles(theme => ({
|
|
||||||
root: {
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 400,
|
|
||||||
color: fontColor
|
|
||||||
},
|
|
||||||
selected: {
|
|
||||||
'&.Mui-selected, &.Mui-selected:hover': {
|
|
||||||
fontWeight: 500,
|
|
||||||
backgroundColor: zircon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))(MenuItem)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledMenuItem
|
|
||||||
{...itemProps}
|
|
||||||
key={suggestion[code]}
|
|
||||||
selected={isHighlighted}
|
|
||||||
component="div">
|
|
||||||
{suggestion[display]}
|
|
||||||
</StyledMenuItem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterSuggestions(
|
|
||||||
suggestions = [],
|
|
||||||
value = '',
|
|
||||||
currentValues = [],
|
|
||||||
code,
|
|
||||||
display
|
|
||||||
) {
|
|
||||||
const options = {
|
|
||||||
shouldSort: true,
|
|
||||||
threshold: 0.2,
|
|
||||||
location: 0,
|
|
||||||
distance: 100,
|
|
||||||
maxPatternLength: 32,
|
|
||||||
minMatchCharLength: 1,
|
|
||||||
code,
|
|
||||||
display
|
|
||||||
}
|
|
||||||
|
|
||||||
const fuse = new Fuse(suggestions, options)
|
|
||||||
const result = value ? fuse.search(slugify(value, ' ')) : suggestions
|
|
||||||
|
|
||||||
const currentCodes = S.map(S.prop(code))(currentValues)
|
|
||||||
const filtered = S.filter(it => !S.elem(it[code])(currentCodes))(result)
|
|
||||||
|
|
||||||
const amountToTake = S.min(filtered.length)(5)
|
|
||||||
|
|
||||||
return S.compose(S.fromMaybe([]))(S.take(amountToTake))(filtered)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = theme => ({
|
|
||||||
root: {
|
|
||||||
flexGrow: 1,
|
|
||||||
height: 250
|
|
||||||
},
|
|
||||||
container: {
|
|
||||||
flexGrow: 1,
|
|
||||||
position: 'relative'
|
|
||||||
},
|
|
||||||
paper: {
|
|
||||||
// position: 'absolute',
|
|
||||||
zIndex: 1,
|
|
||||||
marginTop: theme.spacing(1),
|
|
||||||
left: 0,
|
|
||||||
right: 0
|
|
||||||
},
|
|
||||||
inputRoot: {
|
|
||||||
fontSize: inputFontSize,
|
|
||||||
color: fontColor,
|
|
||||||
fontWeight: inputFontWeight,
|
|
||||||
flexWrap: 'wrap'
|
|
||||||
},
|
|
||||||
inputInput: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
success: {
|
|
||||||
'&:after': {
|
|
||||||
transform: 'scaleX(1)'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
divider: {
|
|
||||||
height: theme.spacing(2)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export { renderInput, renderSuggestion, filterSuggestions, styles }
|
|
||||||
69
new-lamassu-admin/src/components/inputs/base/Autocomplete.js
Normal file
69
new-lamassu-admin/src/components/inputs/base/Autocomplete.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import MAutocomplete, {
|
||||||
|
createFilterOptions
|
||||||
|
} from '@material-ui/lab/Autocomplete'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import TextInput from './TextInput'
|
||||||
|
|
||||||
|
const Autocomplete = ({
|
||||||
|
limit = 5,
|
||||||
|
options,
|
||||||
|
label,
|
||||||
|
shouldAdd,
|
||||||
|
getOptionSelected,
|
||||||
|
forceShowValue,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
multiple,
|
||||||
|
getLabel,
|
||||||
|
error,
|
||||||
|
fullWidth,
|
||||||
|
textAlign,
|
||||||
|
size,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
let iOptions = options
|
||||||
|
|
||||||
|
const compare = getOptionSelected || R.equals
|
||||||
|
const find = R.find(it => compare(value, it))
|
||||||
|
|
||||||
|
if (forceShowValue && !multiple && value && !find(options)) {
|
||||||
|
iOptions = R.concat(options, [value])
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MAutocomplete
|
||||||
|
options={iOptions}
|
||||||
|
multiple={multiple}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
getOptionLabel={getLabel}
|
||||||
|
forcePopupIcon={false}
|
||||||
|
filterOptions={createFilterOptions({ ignoreAccents: true, limit })}
|
||||||
|
openOnFocus
|
||||||
|
autoHighlight
|
||||||
|
disableClearable
|
||||||
|
ChipProps={{ onDelete: null }}
|
||||||
|
blurOnSelect
|
||||||
|
clearOnEscape
|
||||||
|
getOptionSelected={getOptionSelected}
|
||||||
|
{...props}
|
||||||
|
renderInput={params => {
|
||||||
|
return (
|
||||||
|
<TextInput
|
||||||
|
{...params}
|
||||||
|
label={label}
|
||||||
|
value={value}
|
||||||
|
error={error}
|
||||||
|
size={size}
|
||||||
|
fullWidth={fullWidth}
|
||||||
|
textAlign={textAlign}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Autocomplete
|
||||||
|
|
@ -19,8 +19,6 @@ const useStyles = makeStyles({
|
||||||
const CheckboxInput = ({ name, onChange, value, label, ...props }) => {
|
const CheckboxInput = ({ name, onChange, value, label, ...props }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
// const { name, onChange, value } = props.field
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={name}
|
id={name}
|
||||||
|
|
|
||||||
|
|
@ -1,105 +1,12 @@
|
||||||
import React, { memo } from 'react'
|
import { makeStyles } from '@material-ui/core'
|
||||||
import classnames from 'classnames'
|
|
||||||
import TextField from '@material-ui/core/TextField'
|
import TextField from '@material-ui/core/TextField'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import classnames from 'classnames'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { memo } from 'react'
|
||||||
|
|
||||||
import {
|
import styles from './TextInput.styles'
|
||||||
fontColor,
|
|
||||||
offColor,
|
|
||||||
secondaryColor,
|
|
||||||
inputFontSize,
|
|
||||||
inputFontSizeLg,
|
|
||||||
inputFontWeight,
|
|
||||||
inputFontWeightLg
|
|
||||||
} from 'src/styling/variables'
|
|
||||||
import { TL2, Label2, Info1, Info2 } from 'src/components/typography'
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles(styles)
|
||||||
wrapper: {
|
|
||||||
display: 'inline-block',
|
|
||||||
maxWidth: '100%',
|
|
||||||
'& > span': {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'baseline',
|
|
||||||
'& > p:first-child': {
|
|
||||||
margin: [[0, 4, 5, 0]]
|
|
||||||
},
|
|
||||||
'&> p:last-child': {
|
|
||||||
margin: [[0, 0, 0, 3]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inputRoot: {
|
|
||||||
fontSize: inputFontSize,
|
|
||||||
color: fontColor,
|
|
||||||
fontWeight: inputFontWeight,
|
|
||||||
paddingLeft: 4,
|
|
||||||
'& > .MuiInputBase-input': {
|
|
||||||
width: 282
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inputRootLg: {
|
|
||||||
fontSize: inputFontSizeLg,
|
|
||||||
color: fontColor,
|
|
||||||
fontWeight: inputFontWeightLg,
|
|
||||||
'& > .MuiInputBase-input': {
|
|
||||||
width: 96
|
|
||||||
}
|
|
||||||
},
|
|
||||||
labelRoot: {
|
|
||||||
color: fontColor,
|
|
||||||
paddingLeft: 4
|
|
||||||
},
|
|
||||||
root: {
|
|
||||||
'& > .MuiInput-underline:before': {
|
|
||||||
borderBottom: [[2, 'solid', fontColor]]
|
|
||||||
},
|
|
||||||
'& .Mui-focused': {
|
|
||||||
color: fontColor
|
|
||||||
},
|
|
||||||
'& input': {
|
|
||||||
paddingTop: 4,
|
|
||||||
paddingBottom: 3
|
|
||||||
},
|
|
||||||
'& .MuiInputBase-inputMultiline': {
|
|
||||||
width: 500,
|
|
||||||
paddingRight: 20
|
|
||||||
}
|
|
||||||
},
|
|
||||||
empty: {
|
|
||||||
'& .MuiInputLabel-root:not(.MuiFormLabel-filled):not(.MuiInputLabel-shrink)': {
|
|
||||||
color: offColor
|
|
||||||
},
|
|
||||||
'& .MuiInputLabel-formControl:not(.MuiInputLabel-shrink)': {
|
|
||||||
top: -2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filled: {
|
|
||||||
'& .MuiInput-underline:before': {
|
|
||||||
borderBottomColor: secondaryColor
|
|
||||||
},
|
|
||||||
'& .MuiInput-underline:hover:not(.Mui-disabled)::before': {
|
|
||||||
borderBottomColor: secondaryColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const TextInputDisplay = memo(({ display, suffix, large }) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.wrapper}>
|
|
||||||
<span>
|
|
||||||
{large && !suffix && <span>{display}</span>}
|
|
||||||
{!large && !suffix && <span>{display}</span>}
|
|
||||||
{large && suffix && <Info1>{display}</Info1>}
|
|
||||||
{!large && suffix && <Info2>{display}</Info2>}
|
|
||||||
{suffix && large && <TL2>{suffix}</TL2>}
|
|
||||||
{suffix && !large && <Label2>{suffix}</Label2>}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const TextInput = memo(
|
const TextInput = memo(
|
||||||
({
|
({
|
||||||
|
|
@ -109,47 +16,43 @@ const TextInput = memo(
|
||||||
value,
|
value,
|
||||||
error,
|
error,
|
||||||
suffix,
|
suffix,
|
||||||
large,
|
textAlign,
|
||||||
|
width,
|
||||||
|
// lg or sm
|
||||||
|
size,
|
||||||
|
bold,
|
||||||
className,
|
className,
|
||||||
InputProps,
|
InputProps,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles({ textAlign, width, size })
|
||||||
|
const filled = !error && value && !R.isEmpty(value)
|
||||||
|
|
||||||
const classNames = {
|
const inputClasses = {
|
||||||
[className]: true,
|
[classes.bold]: bold
|
||||||
[classes.filled]: !error && value,
|
|
||||||
[classes.empty]: !value || value === ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<TextField
|
||||||
<span>
|
id={name}
|
||||||
<TextField
|
onChange={onChange}
|
||||||
id={name}
|
onBlur={onBlur}
|
||||||
onChange={onChange}
|
error={error}
|
||||||
onBlur={onBlur}
|
value={value}
|
||||||
error={error}
|
classes={{ root: classes.root }}
|
||||||
value={value}
|
className={className}
|
||||||
classes={{ root: classes.root }}
|
InputProps={{
|
||||||
className={classnames(classNames)}
|
className: classnames(inputClasses),
|
||||||
InputProps={{
|
classes: {
|
||||||
className: large ? classes.inputRootLg : classes.inputRoot,
|
root: classes.size,
|
||||||
...InputProps
|
underline: filled ? classes.underline : null
|
||||||
}}
|
},
|
||||||
InputLabelProps={{ className: classes.labelRoot }}
|
...InputProps
|
||||||
{...props}
|
}}
|
||||||
/>
|
{...props}
|
||||||
{suffix && large && (
|
/>
|
||||||
<>
|
|
||||||
<TL2>{suffix}</TL2>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{suffix && !large && <Label2>{suffix}</Label2>}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export { TextInput, TextInputDisplay }
|
export default TextInput
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { bySize, bold } from 'src/styling/helpers'
|
||||||
|
import { secondaryColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
size: ({ size }) => bySize(size),
|
||||||
|
bold,
|
||||||
|
root: ({ width, textAlign }) => ({
|
||||||
|
width,
|
||||||
|
'& input': {
|
||||||
|
textAlign
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
underline: {
|
||||||
|
'&:before': {
|
||||||
|
borderBottomColor: secondaryColor
|
||||||
|
},
|
||||||
|
'&:hover:not(.Mui-disabled)::before': {
|
||||||
|
borderBottomColor: secondaryColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
import Autocomplete from './Autocomplete'
|
||||||
import Checkbox from './Checkbox'
|
import Checkbox from './Checkbox'
|
||||||
import { TextInput } from './TextInput'
|
|
||||||
import Switch from './Switch'
|
|
||||||
import RadioGroup from './RadioGroup'
|
import RadioGroup from './RadioGroup'
|
||||||
|
import Switch from './Switch'
|
||||||
|
import TextInput from './TextInput'
|
||||||
|
|
||||||
export { Checkbox, TextInput, Switch, RadioGroup }
|
export { Checkbox, TextInput, Switch, RadioGroup, Autocomplete }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { Autocomplete } from '../base'
|
||||||
|
|
||||||
|
const AutocompleteFormik = props => {
|
||||||
|
const { name, onBlur, value } = props.field
|
||||||
|
const { touched, errors, setFieldValue } = props.form
|
||||||
|
const error = !!(touched[name] && errors[name])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Autocomplete
|
||||||
|
name={name}
|
||||||
|
onChange={(event, item) => setFieldValue(name, item)}
|
||||||
|
onBlur={onBlur}
|
||||||
|
value={value}
|
||||||
|
error={error}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AutocompleteFormik
|
||||||
|
|
@ -2,7 +2,7 @@ import React, { memo } from 'react'
|
||||||
|
|
||||||
import { Checkbox } from '../base'
|
import { Checkbox } from '../base'
|
||||||
|
|
||||||
const CheckboxInput = memo(({ label, ...props }) => {
|
const CheckboxInput = memo(({ label, textAlign, fullWidth, ...props }) => {
|
||||||
const { name, onChange, value } = props.field
|
const { name, onChange, value } = props.field
|
||||||
|
|
||||||
return <Checkbox name={name} onChange={onChange} value={value} {...props} />
|
return <Checkbox name={name} onChange={onChange} value={value} {...props} />
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import {
|
import {
|
||||||
fontColor,
|
fontColor,
|
||||||
offColor,
|
offColor,
|
||||||
inputFontSize,
|
inputFontSize,
|
||||||
inputFontWeight
|
inputFontWeight
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { info3 } = typographyStyles
|
const { info3 } = typographyStyles
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
import Autocomplete from './autocomplete/Autocomplete'
|
|
||||||
import AutocompleteMultiple from './autocomplete/AutocompleteMultiple'
|
|
||||||
import Checkbox from './base/Checkbox'
|
import Checkbox from './base/Checkbox'
|
||||||
import Radio from './base/Radio'
|
import Radio from './base/Radio'
|
||||||
import RadioGroup from './base/RadioGroup'
|
import RadioGroup from './base/RadioGroup'
|
||||||
import Select from './base/Select'
|
import Select from './base/Select'
|
||||||
import Switch from './base/Switch'
|
import Switch from './base/Switch'
|
||||||
import { TextInput } from './base/TextInput'
|
import TextInput from './base/TextInput'
|
||||||
|
|
||||||
export {
|
export { TextInput, Radio, Checkbox, Switch, Select, RadioGroup }
|
||||||
Autocomplete,
|
|
||||||
AutocompleteMultiple,
|
|
||||||
TextInput,
|
|
||||||
Radio,
|
|
||||||
Checkbox,
|
|
||||||
Switch,
|
|
||||||
Select,
|
|
||||||
RadioGroup
|
|
||||||
}
|
|
||||||
|
|
|
||||||
24
new-lamassu-admin/src/components/layout/Section.js
Normal file
24
new-lamassu-admin/src/components/layout/Section.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import ErrorMessage from 'src/components/ErrorMessage'
|
||||||
|
import { TL1 } from 'src/components/typography'
|
||||||
|
|
||||||
|
import styles from './Section.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const Section = ({ error, children, title }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<div className={classes.section}>
|
||||||
|
<div className={classes.sectionHeader}>
|
||||||
|
<TL1 className={classes.sectionTitle}>{title}</TL1>
|
||||||
|
{error && <ErrorMessage>Failed to save changes</ErrorMessage>}
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Section
|
||||||
15
new-lamassu-admin/src/components/layout/Section.styles.js
Normal file
15
new-lamassu-admin/src/components/layout/Section.styles.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { offColor } from 'src/styling/variables'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
section: {
|
||||||
|
marginBottom: 72
|
||||||
|
},
|
||||||
|
sectionHeader: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
color: offColor,
|
||||||
|
margin: [[16, 20, 23, 0]]
|
||||||
|
}
|
||||||
|
}
|
||||||
21
new-lamassu-admin/src/components/layout/TitleSection.js
Normal file
21
new-lamassu-admin/src/components/layout/TitleSection.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Title from 'src/components/Title'
|
||||||
|
|
||||||
|
import styles from './TitleSection.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const TitleSection = ({ title }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<div className={classes.titleWrapper}>
|
||||||
|
<div className={classes.titleAndButtonsContainer}>
|
||||||
|
<Title>{title}</Title>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TitleSection
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import React from 'react'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/white.svg'
|
import { Table, THead, TBody, Td, Th } from 'src/components/fake-table/Table'
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||||
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/white.svg'
|
||||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||||
import {
|
import {
|
||||||
offColor,
|
offColor,
|
||||||
|
|
@ -11,8 +13,6 @@ import {
|
||||||
tableNewDisabledHeaderColor,
|
tableNewDisabledHeaderColor,
|
||||||
secondaryColorDarker
|
secondaryColorDarker
|
||||||
} from 'src/styling/variables'
|
} from 'src/styling/variables'
|
||||||
import { Table, THead, TBody, Td, Th } from 'src/components/fake-table/Table'
|
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
|
|
||||||
const { label1, p } = typographyStyles
|
const { label1, p } = typographyStyles
|
||||||
|
|
||||||
|
|
@ -106,11 +106,11 @@ const SingleRowTable = ({
|
||||||
{items && (
|
{items && (
|
||||||
<Table className={classnames(className, classes.wrapper)}>
|
<Table className={classnames(className, classes.wrapper)}>
|
||||||
<THead className={classnames(headerClasses)}>
|
<THead className={classnames(headerClasses)}>
|
||||||
<Th size={width - editButtonSize}>
|
<Th width={width - editButtonSize}>
|
||||||
{title}
|
{title}
|
||||||
{newService && <span className={classes.spanNew}>New</span>}
|
{newService && <span className={classes.spanNew}>New</span>}
|
||||||
</Th>
|
</Th>
|
||||||
<Th size={editButtonSize} className={classes.buttonTh}>
|
<Th width={editButtonSize} className={classes.buttonTh}>
|
||||||
{!disabled && (
|
{!disabled && (
|
||||||
<button className={classes.editButton} onClick={onEdit}>
|
<button className={classes.editButton} onClick={onEdit}>
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
|
|
@ -124,7 +124,7 @@ const SingleRowTable = ({
|
||||||
</Th>
|
</Th>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody className={classnames(bodyClasses)}>
|
<TBody className={classnames(bodyClasses)}>
|
||||||
<Td size={width}>
|
<Td width={width}>
|
||||||
{!disabled && (
|
{!disabled && (
|
||||||
<>
|
<>
|
||||||
{items[0] && (
|
{items[0] && (
|
||||||
|
|
|
||||||
8
new-lamassu-admin/src/components/tables/BooleanCell.js
Normal file
8
new-lamassu-admin/src/components/tables/BooleanCell.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { ReactComponent as FalseIcon } from 'src/styling/icons/table/false.svg'
|
||||||
|
import { ReactComponent as TrueIcon } from 'src/styling/icons/table/true.svg'
|
||||||
|
|
||||||
|
const BooleanCell = ({ value }) => (value ? <TrueIcon /> : <FalseIcon />)
|
||||||
|
|
||||||
|
export default BooleanCell
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import useAxios from '@use-hooks/axios'
|
import useAxios from '@use-hooks/axios'
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useLocation, useHistory } from 'react-router-dom'
|
import { useLocation, useHistory } from 'react-router-dom'
|
||||||
|
|
||||||
const useQuery = () => new URLSearchParams(useLocation().search)
|
const useQuery = () => new URLSearchParams(useLocation().search)
|
||||||
|
const url =
|
||||||
|
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
|
||||||
|
|
||||||
const AuthRegister = () => {
|
const AuthRegister = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const query = useQuery()
|
const query = useQuery()
|
||||||
|
|
||||||
useAxios({
|
useAxios({
|
||||||
url: `/api/register?otp=${query.get('otp')}`,
|
url: `${url}/api/register?otp=${query.get('otp')}`,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
options: {
|
options: {
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { gql } from 'apollo-boost'
|
||||||
|
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
|
||||||
|
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import { DataTable } from 'src/components/dataTable'
|
import { DataTable } from 'src/components/dataTable'
|
||||||
|
|
@ -38,34 +38,34 @@ const Customers = () => {
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
header: 'Name',
|
header: 'Name',
|
||||||
size: 277,
|
width: 277,
|
||||||
view: R.path(['name'])
|
view: R.path(['name'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Phone',
|
header: 'Phone',
|
||||||
size: 166,
|
width: 166,
|
||||||
view: it => parsePhoneNumberFromString(it.phone).formatInternational()
|
view: it => parsePhoneNumberFromString(it.phone).formatInternational()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Total TXs',
|
header: 'Total TXs',
|
||||||
size: 174,
|
width: 174,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it => `${Number.parseInt(it.totalTxs)}`
|
view: it => `${Number.parseInt(it.totalTxs)}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Total spent',
|
header: 'Total spent',
|
||||||
size: 188,
|
width: 188,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it => `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode}`
|
view: it => `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Last active',
|
header: 'Last active',
|
||||||
size: 197,
|
width: 197,
|
||||||
view: it => moment.utc(it.lastActive).format('YYYY-MM-D')
|
view: it => moment.utc(it.lastActive).format('YYYY-MM-D')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Last transaction',
|
header: 'Last transaction',
|
||||||
size: 198,
|
width: 198,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it => (
|
view: it => (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { gql } from 'apollo-boost'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import QRCode from 'qrcode.react'
|
import QRCode from 'qrcode.react'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
|
|
||||||
import Sidebar from 'src/components/Sidebar'
|
import Sidebar from 'src/components/Sidebar'
|
||||||
import TableLabel from 'src/components/TableLabel'
|
import TableLabel from 'src/components/TableLabel'
|
||||||
|
|
@ -200,22 +200,22 @@ const Funding = () => {
|
||||||
/>
|
/>
|
||||||
<Table className={classes.table}>
|
<Table className={classes.table}>
|
||||||
<THead>
|
<THead>
|
||||||
<Td header size={sizes.big}>
|
<Td header width={sizes.big}>
|
||||||
Amount Entered
|
Amount Entered
|
||||||
</Td>
|
</Td>
|
||||||
<Td header size={sizes.big}>
|
<Td header width={sizes.big}>
|
||||||
Balance After
|
Balance After
|
||||||
</Td>
|
</Td>
|
||||||
<Td header size={sizes.big}>
|
<Td header width={sizes.big}>
|
||||||
Cash Value
|
Cash Value
|
||||||
</Td>
|
</Td>
|
||||||
<Td header size={sizes.date}>
|
<Td header width={sizes.date}>
|
||||||
Date
|
Date
|
||||||
</Td>
|
</Td>
|
||||||
<Td header size={sizes.time}>
|
<Td header width={sizes.time}>
|
||||||
Time (h:m:s)
|
Time (h:m:s)
|
||||||
</Td>
|
</Td>
|
||||||
<Td header size={sizes.big}>
|
<Td header width={sizes.big}>
|
||||||
Performed By
|
Performed By
|
||||||
</Td>
|
</Td>
|
||||||
</THead>
|
</THead>
|
||||||
|
|
@ -224,22 +224,22 @@ const Funding = () => {
|
||||||
<Tr
|
<Tr
|
||||||
key={idx}
|
key={idx}
|
||||||
className={classnames({ [classes.pending]: it.pending })}>
|
className={classnames({ [classes.pending]: it.pending })}>
|
||||||
<Td size={sizes.big}>
|
<Td width={sizes.big}>
|
||||||
{it.cryptoAmount} {selected.cryptoCode}
|
{it.cryptoAmount} {selected.cryptoCode}
|
||||||
</Td>
|
</Td>
|
||||||
<Td size={sizes.big}>
|
<Td width={sizes.big}>
|
||||||
{it.balance} {selected.cryptoCode}
|
{it.balance} {selected.cryptoCode}
|
||||||
</Td>
|
</Td>
|
||||||
<Td size={sizes.big}>
|
<Td width={sizes.big}>
|
||||||
{it.fiatValue} {selected.fiatCode}
|
{it.fiatValue} {selected.fiatCode}
|
||||||
</Td>
|
</Td>
|
||||||
<Td size={sizes.date}>
|
<Td width={sizes.date}>
|
||||||
{moment(it.date).format('YYYY-MM-DD')}
|
{moment(it.date).format('YYYY-MM-DD')}
|
||||||
</Td>
|
</Td>
|
||||||
<Td size={sizes.time}>
|
<Td width={sizes.time}>
|
||||||
{moment(it.date).format('hh:mm:ss')}
|
{moment(it.date).format('hh:mm:ss')}
|
||||||
</Td>
|
</Td>
|
||||||
<Td size={sizes.big}>add</Td>
|
<Td width={sizes.big}>add</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
</TBody>
|
</TBody>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { memo } from 'react'
|
|
||||||
import * as Yup from 'yup'
|
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { gql } from 'apollo-boost'
|
import { gql } from 'apollo-boost'
|
||||||
|
import React, { memo } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
import Subtitle from 'src/components/Subtitle'
|
import Subtitle from 'src/components/Subtitle'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
|
|
|
||||||
|
|
@ -20,41 +20,48 @@ const MainForm = memo(({ value, save, auxData, validationSchema }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditableTable
|
<EditableTable
|
||||||
|
name="locale"
|
||||||
|
title="Default Settings"
|
||||||
|
altTitleColor
|
||||||
|
initialValues={{ country: null }}
|
||||||
|
enableEdit
|
||||||
|
onDelete={() => {}}
|
||||||
|
setEditing={() => {}}
|
||||||
save={save}
|
save={save}
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
data={R.of(value)}
|
data={R.of(value)}
|
||||||
elements={[
|
elements={[
|
||||||
{
|
{
|
||||||
name: 'country',
|
name: 'country',
|
||||||
size: sizes.country,
|
width: sizes.country,
|
||||||
view: R.path(['display']),
|
view: R.path(['display']),
|
||||||
input: Autocomplete,
|
input: Autocomplete,
|
||||||
inputProps: { suggestions: getData(['countries']) }
|
inputProps: { suggestions: getData(['countries']) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'fiatCurrency',
|
name: 'fiatCurrency',
|
||||||
size: sizes.fiatCurrency,
|
width: sizes.fiatCurrency,
|
||||||
view: R.path(['code']),
|
view: R.path(['code']),
|
||||||
input: Autocomplete,
|
input: Autocomplete,
|
||||||
inputProps: { suggestions: getData(['currencies']) }
|
inputProps: { suggestions: getData(['currencies']) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'languages',
|
name: 'languages',
|
||||||
size: sizes.languages,
|
width: sizes.languages,
|
||||||
view: displayCodeArray,
|
view: displayCodeArray,
|
||||||
input: AutocompleteMultiple,
|
input: AutocompleteMultiple,
|
||||||
inputProps: { suggestions: getData(['languages']) }
|
inputProps: { suggestions: getData(['languages']) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cryptoCurrencies',
|
name: 'cryptoCurrencies',
|
||||||
size: sizes.cryptoCurrencies,
|
width: sizes.cryptoCurrencies,
|
||||||
view: displayCodeArray,
|
view: displayCodeArray,
|
||||||
input: AutocompleteMultiple,
|
input: AutocompleteMultiple,
|
||||||
inputProps: { suggestions: getData(['cryptoCurrencies']) }
|
inputProps: { suggestions: getData(['cryptoCurrencies']) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'showRates',
|
name: 'showRates',
|
||||||
size: sizes.showRates,
|
width: sizes.showRates,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
view: it => (it ? 'true' : 'false'),
|
view: it => (it ? 'true' : 'false'),
|
||||||
input: Checkbox
|
input: Checkbox
|
||||||
|
|
|
||||||
|
|
@ -1,254 +0,0 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import * as Yup from 'yup'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
|
|
||||||
import { Info2 } from 'src/components/typography'
|
|
||||||
import commonStyles from 'src/pages/common.styles'
|
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
|
||||||
import Link from 'src/components/buttons/Link.js'
|
|
||||||
import { Autocomplete } from 'src/components/inputs/index.js'
|
|
||||||
import { AddButton } from 'src/components/buttons/index.js'
|
|
||||||
import TextInputFormik from 'src/components/inputs/formik/TextInput.js'
|
|
||||||
|
|
||||||
import {
|
|
||||||
isDisabled,
|
|
||||||
LOW_BALANCE_KEY,
|
|
||||||
HIGH_BALANCE_KEY,
|
|
||||||
OVERRIDES_KEY,
|
|
||||||
ADD_OVERRIDE_CBA_KEY
|
|
||||||
} from './aux.js'
|
|
||||||
import { BigNumericInput } from './Inputs'
|
|
||||||
import { localStyles, cryptoBalanceAlertsStyles } from './Notifications.styles'
|
|
||||||
|
|
||||||
const CRYPTOCURRENCY_KEY = 'cryptocurrency'
|
|
||||||
|
|
||||||
const styles = R.mergeAll([
|
|
||||||
commonStyles,
|
|
||||||
localStyles,
|
|
||||||
cryptoBalanceAlertsStyles
|
|
||||||
])
|
|
||||||
|
|
||||||
const GET_CRYPTOCURRENCIES = gql`
|
|
||||||
{
|
|
||||||
cryptoCurrencies {
|
|
||||||
code
|
|
||||||
display
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const CryptoBalanceAlerts = ({
|
|
||||||
values: setupValues,
|
|
||||||
save,
|
|
||||||
editingState,
|
|
||||||
handleEditingClick,
|
|
||||||
setError
|
|
||||||
}) => {
|
|
||||||
const [cryptoCurrencies, setCryptoCurrencies] = useState(null)
|
|
||||||
|
|
||||||
useQuery(GET_CRYPTOCURRENCIES, {
|
|
||||||
onCompleted: data => {
|
|
||||||
setCryptoCurrencies(data.cryptoCurrencies)
|
|
||||||
},
|
|
||||||
onError: error => console.error(error)
|
|
||||||
})
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const editingLowBalance = editingState[LOW_BALANCE_KEY]
|
|
||||||
const editingHighBalance = editingState[HIGH_BALANCE_KEY]
|
|
||||||
const addingOverride = editingState[ADD_OVERRIDE_CBA_KEY]
|
|
||||||
|
|
||||||
const overrideOpsDisabled = isDisabled(editingState, ADD_OVERRIDE_CBA_KEY)
|
|
||||||
|
|
||||||
const handleEdit = R.curry(handleEditingClick)
|
|
||||||
|
|
||||||
const handleSubmit = it => save(it)
|
|
||||||
|
|
||||||
const handleSubmitOverrides = it => {
|
|
||||||
const newOverrides = {
|
|
||||||
[OVERRIDES_KEY]: R.prepend(it, setupValues[OVERRIDES_KEY])
|
|
||||||
}
|
|
||||||
save(newOverrides)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleResetForm = () => {
|
|
||||||
handleEdit(ADD_OVERRIDE_CBA_KEY)(false)
|
|
||||||
setError(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteOverride = it => {
|
|
||||||
const cryptocurrency = it[CRYPTOCURRENCY_KEY]
|
|
||||||
|
|
||||||
const idx = R.findIndex(
|
|
||||||
R.propEq([CRYPTOCURRENCY_KEY], cryptocurrency),
|
|
||||||
setupValues[OVERRIDES_KEY]
|
|
||||||
)
|
|
||||||
const newOverrides = R.remove(idx, 1, setupValues[OVERRIDES_KEY])
|
|
||||||
|
|
||||||
save({ [OVERRIDES_KEY]: newOverrides })
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultsFields = {
|
|
||||||
[LOW_BALANCE_KEY]: {
|
|
||||||
name: LOW_BALANCE_KEY,
|
|
||||||
label: 'Alert me under',
|
|
||||||
value: setupValues[LOW_BALANCE_KEY]
|
|
||||||
},
|
|
||||||
[HIGH_BALANCE_KEY]: {
|
|
||||||
name: HIGH_BALANCE_KEY,
|
|
||||||
label: 'Alert me over',
|
|
||||||
value: setupValues[HIGH_BALANCE_KEY]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSuggestions = () => {
|
|
||||||
const overridenCryptos = R.map(
|
|
||||||
override => override[CRYPTOCURRENCY_KEY],
|
|
||||||
setupValues[OVERRIDES_KEY]
|
|
||||||
)
|
|
||||||
return R.without(overridenCryptos, cryptoCurrencies ?? [])
|
|
||||||
}
|
|
||||||
|
|
||||||
const { [OVERRIDES_KEY]: overrides } = setupValues
|
|
||||||
|
|
||||||
const initialValues = {
|
|
||||||
[CRYPTOCURRENCY_KEY]: '',
|
|
||||||
[LOW_BALANCE_KEY]: '',
|
|
||||||
[HIGH_BALANCE_KEY]: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
|
||||||
[CRYPTOCURRENCY_KEY]: Yup.string().required(),
|
|
||||||
[LOW_BALANCE_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(99999999)
|
|
||||||
.required(),
|
|
||||||
[HIGH_BALANCE_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(99999999)
|
|
||||||
.required()
|
|
||||||
})
|
|
||||||
|
|
||||||
const elements = [
|
|
||||||
{
|
|
||||||
name: CRYPTOCURRENCY_KEY,
|
|
||||||
display: 'Cryptocurrency',
|
|
||||||
size: 166,
|
|
||||||
textAlign: 'left',
|
|
||||||
view: R.path(['display']),
|
|
||||||
type: 'text',
|
|
||||||
input: Autocomplete,
|
|
||||||
inputProps: {
|
|
||||||
suggestions: getSuggestions(),
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: LOW_BALANCE_KEY,
|
|
||||||
display: 'Low Balance',
|
|
||||||
size: 140,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: 'EUR', // TODO: Current currency?
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: HIGH_BALANCE_KEY,
|
|
||||||
display: 'High Balance',
|
|
||||||
size: 140,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: 'EUR', // TODO: Current currency?
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'delete',
|
|
||||||
size: 91
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (!cryptoCurrencies) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<div className={classnames(classes.defaults, classes.cbaDefaults)}>
|
|
||||||
<BigNumericInput
|
|
||||||
title="Default (Low Balance)"
|
|
||||||
field={defaultsFields[LOW_BALANCE_KEY]}
|
|
||||||
editing={editingLowBalance}
|
|
||||||
disabled={isDisabled(editingState, LOW_BALANCE_KEY)}
|
|
||||||
setEditing={handleEdit(LOW_BALANCE_KEY)}
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
className={classes.lowBalance}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
<BigNumericInput
|
|
||||||
title="Default (High Balance)"
|
|
||||||
field={defaultsFields[HIGH_BALANCE_KEY]}
|
|
||||||
editing={editingHighBalance}
|
|
||||||
disabled={isDisabled(editingState, HIGH_BALANCE_KEY)}
|
|
||||||
setEditing={handleEdit(HIGH_BALANCE_KEY)}
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.overrides}>
|
|
||||||
<div className={classes.overridesTitle}>
|
|
||||||
<Info2>Overrides</Info2>
|
|
||||||
{!addingOverride && !overrideOpsDisabled && overrides.length > 0 && (
|
|
||||||
<Link
|
|
||||||
color="primary"
|
|
||||||
onClick={() => handleEdit(ADD_OVERRIDE_CBA_KEY)(true)}>
|
|
||||||
Add override
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!addingOverride && !overrideOpsDisabled && overrides.length === 0 && (
|
|
||||||
<AddButton onClick={() => handleEdit(ADD_OVERRIDE_CBA_KEY)(true)}>
|
|
||||||
Add overrides
|
|
||||||
</AddButton>
|
|
||||||
)}
|
|
||||||
{(addingOverride || overrides.length > 0) && (
|
|
||||||
<EditableTable
|
|
||||||
className={classes.overridesTable}
|
|
||||||
addingRow={addingOverride}
|
|
||||||
disableAction={overrideOpsDisabled || addingOverride}
|
|
||||||
editing={R.map(
|
|
||||||
() => false,
|
|
||||||
R.range(0, setupValues[OVERRIDES_KEY].length)
|
|
||||||
)}
|
|
||||||
save={handleSubmitOverrides}
|
|
||||||
reset={handleResetForm}
|
|
||||||
action={deleteOverride}
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
data={setupValues[OVERRIDES_KEY]}
|
|
||||||
elements={elements}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CryptoBalanceAlerts
|
|
||||||
|
|
@ -1,597 +0,0 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import * as Yup from 'yup'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
|
||||||
|
|
||||||
import { Info2 } from 'src/components/typography'
|
|
||||||
import commonStyles from 'src/pages/common.styles'
|
|
||||||
import { Table as EditableTable } from 'src/components/editableTable'
|
|
||||||
import { Link, AddButton } from 'src/components/buttons'
|
|
||||||
import { Autocomplete } from 'src/components/inputs'
|
|
||||||
import TextInputFormik from 'src/components/inputs/formik/TextInput.js'
|
|
||||||
|
|
||||||
import { BigPercentageAndNumericInput, MultiplePercentageInput } from './Inputs'
|
|
||||||
import { localStyles, fiatBalanceAlertsStyles } from './Notifications.styles'
|
|
||||||
import {
|
|
||||||
CASH_IN_FULL_KEY,
|
|
||||||
isDisabled,
|
|
||||||
CASH_OUT_EMPTY_KEY,
|
|
||||||
CASSETTE_1_KEY,
|
|
||||||
CASSETTE_2_KEY,
|
|
||||||
PERCENTAGE_KEY,
|
|
||||||
NUMERARY_KEY,
|
|
||||||
OVERRIDES_KEY,
|
|
||||||
ADD_OVERRIDE_FBA_KEY,
|
|
||||||
MACHINE_KEY
|
|
||||||
} from './aux'
|
|
||||||
|
|
||||||
const styles = R.mergeAll([commonStyles, localStyles, fiatBalanceAlertsStyles])
|
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const GET_MACHINES = gql`
|
|
||||||
{
|
|
||||||
machines {
|
|
||||||
name
|
|
||||||
deviceId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// const OverridesRow = ({
|
|
||||||
// machine,
|
|
||||||
// handleSubmitOverrides,
|
|
||||||
// handleEdit,
|
|
||||||
// setError,
|
|
||||||
// sizes,
|
|
||||||
// editing,
|
|
||||||
// fields,
|
|
||||||
// disabled,
|
|
||||||
// getSuggestions,
|
|
||||||
// ...props
|
|
||||||
// }) => {
|
|
||||||
// const classes = useStyles()
|
|
||||||
|
|
||||||
// const baseInitialValues = {
|
|
||||||
// [fields[PERCENTAGE_KEY].name]: fields[PERCENTAGE_KEY].value ?? '',
|
|
||||||
// [fields[NUMERARY_KEY].name]: fields[NUMERARY_KEY].value ?? '',
|
|
||||||
// [fields[CASSETTE_1_KEY].name]: fields[CASSETTE_1_KEY].value ?? '',
|
|
||||||
// [fields[CASSETTE_2_KEY].name]: fields[CASSETTE_2_KEY].value ?? ''
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const initialValues = machine
|
|
||||||
// ? baseInitialValues
|
|
||||||
// : R.assoc(fields[MACHINE_KEY].name, '', baseInitialValues)
|
|
||||||
|
|
||||||
// const baseValidationSchemaShape = {
|
|
||||||
// [fields[PERCENTAGE_KEY].name]: Yup.number()
|
|
||||||
// .integer()
|
|
||||||
// .min(0)
|
|
||||||
// .max(100)
|
|
||||||
// .required(),
|
|
||||||
// [fields[NUMERARY_KEY].name]: Yup.number()
|
|
||||||
// .integer()
|
|
||||||
// .min(0)
|
|
||||||
// .max(99999999)
|
|
||||||
// .required(),
|
|
||||||
// [fields[CASSETTE_1_KEY].name]: Yup.number()
|
|
||||||
// .integer()
|
|
||||||
// .min(0)
|
|
||||||
// .max(100)
|
|
||||||
// .required(),
|
|
||||||
// [fields[CASSETTE_2_KEY].name]: Yup.number()
|
|
||||||
// .integer()
|
|
||||||
// .min(0)
|
|
||||||
// .max(100)
|
|
||||||
// .required()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const validationSchemaShape = machine
|
|
||||||
// ? baseValidationSchemaShape
|
|
||||||
// : R.assoc(
|
|
||||||
// fields[MACHINE_KEY].name,
|
|
||||||
// Yup.string().required(),
|
|
||||||
// baseValidationSchemaShape
|
|
||||||
// )
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <Formik
|
|
||||||
// initialValues={initialValues}
|
|
||||||
// validationSchema={Yup.object().shape(validationSchemaShape)}
|
|
||||||
// onSubmit={values => {
|
|
||||||
// const machineName = machine
|
|
||||||
// ? machine.name
|
|
||||||
// : values[fields[MACHINE_KEY].name].name
|
|
||||||
// handleSubmitOverrides(machineName)(values)
|
|
||||||
// }}
|
|
||||||
// onReset={(values, bag) => {
|
|
||||||
// handleEdit(machine?.name ?? ADD_OVERRIDE_FBA_KEY)(false)
|
|
||||||
// setError(null)
|
|
||||||
// }}>
|
|
||||||
// <Form>
|
|
||||||
// <Tr>
|
|
||||||
// <Td size={sizes.machine}>
|
|
||||||
// {machine && machine.name}
|
|
||||||
// {!machine && (
|
|
||||||
// <FormikField
|
|
||||||
// id={fields[MACHINE_KEY].name}
|
|
||||||
// name={fields[MACHINE_KEY].name}
|
|
||||||
// component={Autocomplete}
|
|
||||||
// type="text"
|
|
||||||
// suggestions={getSuggestions()}
|
|
||||||
// code="deviceId"
|
|
||||||
// display="name"
|
|
||||||
// />
|
|
||||||
// )}
|
|
||||||
// </Td>
|
|
||||||
// <CellDoubleLevel className={classes.doubleLevelRow}>
|
|
||||||
// <Td size={sizes.percentage} textAlign="right">
|
|
||||||
// <Field
|
|
||||||
// editing={editing}
|
|
||||||
// field={fields[PERCENTAGE_KEY]}
|
|
||||||
// displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
// decoration="%"
|
|
||||||
// className={classes.eRowField}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// </Td>
|
|
||||||
// <Td size={sizes.amount} textAlign="right">
|
|
||||||
// <Field
|
|
||||||
// editing={editing}
|
|
||||||
// field={fields[NUMERARY_KEY]}
|
|
||||||
// displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
// decoration="EUR"
|
|
||||||
// className={classes.eRowField}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// </Td>
|
|
||||||
// </CellDoubleLevel>
|
|
||||||
// <CellDoubleLevel className={classes.doubleLevelRow}>
|
|
||||||
// <Td size={sizes.cashOut1} textAlign="right">
|
|
||||||
// <Field
|
|
||||||
// editing={editing}
|
|
||||||
// field={fields[CASSETTE_1_KEY]}
|
|
||||||
// displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
// decoration="%"
|
|
||||||
// className={classes.eRowField}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// </Td>
|
|
||||||
// <Td size={sizes.cashOut2} textAlign="right">
|
|
||||||
// <Field
|
|
||||||
// editing={editing}
|
|
||||||
// field={fields[CASSETTE_2_KEY]}
|
|
||||||
// displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
// decoration="%"
|
|
||||||
// className={classes.eRowField}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// </Td>
|
|
||||||
// </CellDoubleLevel>
|
|
||||||
// <Td
|
|
||||||
// size={sizes.edit}
|
|
||||||
// textAlign="center"
|
|
||||||
// className={editing && classes.edit}>
|
|
||||||
// {!editing && !disabled && (
|
|
||||||
// <button
|
|
||||||
// className={classes.button}
|
|
||||||
// onClick={() => handleEdit(machine.name)(true)}>
|
|
||||||
// <EditIcon />
|
|
||||||
// </button>
|
|
||||||
// )}
|
|
||||||
// {disabled && (
|
|
||||||
// <div>
|
|
||||||
// <DisabledEditIcon />
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// {editing && (
|
|
||||||
// <>
|
|
||||||
// <Link color="primary" type="submit">
|
|
||||||
// Save
|
|
||||||
// </Link>
|
|
||||||
// <Link color="secondary" type="reset">
|
|
||||||
// Cancel
|
|
||||||
// </Link>
|
|
||||||
// </>
|
|
||||||
// )}
|
|
||||||
// </Td>
|
|
||||||
// </Tr>
|
|
||||||
// </Form>
|
|
||||||
// </Formik>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const FiatBalanceAlerts = ({
|
|
||||||
values: setupValues,
|
|
||||||
save,
|
|
||||||
editingState,
|
|
||||||
handleEditingClick,
|
|
||||||
setError
|
|
||||||
}) => {
|
|
||||||
const [machines, setMachines] = useState(null)
|
|
||||||
useQuery(GET_MACHINES, {
|
|
||||||
onCompleted: data => {
|
|
||||||
setMachines(data.machines)
|
|
||||||
},
|
|
||||||
onError: error => console.error(error)
|
|
||||||
})
|
|
||||||
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const getValue = R.path(R.__, setupValues)
|
|
||||||
|
|
||||||
const handleEdit = R.curry(handleEditingClick)
|
|
||||||
|
|
||||||
const handleSubmit = R.curry((key, it) => {
|
|
||||||
const setup = setupValues[key]
|
|
||||||
const pairs = R.mapObjIndexed((num, k, obj) => {
|
|
||||||
return [R.split('-', k)[1], num]
|
|
||||||
}, it)
|
|
||||||
const rightKeys = R.fromPairs(R.values(pairs))
|
|
||||||
const newItem = { [key]: R.merge(setup, rightKeys) }
|
|
||||||
save(newItem)
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSubmitOverrides = R.curry((key, it) => {
|
|
||||||
const setup = setupValues[OVERRIDES_KEY]
|
|
||||||
const pathMatches = R.pathEq(['machine', 'name'], key)
|
|
||||||
|
|
||||||
const pairs = R.values(
|
|
||||||
R.mapObjIndexed((num, k, obj) => {
|
|
||||||
const split = R.split('-', k)
|
|
||||||
if (split.length < 3) return { [split[1]]: num }
|
|
||||||
return { [split[1]]: { [split[2]]: num } }
|
|
||||||
}, it)
|
|
||||||
)
|
|
||||||
|
|
||||||
const old = R.find(pathMatches, setup)
|
|
||||||
if (!old) {
|
|
||||||
const newOverride = R.reduce(R.mergeDeepRight, {}, pairs)
|
|
||||||
const newOverrides = {
|
|
||||||
[OVERRIDES_KEY]: R.prepend(newOverride, setup)
|
|
||||||
}
|
|
||||||
save(newOverrides)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const machineIdx = R.findIndex(pathMatches, setup)
|
|
||||||
const newOverride = R.mergeDeepRight(
|
|
||||||
old,
|
|
||||||
R.reduce(R.mergeDeepRight, {}, pairs)
|
|
||||||
)
|
|
||||||
const newOverrides = {
|
|
||||||
[OVERRIDES_KEY]: R.update(machineIdx, newOverride, setup)
|
|
||||||
}
|
|
||||||
save(newOverrides)
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleResetForm = it => {
|
|
||||||
const machine = it?.machine
|
|
||||||
handleEdit(machine?.name ?? ADD_OVERRIDE_FBA_KEY)(false)
|
|
||||||
setError(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSuggestions = () => {
|
|
||||||
const overridenMachines = R.map(
|
|
||||||
override => override.machine,
|
|
||||||
setupValues[OVERRIDES_KEY]
|
|
||||||
)
|
|
||||||
return R.without(overridenMachines, machines ?? [])
|
|
||||||
}
|
|
||||||
|
|
||||||
const cashInFields = {
|
|
||||||
percentage: {
|
|
||||||
name: CASH_IN_FULL_KEY + '-' + PERCENTAGE_KEY,
|
|
||||||
label: 'Alert me over',
|
|
||||||
value: getValue([CASH_IN_FULL_KEY, PERCENTAGE_KEY])
|
|
||||||
},
|
|
||||||
numeric: {
|
|
||||||
name: CASH_IN_FULL_KEY + '-' + NUMERARY_KEY,
|
|
||||||
label: 'Or',
|
|
||||||
value: getValue([CASH_IN_FULL_KEY, NUMERARY_KEY])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cashOutFields = [
|
|
||||||
{
|
|
||||||
title: 'Cassette 1 (Top)',
|
|
||||||
name: CASH_OUT_EMPTY_KEY + '-' + CASSETTE_1_KEY,
|
|
||||||
label: 'Alert me at',
|
|
||||||
value: getValue([CASH_OUT_EMPTY_KEY, CASSETTE_1_KEY])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Cassette 2',
|
|
||||||
name: CASH_OUT_EMPTY_KEY + '-' + CASSETTE_2_KEY,
|
|
||||||
label: 'Alert me at',
|
|
||||||
value: getValue([CASH_OUT_EMPTY_KEY, CASSETTE_2_KEY])
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const { overrides } = setupValues
|
|
||||||
|
|
||||||
const initialValues = {
|
|
||||||
[MACHINE_KEY]: '',
|
|
||||||
[PERCENTAGE_KEY]: '',
|
|
||||||
[NUMERARY_KEY]: '',
|
|
||||||
[CASSETTE_1_KEY]: '',
|
|
||||||
[CASSETTE_2_KEY]: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
|
||||||
[MACHINE_KEY]: Yup.string().required(),
|
|
||||||
[PERCENTAGE_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(100)
|
|
||||||
.required(),
|
|
||||||
[NUMERARY_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(99999999)
|
|
||||||
.required(),
|
|
||||||
[CASSETTE_1_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(100)
|
|
||||||
.required(),
|
|
||||||
[CASSETTE_2_KEY]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(100)
|
|
||||||
.required()
|
|
||||||
})
|
|
||||||
|
|
||||||
const elements = [
|
|
||||||
{
|
|
||||||
name: MACHINE_KEY,
|
|
||||||
display: 'Machine',
|
|
||||||
size: 238,
|
|
||||||
textAlign: 'left',
|
|
||||||
view: R.path(['display']),
|
|
||||||
type: 'text',
|
|
||||||
input: Autocomplete,
|
|
||||||
inputProps: {
|
|
||||||
suggestions: getSuggestions(),
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[
|
|
||||||
{ display: 'Cash-in (Cassette Full)' },
|
|
||||||
{
|
|
||||||
name: PERCENTAGE_KEY,
|
|
||||||
display: 'Percentage',
|
|
||||||
size: 128,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: '%',
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: NUMERARY_KEY,
|
|
||||||
display: 'Amount',
|
|
||||||
size: 128,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: 'EUR', // TODO: Current currency?
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ display: 'Cash-out (Cassette Empty)' },
|
|
||||||
{
|
|
||||||
name: CASSETTE_1_KEY,
|
|
||||||
display: 'Cash-out 1',
|
|
||||||
size: 128,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: '%',
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: CASSETTE_2_KEY,
|
|
||||||
display: 'Cash-out 2',
|
|
||||||
size: 128,
|
|
||||||
textAlign: 'right',
|
|
||||||
view: it => it,
|
|
||||||
type: 'text',
|
|
||||||
input: TextInputFormik,
|
|
||||||
inputProps: {
|
|
||||||
suffix: '%',
|
|
||||||
className: classes.textInput,
|
|
||||||
onFocus: () => setError(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
{
|
|
||||||
name: 'edit',
|
|
||||||
size: 98
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const cashInEditing = editingState[CASH_IN_FULL_KEY]
|
|
||||||
const cashOutEditing = editingState[CASH_OUT_EMPTY_KEY]
|
|
||||||
const overrideOpsDisabled = isDisabled(editingState, ADD_OVERRIDE_FBA_KEY)
|
|
||||||
const addingOverride = editingState[ADD_OVERRIDE_FBA_KEY]
|
|
||||||
|
|
||||||
if (!machines) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={classnames(classes.defaults, classes.fbaDefaults)}>
|
|
||||||
<BigPercentageAndNumericInput
|
|
||||||
title="Cash-in (Full)"
|
|
||||||
fields={cashInFields}
|
|
||||||
editing={cashInEditing}
|
|
||||||
disabled={isDisabled(editingState, CASH_IN_FULL_KEY)}
|
|
||||||
setEditing={handleEdit(CASH_IN_FULL_KEY)}
|
|
||||||
handleSubmit={handleSubmit(CASH_IN_FULL_KEY)}
|
|
||||||
className={classes.cashInWrapper}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<MultiplePercentageInput
|
|
||||||
title="Cash-out (Empty)"
|
|
||||||
fields={cashOutFields}
|
|
||||||
editing={cashOutEditing}
|
|
||||||
disabled={isDisabled(editingState, CASH_OUT_EMPTY_KEY)}
|
|
||||||
setEditing={handleEdit(CASH_OUT_EMPTY_KEY)}
|
|
||||||
handleSubmit={handleSubmit(CASH_OUT_EMPTY_KEY)}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.overrides}>
|
|
||||||
<div className={classes.overridesTitle}>
|
|
||||||
<Info2>Overrides</Info2>
|
|
||||||
{!addingOverride && !overrideOpsDisabled && overrides.length > 0 && (
|
|
||||||
<Link
|
|
||||||
color="primary"
|
|
||||||
onClick={() => handleEdit(ADD_OVERRIDE_FBA_KEY)(true)}>
|
|
||||||
Add override
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!addingOverride && !overrideOpsDisabled && overrides.length === 0 && (
|
|
||||||
<AddButton onClick={() => handleEdit(ADD_OVERRIDE_FBA_KEY)(true)}>
|
|
||||||
Add overrides
|
|
||||||
</AddButton>
|
|
||||||
)}
|
|
||||||
{(addingOverride || overrides.length > 0) && (
|
|
||||||
<EditableTable
|
|
||||||
className={{ head: { cell: classes.doubleLevelHead } }}
|
|
||||||
addingRow={addingOverride}
|
|
||||||
disableAction={overrideOpsDisabled || addingOverride}
|
|
||||||
editing={R.map(
|
|
||||||
() => false,
|
|
||||||
R.range(0, setupValues[OVERRIDES_KEY].length)
|
|
||||||
)}
|
|
||||||
save={handleSubmitOverrides}
|
|
||||||
reset={() => handleResetForm}
|
|
||||||
action={it => handleEdit(it.machine.name)(true)}
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
// data={setupValues[OVERRIDES_KEY]}
|
|
||||||
data={[]}
|
|
||||||
elements={elements}
|
|
||||||
double
|
|
||||||
/>
|
|
||||||
// <Table>
|
|
||||||
// <TDoubleLevelHead>
|
|
||||||
// <Th size={sizes.machine}>Machine</Th>
|
|
||||||
// <ThDoubleLevel
|
|
||||||
// title="Cash-in (Cassette Full)"
|
|
||||||
// className={classes.doubleLevelHead}>
|
|
||||||
// <Th size={sizes.percentage} textAlign="right">
|
|
||||||
// Percentage
|
|
||||||
// </Th>
|
|
||||||
// <Th size={sizes.amount} textAlign="right">
|
|
||||||
// Amount
|
|
||||||
// </Th>
|
|
||||||
// </ThDoubleLevel>
|
|
||||||
// <ThDoubleLevel
|
|
||||||
// title="Cash-out (Cassette Empty)"
|
|
||||||
// className={classes.doubleLevelHead}>
|
|
||||||
// <Th size={sizes.cashOut1} textAlign="right">
|
|
||||||
// Cash-out 1
|
|
||||||
// </Th>
|
|
||||||
// <Th size={sizes.cashOut2} textAlign="right">
|
|
||||||
// Cash-out 2
|
|
||||||
// </Th>
|
|
||||||
// </ThDoubleLevel>
|
|
||||||
// <Th size={sizes.edit} textAlign="center">
|
|
||||||
// Edit
|
|
||||||
// </Th>
|
|
||||||
// </TDoubleLevelHead>
|
|
||||||
// <TBody>
|
|
||||||
// {addingOverride && (
|
|
||||||
// <OverridesRow
|
|
||||||
// handleSubmitOverrides={handleSubmitOverrides}
|
|
||||||
// handleEdit={handleEdit}
|
|
||||||
// sizes={sizes}
|
|
||||||
// editing={editingState[ADD_OVERRIDE_FBA_KEY]}
|
|
||||||
// fields={{
|
|
||||||
// [MACHINE_KEY]: { name: `new-${MACHINE_KEY}` },
|
|
||||||
// [PERCENTAGE_KEY]: {
|
|
||||||
// name: `new-${CASH_IN_FULL_KEY}-${PERCENTAGE_KEY}`
|
|
||||||
// },
|
|
||||||
// [NUMERARY_KEY]: {
|
|
||||||
// name: `new-${CASH_IN_FULL_KEY}-${NUMERARY_KEY}`
|
|
||||||
// },
|
|
||||||
// [CASSETTE_1_KEY]: {
|
|
||||||
// name: `new-${CASH_OUT_EMPTY_KEY}-${CASSETTE_1_KEY}`
|
|
||||||
// },
|
|
||||||
// [CASSETTE_2_KEY]: {
|
|
||||||
// name: `new-${CASH_OUT_EMPTY_KEY}-${CASSETTE_2_KEY}`
|
|
||||||
// }
|
|
||||||
// }}
|
|
||||||
// disabled={isDisabled(ADD_OVERRIDE_FBA_KEY)}
|
|
||||||
// getSuggestions={getSuggestions}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// )}
|
|
||||||
// {overrides.map((override, idx) => {
|
|
||||||
// const machine = override[MACHINE_KEY]
|
|
||||||
|
|
||||||
// const fields = {
|
|
||||||
// [PERCENTAGE_KEY]: {
|
|
||||||
// name: `${machine.name}-${CASH_IN_FULL_KEY}-${PERCENTAGE_KEY}`,
|
|
||||||
// value: override[CASH_IN_FULL_KEY][PERCENTAGE_KEY]
|
|
||||||
// },
|
|
||||||
// [NUMERARY_KEY]: {
|
|
||||||
// name: `${machine.name}-${CASH_IN_FULL_KEY}-${NUMERARY_KEY}`,
|
|
||||||
// value: override[CASH_IN_FULL_KEY][NUMERARY_KEY]
|
|
||||||
// },
|
|
||||||
// [CASSETTE_1_KEY]: {
|
|
||||||
// name: `${machine.name}-${CASH_OUT_EMPTY_KEY}-${CASSETTE_1_KEY}`,
|
|
||||||
// value: override[CASH_OUT_EMPTY_KEY][CASSETTE_1_KEY]
|
|
||||||
// },
|
|
||||||
// [CASSETTE_2_KEY]: {
|
|
||||||
// name: `${machine.name}-${CASH_OUT_EMPTY_KEY}-${CASSETTE_2_KEY}`,
|
|
||||||
// value: override[CASH_OUT_EMPTY_KEY][CASSETTE_2_KEY]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const editing = editingState[machine.name]
|
|
||||||
// const disabled = isDisabled(editingState, machine.name)
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <OverridesRow
|
|
||||||
// key={idx}
|
|
||||||
// machine={machine}
|
|
||||||
// handleSubmitOverrides={handleSubmitOverrides}
|
|
||||||
// handleEdit={handleEdit}
|
|
||||||
// sizes={sizes}
|
|
||||||
// editing={editing}
|
|
||||||
// fields={fields}
|
|
||||||
// disabled={disabled}
|
|
||||||
// setError={setError}
|
|
||||||
// />
|
|
||||||
// )
|
|
||||||
// })}
|
|
||||||
// </TBody>
|
|
||||||
// </Table>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FiatBalanceAlerts
|
|
||||||
|
|
@ -1,340 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import * as Yup from 'yup'
|
|
||||||
import { Form, Formik, Field as FormikField } from 'formik'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
|
|
||||||
import {
|
|
||||||
H4,
|
|
||||||
Label1,
|
|
||||||
Info1,
|
|
||||||
TL2,
|
|
||||||
Info2,
|
|
||||||
Label2
|
|
||||||
} from 'src/components/typography'
|
|
||||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
|
||||||
import { ReactComponent as DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.svg'
|
|
||||||
import { Link } from 'src/components/buttons'
|
|
||||||
import TextInputFormik from 'src/components/inputs/formik/TextInput'
|
|
||||||
|
|
||||||
import {
|
|
||||||
localStyles,
|
|
||||||
inputSectionStyles,
|
|
||||||
percentageAndNumericInputStyles,
|
|
||||||
multiplePercentageInputStyles,
|
|
||||||
fieldStyles
|
|
||||||
} from './Notifications.styles'
|
|
||||||
|
|
||||||
const fieldUseStyles = makeStyles(R.mergeAll([fieldStyles, localStyles]))
|
|
||||||
|
|
||||||
const Field = ({
|
|
||||||
editing,
|
|
||||||
field,
|
|
||||||
displayValue,
|
|
||||||
decoration,
|
|
||||||
className,
|
|
||||||
setError,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
const classes = fieldUseStyles()
|
|
||||||
|
|
||||||
const classNames = {
|
|
||||||
[className]: true,
|
|
||||||
[classes.field]: true,
|
|
||||||
[classes.notEditing]: !editing,
|
|
||||||
[classes.percentageInput]: decoration === '%'
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classnames(classNames)}>
|
|
||||||
{field.label && <Label1 className={classes.label}>{field.label}</Label1>}
|
|
||||||
<div className={classes.displayValue}>
|
|
||||||
{!editing && props.large && (
|
|
||||||
<>
|
|
||||||
<Info1>{displayValue(field.value)}</Info1>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!editing && !props.large && (
|
|
||||||
<>
|
|
||||||
<Info2>{displayValue(field.value)}</Info2>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{editing && (
|
|
||||||
<FormikField
|
|
||||||
id={field.name}
|
|
||||||
name={field.name}
|
|
||||||
component={TextInputFormik}
|
|
||||||
placeholder={field.placeholder}
|
|
||||||
type="text"
|
|
||||||
onFocus={() => setError(null)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{props.large && (
|
|
||||||
<>
|
|
||||||
<TL2>{decoration}</TL2>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!props.large && (
|
|
||||||
<>
|
|
||||||
<Label2>{decoration}</Label2>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const useStyles = makeStyles(inputSectionStyles)
|
|
||||||
|
|
||||||
const Header = ({ title, editing, disabled, setEditing }) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.header}>
|
|
||||||
<H4>{title}</H4>
|
|
||||||
{!editing && !disabled && (
|
|
||||||
<button onClick={() => setEditing(true)} className={classes.editButton}>
|
|
||||||
<EditIcon />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{disabled && (
|
|
||||||
<div className={classes.disabledButton}>
|
|
||||||
<DisabledEditIcon />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{editing && (
|
|
||||||
<div className={classes.editingButtons}>
|
|
||||||
<Link color="primary" type="submit">
|
|
||||||
Save
|
|
||||||
</Link>
|
|
||||||
<Link color="secondary" type="reset">
|
|
||||||
Cancel
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const BigNumericInput = ({
|
|
||||||
title,
|
|
||||||
field,
|
|
||||||
editing,
|
|
||||||
disabled,
|
|
||||||
setEditing,
|
|
||||||
handleSubmit,
|
|
||||||
setError,
|
|
||||||
className
|
|
||||||
}) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const { name, value } = field
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<Formik
|
|
||||||
initialValues={{ [name]: value }}
|
|
||||||
validationSchema={Yup.object().shape({
|
|
||||||
[name]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(99999)
|
|
||||||
.required()
|
|
||||||
})}
|
|
||||||
onSubmit={values => {
|
|
||||||
handleSubmit(values)
|
|
||||||
}}
|
|
||||||
onReset={(values, bag) => {
|
|
||||||
setEditing(false)
|
|
||||||
setError(null)
|
|
||||||
}}>
|
|
||||||
<Form>
|
|
||||||
<Header
|
|
||||||
title={title}
|
|
||||||
editing={editing}
|
|
||||||
disabled={disabled}
|
|
||||||
setEditing={() => setEditing(true)}
|
|
||||||
/>
|
|
||||||
<div className={classes.body}>
|
|
||||||
<Field
|
|
||||||
editing={editing}
|
|
||||||
field={field}
|
|
||||||
displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
decoration="EUR"
|
|
||||||
large
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const percentageAndNumericInputUseStyles = makeStyles(
|
|
||||||
R.merge(inputSectionStyles, percentageAndNumericInputStyles)
|
|
||||||
)
|
|
||||||
|
|
||||||
const BigPercentageAndNumericInput = ({
|
|
||||||
title,
|
|
||||||
fields,
|
|
||||||
editing,
|
|
||||||
disabled,
|
|
||||||
setEditing,
|
|
||||||
handleSubmit,
|
|
||||||
setError,
|
|
||||||
className
|
|
||||||
}) => {
|
|
||||||
const classes = percentageAndNumericInputUseStyles()
|
|
||||||
|
|
||||||
const { percentage, numeric } = fields
|
|
||||||
const { name: percentageName, value: percentageValue } = percentage
|
|
||||||
const { name: numericName, value: numericValue } = numeric
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<Formik
|
|
||||||
initialValues={{
|
|
||||||
[percentageName]: percentageValue,
|
|
||||||
[numericName]: numericValue
|
|
||||||
}}
|
|
||||||
validationSchema={Yup.object().shape({
|
|
||||||
[percentageName]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(100)
|
|
||||||
.required(),
|
|
||||||
[numericName]: Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(99999)
|
|
||||||
.required()
|
|
||||||
})}
|
|
||||||
onSubmit={values => {
|
|
||||||
handleSubmit(values)
|
|
||||||
}}
|
|
||||||
onReset={(values, bag) => {
|
|
||||||
setEditing(false)
|
|
||||||
setError(null)
|
|
||||||
}}>
|
|
||||||
<Form>
|
|
||||||
<Header
|
|
||||||
title={title}
|
|
||||||
editing={editing}
|
|
||||||
disabled={disabled}
|
|
||||||
setEditing={() => setEditing(true)}
|
|
||||||
/>
|
|
||||||
<div className={classes.body}>
|
|
||||||
<div className={classes.percentageDisplay}>
|
|
||||||
<div style={{ height: `${percentageValue}%` }}></div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.inputColumn}>
|
|
||||||
<Field
|
|
||||||
editing={editing}
|
|
||||||
field={percentage}
|
|
||||||
displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
decoration="%"
|
|
||||||
large
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
<Field
|
|
||||||
editing={editing}
|
|
||||||
field={numeric}
|
|
||||||
displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
decoration="EUR"
|
|
||||||
large
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiplePercentageInputUseStyles = makeStyles(
|
|
||||||
R.merge(inputSectionStyles, multiplePercentageInputStyles)
|
|
||||||
)
|
|
||||||
|
|
||||||
const MultiplePercentageInput = ({
|
|
||||||
title,
|
|
||||||
fields,
|
|
||||||
editing,
|
|
||||||
disabled,
|
|
||||||
setEditing,
|
|
||||||
handleSubmit,
|
|
||||||
setError,
|
|
||||||
className
|
|
||||||
}) => {
|
|
||||||
const classes = multiplePercentageInputUseStyles()
|
|
||||||
|
|
||||||
const initialValues = R.fromPairs(R.map(f => [f.name, f.value], fields))
|
|
||||||
const validationSchemaShape = R.fromPairs(
|
|
||||||
R.map(
|
|
||||||
f => [
|
|
||||||
f.name,
|
|
||||||
Yup.number()
|
|
||||||
.integer()
|
|
||||||
.min(0)
|
|
||||||
.max(100)
|
|
||||||
.required()
|
|
||||||
],
|
|
||||||
fields
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className}>
|
|
||||||
<Formik
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={Yup.object().shape(validationSchemaShape)}
|
|
||||||
onSubmit={values => {
|
|
||||||
handleSubmit(values)
|
|
||||||
}}
|
|
||||||
onReset={(values, bag) => {
|
|
||||||
setEditing(false)
|
|
||||||
setError(null)
|
|
||||||
}}>
|
|
||||||
<Form>
|
|
||||||
<Header
|
|
||||||
title={title}
|
|
||||||
editing={editing}
|
|
||||||
disabled={disabled}
|
|
||||||
setEditing={() => setEditing(true)}
|
|
||||||
/>
|
|
||||||
<div className={classes.body}>
|
|
||||||
{fields.map((field, idx) => (
|
|
||||||
<div key={idx}>
|
|
||||||
<div className={classes.percentageDisplay}>
|
|
||||||
<div style={{ height: `${field.value}%` }}></div>
|
|
||||||
</div>
|
|
||||||
<div className={classes.inputColumn}>
|
|
||||||
<TL2 className={classes.title}>{field.title}</TL2>
|
|
||||||
<Field
|
|
||||||
editing={editing}
|
|
||||||
field={field}
|
|
||||||
displayValue={x => (x === '' ? '-' : x)}
|
|
||||||
decoration="%"
|
|
||||||
className={classes.percentageInput}
|
|
||||||
large
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Field,
|
|
||||||
BigNumericInput,
|
|
||||||
BigPercentageAndNumericInput,
|
|
||||||
MultiplePercentageInput
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +1,32 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
|
import { gql } from 'apollo-boost'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { TL1 } from 'src/components/typography'
|
import TitleSection from 'src/components/layout/TitleSection'
|
||||||
import Title from 'src/components/Title'
|
import { fromServer, toServer } from 'src/utils/config'
|
||||||
import ErrorMessage from 'src/components/ErrorMessage'
|
|
||||||
import commonStyles from 'src/pages/common.styles'
|
|
||||||
|
|
||||||
import { localStyles } from './Notifications.styles'
|
import Section from '../../components/layout/Section'
|
||||||
import Setup from './Setup'
|
|
||||||
import TransactionAlerts from './TransactionAlerts'
|
|
||||||
import {
|
|
||||||
SETUP_KEY,
|
|
||||||
TRANSACTION_ALERTS_KEY,
|
|
||||||
HIGH_VALUE_TRANSACTION_KEY,
|
|
||||||
CASH_IN_FULL_KEY,
|
|
||||||
FIAT_BALANCE_ALERTS_KEY,
|
|
||||||
CASH_OUT_EMPTY_KEY,
|
|
||||||
CASSETTE_1_KEY,
|
|
||||||
CASSETTE_2_KEY,
|
|
||||||
OVERRIDES_KEY,
|
|
||||||
PERCENTAGE_KEY,
|
|
||||||
NUMERARY_KEY,
|
|
||||||
CRYPTO_BALANCE_ALERTS_KEY,
|
|
||||||
LOW_BALANCE_KEY,
|
|
||||||
HIGH_BALANCE_KEY,
|
|
||||||
ADD_OVERRIDE_FBA_KEY,
|
|
||||||
ADD_OVERRIDE_CBA_KEY,
|
|
||||||
EMAIL_KEY,
|
|
||||||
BALANCE_KEY,
|
|
||||||
TRANSACTIONS_KEY,
|
|
||||||
COMPLIANCE_KEY,
|
|
||||||
SECURITY_KEY,
|
|
||||||
ERRORS_KEY,
|
|
||||||
ACTIVE_KEY,
|
|
||||||
SMS_KEY
|
|
||||||
} from './aux.js'
|
|
||||||
import FiatBalanceAlerts from './FiatBalanceAlerts'
|
|
||||||
import CryptoBalanceAlerts from './CryptoBalanceAlerts'
|
|
||||||
|
|
||||||
const fiatBalanceAlertsInitialValues = {
|
import NotificationsCtx from './NotificationsContext'
|
||||||
[CASH_IN_FULL_KEY]: {
|
import CryptoBalanceAlerts from './sections/CryptoBalanceAlerts'
|
||||||
[PERCENTAGE_KEY]: '',
|
import CryptoBalanceOverrides from './sections/CryptoBalanceOverrides'
|
||||||
[NUMERARY_KEY]: ''
|
import FiatBalanceAlerts from './sections/FiatBalanceAlerts'
|
||||||
},
|
import FiatBalanceOverrides from './sections/FiatBalanceOverrides'
|
||||||
[CASH_OUT_EMPTY_KEY]: {
|
import Setup from './sections/Setup'
|
||||||
[CASSETTE_1_KEY]: '',
|
import TransactionAlerts from './sections/TransactionAlerts'
|
||||||
[CASSETTE_2_KEY]: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialValues = {
|
|
||||||
[SETUP_KEY]: {
|
|
||||||
[EMAIL_KEY]: {
|
|
||||||
[BALANCE_KEY]: false,
|
|
||||||
[TRANSACTIONS_KEY]: false,
|
|
||||||
[COMPLIANCE_KEY]: false,
|
|
||||||
[SECURITY_KEY]: false,
|
|
||||||
[ERRORS_KEY]: false,
|
|
||||||
[ACTIVE_KEY]: false
|
|
||||||
},
|
|
||||||
[SMS_KEY]: {
|
|
||||||
[BALANCE_KEY]: false,
|
|
||||||
[TRANSACTIONS_KEY]: false,
|
|
||||||
[COMPLIANCE_KEY]: false,
|
|
||||||
[SECURITY_KEY]: false,
|
|
||||||
[ERRORS_KEY]: false,
|
|
||||||
[ACTIVE_KEY]: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[TRANSACTION_ALERTS_KEY]: {
|
|
||||||
[HIGH_VALUE_TRANSACTION_KEY]: ''
|
|
||||||
},
|
|
||||||
[FIAT_BALANCE_ALERTS_KEY]: {
|
|
||||||
...fiatBalanceAlertsInitialValues,
|
|
||||||
[OVERRIDES_KEY]: []
|
|
||||||
},
|
|
||||||
[CRYPTO_BALANCE_ALERTS_KEY]: {
|
|
||||||
[LOW_BALANCE_KEY]: '',
|
|
||||||
[HIGH_BALANCE_KEY]: '',
|
|
||||||
[OVERRIDES_KEY]: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialEditingState = {
|
|
||||||
[HIGH_VALUE_TRANSACTION_KEY]: false,
|
|
||||||
[CASH_IN_FULL_KEY]: false,
|
|
||||||
[CASH_OUT_EMPTY_KEY]: false,
|
|
||||||
[LOW_BALANCE_KEY]: false,
|
|
||||||
[HIGH_BALANCE_KEY]: false,
|
|
||||||
[ADD_OVERRIDE_FBA_KEY]: false,
|
|
||||||
[ADD_OVERRIDE_CBA_KEY]: false
|
|
||||||
}
|
|
||||||
|
|
||||||
const GET_INFO = gql`
|
const GET_INFO = gql`
|
||||||
{
|
query getData {
|
||||||
config
|
config
|
||||||
|
machines {
|
||||||
|
name
|
||||||
|
deviceId
|
||||||
|
}
|
||||||
|
cryptoCurrencies {
|
||||||
|
code
|
||||||
|
display
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -107,125 +36,81 @@ const SAVE_CONFIG = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const styles = R.merge(commonStyles, localStyles)
|
const Notifications = ({ name: SCREEN_KEY }) => {
|
||||||
|
const [section, setSection] = useState(null)
|
||||||
const useStyles = makeStyles(styles)
|
|
||||||
|
|
||||||
const SectionHeader = ({ error, children }) => {
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes.sectionHeader}>
|
|
||||||
<TL1 className={classes.sectionTitle}>{children}</TL1>
|
|
||||||
{error && <ErrorMessage>Failed to save changes</ErrorMessage>}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Notifications = () => {
|
|
||||||
const [state, setState] = useState(null)
|
|
||||||
const [editingState, setEditingState] = useState(initialEditingState)
|
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [tryingToSave, setTryingToSave] = useState(null)
|
const [editingKey, setEditingKey] = useState(null)
|
||||||
|
|
||||||
|
const { data } = useQuery(GET_INFO)
|
||||||
|
|
||||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||||
onCompleted: data => {
|
refetchQueries: ['getData'],
|
||||||
const { notifications } = data.saveConfig
|
onCompleted: () => setEditingKey(null),
|
||||||
setState(notifications)
|
onError: error => setError({ error })
|
||||||
setEditingState(R.map(x => false, editingState))
|
})
|
||||||
setTryingToSave(null)
|
|
||||||
|
const config = data?.config && fromServer(SCREEN_KEY)(data.config)
|
||||||
|
const machines = data?.machines
|
||||||
|
const cryptoCurrencies = data?.cryptoCurrencies
|
||||||
|
|
||||||
|
// TODO check path when locales is finished
|
||||||
|
const currency = R.path(['locales_currency'])(data?.config ?? {})
|
||||||
|
|
||||||
|
const save = (section, rawConfig) => {
|
||||||
|
const config = toServer(SCREEN_KEY)(rawConfig)
|
||||||
|
setSection(section)
|
||||||
|
setError(null)
|
||||||
|
return saveConfig({ variables: { config } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const setEditing = (key, state) => {
|
||||||
|
if (!state) {
|
||||||
setError(null)
|
setError(null)
|
||||||
},
|
|
||||||
onError: e => {
|
|
||||||
setError({ section: tryingToSave, error: e })
|
|
||||||
}
|
}
|
||||||
})
|
setEditingKey(state ? key : null)
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
useQuery(GET_INFO, {
|
|
||||||
onCompleted: data => {
|
|
||||||
const { notifications } = data.config
|
|
||||||
if (notifications) {
|
|
||||||
const { [OVERRIDES_KEY]: machines } = notifications[
|
|
||||||
FIAT_BALANCE_ALERTS_KEY
|
|
||||||
]
|
|
||||||
const editingFiatBalanceAlertsOverrides = R.fromPairs(
|
|
||||||
machines.map(machine => [machine.name, false])
|
|
||||||
)
|
|
||||||
setEditingState({
|
|
||||||
...editingState,
|
|
||||||
...editingFiatBalanceAlertsOverrides
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setState(notifications ?? initialValues)
|
|
||||||
},
|
|
||||||
fetchPolicy: 'network-only'
|
|
||||||
})
|
|
||||||
|
|
||||||
const save = it => {
|
|
||||||
return saveConfig({ variables: { config: { notifications: it } } })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleEditingClick = (key, state) => {
|
const isEditing = key => editingKey === key
|
||||||
setEditingState(R.merge(editingState, { [key]: state }))
|
const isDisabled = key => editingKey && editingKey !== key
|
||||||
|
|
||||||
|
const contextValue = {
|
||||||
|
save,
|
||||||
|
error,
|
||||||
|
editingKey,
|
||||||
|
data: config,
|
||||||
|
currency,
|
||||||
|
isEditing,
|
||||||
|
isDisabled,
|
||||||
|
setEditing,
|
||||||
|
setSection,
|
||||||
|
machines,
|
||||||
|
cryptoCurrencies
|
||||||
}
|
}
|
||||||
|
|
||||||
const curriedSave = R.curry((key, values) => {
|
|
||||||
setTryingToSave(key)
|
|
||||||
save(R.mergeDeepRight(state)({ [key]: values }))
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!state) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<NotificationsCtx.Provider value={contextValue}>
|
||||||
<div className={classes.titleWrapper}>
|
<TitleSection title="Notifications" />
|
||||||
<div className={classes.titleAndButtonsContainer}>
|
|
||||||
<Title>Notifications</Title>
|
<Section title="Setup" error={error && !section}>
|
||||||
</div>
|
<Setup />
|
||||||
</div>
|
</Section>
|
||||||
<div className={classes.section}>
|
|
||||||
<SectionHeader error={error?.section === SETUP_KEY}>
|
<Section title="Transaction alerts" error={error && section === 'tx'}>
|
||||||
Setup
|
<TransactionAlerts section="tx" />
|
||||||
</SectionHeader>
|
</Section>
|
||||||
<Setup values={state.setup} save={curriedSave(SETUP_KEY)} />
|
|
||||||
</div>
|
<Section title="Fiat balance alerts" error={error && section === 'fiat'}>
|
||||||
<div className={classes.section}>
|
<FiatBalanceAlerts section="fiat" />
|
||||||
<SectionHeader error={error?.section === TRANSACTION_ALERTS_KEY}>
|
<FiatBalanceOverrides section="fiat" />
|
||||||
Transaction alerts
|
</Section>
|
||||||
</SectionHeader>
|
|
||||||
<TransactionAlerts
|
<Section
|
||||||
value={state[TRANSACTION_ALERTS_KEY]}
|
title="Crypto balance alerts"
|
||||||
editingState={editingState}
|
error={error && section === 'crypto'}>
|
||||||
handleEditingClick={handleEditingClick}
|
<CryptoBalanceAlerts section="crypto" />
|
||||||
save={curriedSave(TRANSACTION_ALERTS_KEY)}
|
<CryptoBalanceOverrides section="crypto" />
|
||||||
setError={setError}
|
</Section>
|
||||||
/>
|
</NotificationsCtx.Provider>
|
||||||
</div>
|
|
||||||
<div className={classes.section}>
|
|
||||||
<SectionHeader error={error?.section === FIAT_BALANCE_ALERTS_KEY}>
|
|
||||||
Fiat balance alerts
|
|
||||||
</SectionHeader>
|
|
||||||
<FiatBalanceAlerts
|
|
||||||
values={state[FIAT_BALANCE_ALERTS_KEY]}
|
|
||||||
editingState={editingState}
|
|
||||||
handleEditingClick={handleEditingClick}
|
|
||||||
save={curriedSave(FIAT_BALANCE_ALERTS_KEY)}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={classes.section}>
|
|
||||||
<SectionHeader error={error?.section === CRYPTO_BALANCE_ALERTS_KEY}>
|
|
||||||
Crypto balance alerts
|
|
||||||
</SectionHeader>
|
|
||||||
<CryptoBalanceAlerts
|
|
||||||
values={state[CRYPTO_BALANCE_ALERTS_KEY]}
|
|
||||||
editingState={editingState}
|
|
||||||
handleEditingClick={handleEditingClick}
|
|
||||||
save={curriedSave(CRYPTO_BALANCE_ALERTS_KEY)}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,252 +1,20 @@
|
||||||
import { offColor, primaryColor } from 'src/styling/variables'
|
export default {
|
||||||
import theme from 'src/styling/theme'
|
cryptoBalanceAlerts: {
|
||||||
|
|
||||||
const localStyles = {
|
|
||||||
section: {
|
|
||||||
marginBottom: 72,
|
|
||||||
'&:last-child': {
|
|
||||||
marginBottom: 150
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sectionHeader: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
'& > :first-child': {
|
|
||||||
marginRight: 20
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
color: offColor,
|
|
||||||
margin: [[16, 0, 16, 0]]
|
|
||||||
},
|
|
||||||
button: {
|
|
||||||
border: 'none',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
cursor: 'pointer',
|
|
||||||
height: '100%'
|
|
||||||
},
|
|
||||||
defaults: {
|
|
||||||
display: 'flex',
|
|
||||||
'& > div': {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center'
|
|
||||||
},
|
|
||||||
'& > div:first-child': {
|
|
||||||
borderRight: [['solid', 1, primaryColor]]
|
|
||||||
},
|
|
||||||
'& > div:not(:first-child)': {
|
|
||||||
marginLeft: 56
|
|
||||||
}
|
|
||||||
},
|
|
||||||
overrides: {
|
|
||||||
display: 'inline-block'
|
|
||||||
},
|
|
||||||
overridesTitle: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'baseline',
|
|
||||||
'& > :first-child': {
|
|
||||||
color: offColor,
|
|
||||||
margin: [[0, 0, 24, 0]]
|
|
||||||
},
|
|
||||||
'& > button': {
|
|
||||||
height: '100%'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
displayValue: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'baseline',
|
|
||||||
'& > p:first-child': {
|
|
||||||
margin: [[0, 4, 5, 0]]
|
|
||||||
},
|
|
||||||
'&> p:last-child': {
|
|
||||||
margin: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
edit: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
'& > :first-child': {
|
|
||||||
marginRight: 16
|
|
||||||
}
|
|
||||||
},
|
|
||||||
eRowField: {
|
|
||||||
display: 'inline-block',
|
|
||||||
height: '100%'
|
|
||||||
},
|
|
||||||
textInput: {
|
|
||||||
'& .MuiInputBase-input': {
|
|
||||||
width: 80
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputSectionStyles = {
|
|
||||||
header: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: 24,
|
|
||||||
height: 26,
|
|
||||||
'& > :first-child': {
|
|
||||||
flexShrink: 2,
|
|
||||||
margin: 0,
|
|
||||||
overflow: 'hidden',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
textOverflow: 'ellipsis'
|
|
||||||
},
|
|
||||||
'& button': {
|
|
||||||
border: 'none',
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
cursor: 'pointer'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
editButton: {
|
|
||||||
marginLeft: 16
|
|
||||||
},
|
|
||||||
disabledButton: {
|
|
||||||
padding: [[0, theme.spacing(1)]],
|
|
||||||
lineHeight: 'normal',
|
|
||||||
marginLeft: 16
|
|
||||||
},
|
|
||||||
editingButtons: {
|
|
||||||
display: 'flex',
|
|
||||||
marginLeft: 16,
|
|
||||||
'& > :not(:last-child)': {
|
|
||||||
marginRight: 20
|
|
||||||
}
|
|
||||||
},
|
|
||||||
percentageDisplay: {
|
|
||||||
position: 'relative',
|
|
||||||
width: 76,
|
|
||||||
height: 118,
|
|
||||||
border: [['solid', 4, primaryColor]],
|
|
||||||
marginRight: 12,
|
|
||||||
'& > div': {
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
backgroundColor: primaryColor,
|
|
||||||
transition: [['height', '0.5s']],
|
|
||||||
transitionTimingFunction: 'ease-out'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
inputColumn: {
|
|
||||||
'& > div:not(:last-child)': {
|
|
||||||
marginBottom: 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fiatBalanceAlertsStyles = {
|
|
||||||
cashInWrapper: {
|
|
||||||
width: 254
|
|
||||||
},
|
|
||||||
doubleLevelHead: {
|
|
||||||
'& > div > div': {
|
|
||||||
marginRight: 72
|
|
||||||
}
|
|
||||||
},
|
|
||||||
doubleLevelRow: {
|
|
||||||
'& > div': {
|
|
||||||
marginRight: 72
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fbaDefaults: {
|
|
||||||
'& > div': {
|
|
||||||
height: 185
|
|
||||||
},
|
|
||||||
marginBottom: 69
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cryptoBalanceAlertsStyles = {
|
|
||||||
lowBalance: {
|
|
||||||
width: 254,
|
|
||||||
'& form': {
|
|
||||||
width: 217
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cbaDefaults: {
|
|
||||||
'& > div': {
|
|
||||||
height: 135
|
|
||||||
},
|
|
||||||
marginBottom: 36
|
|
||||||
},
|
|
||||||
overridesTable: {
|
|
||||||
width: 648
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const percentageAndNumericInputStyles = {
|
|
||||||
body: {
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
marginBottom: 36,
|
||||||
|
height: 135,
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
cryptoBalanceAlertsForm: {
|
||||||
|
width: 222,
|
||||||
|
marginRight: 32
|
||||||
|
},
|
||||||
|
cryptoBalanceAlertsSecondForm: {
|
||||||
|
marginLeft: 50
|
||||||
|
},
|
||||||
|
vertSeparator: {
|
||||||
|
width: 1,
|
||||||
|
height: '100%',
|
||||||
|
borderRight: [[1, 'solid', 'black']]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const multiplePercentageInputStyles = {
|
|
||||||
body: {
|
|
||||||
display: 'flex',
|
|
||||||
'& > div': {
|
|
||||||
display: 'flex'
|
|
||||||
},
|
|
||||||
'& > div:not(:last-child)': {
|
|
||||||
marginRight: 43
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
margin: [[2, 0, 12, 0]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldStyles = {
|
|
||||||
field: {
|
|
||||||
position: 'relative',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
height: 53,
|
|
||||||
padding: 0,
|
|
||||||
'& > div': {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'baseline',
|
|
||||||
'& > p:first-child': {
|
|
||||||
margin: [[0, 4, 5, 0]]
|
|
||||||
},
|
|
||||||
'&> p:last-child': {
|
|
||||||
margin: [[0, 0, 0, 3]]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'& .MuiInputBase-input': {
|
|
||||||
width: 80
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
margin: 0
|
|
||||||
},
|
|
||||||
notEditing: {
|
|
||||||
'& > div': {
|
|
||||||
margin: [[5, 0, 0, 0]],
|
|
||||||
'& > p:first-child': {
|
|
||||||
height: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
percentageInput: {
|
|
||||||
'& > div': {
|
|
||||||
'& .MuiInputBase-input': {
|
|
||||||
width: 30
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
localStyles,
|
|
||||||
inputSectionStyles,
|
|
||||||
fiatBalanceAlertsStyles,
|
|
||||||
cryptoBalanceAlertsStyles,
|
|
||||||
percentageAndNumericInputStyles,
|
|
||||||
multiplePercentageInputStyles,
|
|
||||||
fieldStyles
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default React.createContext()
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
|
|
||||||
import { Switch } from 'src/components/inputs'
|
|
||||||
import {
|
|
||||||
Table as FakeTable,
|
|
||||||
THead,
|
|
||||||
TBody,
|
|
||||||
Tr,
|
|
||||||
Td,
|
|
||||||
Th
|
|
||||||
} from 'src/components/fake-table/Table'
|
|
||||||
|
|
||||||
import {
|
|
||||||
BALANCE_KEY,
|
|
||||||
TRANSACTIONS_KEY,
|
|
||||||
COMPLIANCE_KEY,
|
|
||||||
SECURITY_KEY,
|
|
||||||
ERRORS_KEY,
|
|
||||||
ACTIVE_KEY,
|
|
||||||
CHANNEL_KEY,
|
|
||||||
EMAIL_KEY,
|
|
||||||
SMS_KEY
|
|
||||||
} from './aux'
|
|
||||||
|
|
||||||
const elements = [
|
|
||||||
{
|
|
||||||
header: 'Channel',
|
|
||||||
name: CHANNEL_KEY,
|
|
||||||
size: 129,
|
|
||||||
textAlign: 'left'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Balance',
|
|
||||||
name: BALANCE_KEY,
|
|
||||||
size: 152,
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Transactions',
|
|
||||||
name: TRANSACTIONS_KEY,
|
|
||||||
size: 184,
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Compliance',
|
|
||||||
name: COMPLIANCE_KEY,
|
|
||||||
size: 178,
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Security',
|
|
||||||
name: SECURITY_KEY,
|
|
||||||
size: 152,
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Errors',
|
|
||||||
name: ERRORS_KEY,
|
|
||||||
size: 142,
|
|
||||||
textAlign: 'center'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
header: 'Active',
|
|
||||||
name: ACTIVE_KEY,
|
|
||||||
size: 263,
|
|
||||||
textAlign: 'center'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const Row = ({ channel, columns, values, save }) => {
|
|
||||||
const { active } = values
|
|
||||||
|
|
||||||
const findField = name => R.find(R.propEq('name', name))(columns)
|
|
||||||
const findSize = name => findField(name).size
|
|
||||||
const findAlign = name => findField(name).textAlign
|
|
||||||
|
|
||||||
const Cell = ({ name, disabled }) => {
|
|
||||||
const handleChange = name => event => {
|
|
||||||
save(R.mergeDeepRight(values, { [name]: event.target.checked }))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Td size={findSize(name)} textAlign={findAlign(name)}>
|
|
||||||
<Switch
|
|
||||||
disabled={disabled}
|
|
||||||
checked={values[name]}
|
|
||||||
onChange={handleChange(name)}
|
|
||||||
value={name}
|
|
||||||
/>
|
|
||||||
</Td>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tr>
|
|
||||||
<Td size={findSize(CHANNEL_KEY)} textAlign={findAlign(CHANNEL_KEY)}>
|
|
||||||
{channel}
|
|
||||||
</Td>
|
|
||||||
<Cell name={BALANCE_KEY} disabled={!active} />
|
|
||||||
<Cell name={TRANSACTIONS_KEY} disabled={!active} />
|
|
||||||
<Cell name={COMPLIANCE_KEY} disabled={!active} />
|
|
||||||
<Cell name={SECURITY_KEY} disabled={!active} />
|
|
||||||
<Cell name={ERRORS_KEY} disabled={!active} />
|
|
||||||
<Cell name={ACTIVE_KEY} />
|
|
||||||
</Tr>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Setup = ({ values: setupValues, save }) => {
|
|
||||||
const saveSetup = R.curry((key, values) =>
|
|
||||||
save(R.mergeDeepRight(setupValues, { [key]: values }))
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<FakeTable>
|
|
||||||
<THead>
|
|
||||||
{elements.map(({ size, className, textAlign, header }, idx) => (
|
|
||||||
<Th
|
|
||||||
key={idx}
|
|
||||||
size={size}
|
|
||||||
className={className}
|
|
||||||
textAlign={textAlign}>
|
|
||||||
{header}
|
|
||||||
</Th>
|
|
||||||
))}
|
|
||||||
</THead>
|
|
||||||
<TBody>
|
|
||||||
<Row
|
|
||||||
channel="Email"
|
|
||||||
columns={elements}
|
|
||||||
values={setupValues[EMAIL_KEY]}
|
|
||||||
save={saveSetup(EMAIL_KEY)}
|
|
||||||
/>
|
|
||||||
<Row
|
|
||||||
channel="SMS"
|
|
||||||
columns={elements}
|
|
||||||
values={setupValues[SMS_KEY]}
|
|
||||||
save={saveSetup(SMS_KEY)}
|
|
||||||
/>
|
|
||||||
</TBody>
|
|
||||||
</FakeTable>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Setup
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
|
|
||||||
import { HIGH_VALUE_TRANSACTION_KEY, isDisabled } from './aux.js'
|
|
||||||
import { BigNumericInput } from './Inputs'
|
|
||||||
|
|
||||||
const TransactionAlerts = ({
|
|
||||||
value: setupValue,
|
|
||||||
save,
|
|
||||||
editingState,
|
|
||||||
handleEditingClick,
|
|
||||||
setError
|
|
||||||
}) => {
|
|
||||||
const editing = editingState[HIGH_VALUE_TRANSACTION_KEY]
|
|
||||||
|
|
||||||
const handleEdit = R.curry(handleEditingClick)
|
|
||||||
|
|
||||||
const handleSubmit = it => save(it)
|
|
||||||
|
|
||||||
const field = {
|
|
||||||
name: HIGH_VALUE_TRANSACTION_KEY,
|
|
||||||
label: 'Alert me over',
|
|
||||||
value: setupValue[HIGH_VALUE_TRANSACTION_KEY]
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<BigNumericInput
|
|
||||||
title="High value transaction"
|
|
||||||
field={field}
|
|
||||||
editing={editing}
|
|
||||||
disabled={isDisabled(editingState, HIGH_VALUE_TRANSACTION_KEY)}
|
|
||||||
setEditing={handleEdit(HIGH_VALUE_TRANSACTION_KEY)}
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TransactionAlerts
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import * as R from 'ramda'
|
|
||||||
|
|
||||||
const EMAIL_KEY = 'email'
|
|
||||||
const SMS_KEY = 'sms'
|
|
||||||
const BALANCE_KEY = 'balance'
|
|
||||||
const TRANSACTIONS_KEY = 'transactions'
|
|
||||||
const COMPLIANCE_KEY = 'compliance'
|
|
||||||
const SECURITY_KEY = 'security'
|
|
||||||
const ERRORS_KEY = 'errors'
|
|
||||||
const ACTIVE_KEY = 'active'
|
|
||||||
const SETUP_KEY = 'setup'
|
|
||||||
const CHANNEL_KEY = 'channel'
|
|
||||||
const TRANSACTION_ALERTS_KEY = 'transactionAlerts'
|
|
||||||
const HIGH_VALUE_TRANSACTION_KEY = 'highValueTransaction'
|
|
||||||
const FIAT_BALANCE_ALERTS_KEY = 'fiatBalanceAlerts'
|
|
||||||
const CASH_IN_FULL_KEY = 'cashInFull'
|
|
||||||
const CASH_OUT_EMPTY_KEY = 'cashOutEmpty'
|
|
||||||
const MACHINE_KEY = 'machine'
|
|
||||||
const PERCENTAGE_KEY = 'percentage'
|
|
||||||
const NUMERARY_KEY = 'numerary'
|
|
||||||
const CASSETTE_1_KEY = 'cassete1'
|
|
||||||
const CASSETTE_2_KEY = 'cassete2'
|
|
||||||
const OVERRIDES_KEY = 'overrides'
|
|
||||||
const CRYPTO_BALANCE_ALERTS_KEY = 'cryptoBalanceAlerts'
|
|
||||||
const LOW_BALANCE_KEY = 'lowBalance'
|
|
||||||
const HIGH_BALANCE_KEY = 'highBalance'
|
|
||||||
const ADD_OVERRIDE_FBA_KEY = 'addOverrideFBA'
|
|
||||||
const ADD_OVERRIDE_CBA_KEY = 'addOverrideCBA'
|
|
||||||
|
|
||||||
const isDisabled = (state, self) =>
|
|
||||||
R.any(x => x === true, R.values(R.omit([self], state)))
|
|
||||||
|
|
||||||
export {
|
|
||||||
EMAIL_KEY,
|
|
||||||
SMS_KEY,
|
|
||||||
BALANCE_KEY,
|
|
||||||
TRANSACTIONS_KEY,
|
|
||||||
COMPLIANCE_KEY,
|
|
||||||
SECURITY_KEY,
|
|
||||||
ERRORS_KEY,
|
|
||||||
ACTIVE_KEY,
|
|
||||||
SETUP_KEY,
|
|
||||||
CHANNEL_KEY,
|
|
||||||
TRANSACTION_ALERTS_KEY,
|
|
||||||
HIGH_VALUE_TRANSACTION_KEY,
|
|
||||||
FIAT_BALANCE_ALERTS_KEY,
|
|
||||||
CASH_IN_FULL_KEY,
|
|
||||||
CASH_OUT_EMPTY_KEY,
|
|
||||||
MACHINE_KEY,
|
|
||||||
PERCENTAGE_KEY,
|
|
||||||
NUMERARY_KEY,
|
|
||||||
CASSETTE_1_KEY,
|
|
||||||
CASSETTE_2_KEY,
|
|
||||||
OVERRIDES_KEY,
|
|
||||||
CRYPTO_BALANCE_ALERTS_KEY,
|
|
||||||
LOW_BALANCE_KEY,
|
|
||||||
HIGH_BALANCE_KEY,
|
|
||||||
ADD_OVERRIDE_FBA_KEY,
|
|
||||||
ADD_OVERRIDE_CBA_KEY,
|
|
||||||
isDisabled
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import { Link } from 'src/components/buttons'
|
||||||
|
import { H4 } from 'src/components/typography'
|
||||||
|
import { ReactComponent as DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.svg'
|
||||||
|
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||||
|
|
||||||
|
import styles from './EditHeader.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const Header = ({ title, editing, disabled, setEditing }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.header}>
|
||||||
|
<H4 className={classes.title}>{title}</H4>
|
||||||
|
{!editing && (
|
||||||
|
<button
|
||||||
|
onClick={() => setEditing(true)}
|
||||||
|
className={classes.button}
|
||||||
|
disabled={disabled}>
|
||||||
|
{disabled ? <DisabledEditIcon /> : <EditIcon />}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{editing && (
|
||||||
|
<div className={classes.editingButtons}>
|
||||||
|
<Link color="primary" type="submit">
|
||||||
|
Save
|
||||||
|
</Link>
|
||||||
|
<Link color="secondary" type="reset">
|
||||||
|
Cancel
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
export default {
|
||||||
|
header: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 16,
|
||||||
|
height: 26,
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flexShrink: 2,
|
||||||
|
margin: 0,
|
||||||
|
overflow: 'hidden',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
cursor: 'pointer',
|
||||||
|
marginLeft: 8
|
||||||
|
},
|
||||||
|
editingButtons: {
|
||||||
|
display: 'flex',
|
||||||
|
flexShrink: 0,
|
||||||
|
marginLeft: 16,
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import { useFormikContext, Field as FormikField } from 'formik'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import TextInput from 'src/components/inputs/formik/TextInput'
|
||||||
|
import { Label1, Info1, TL2 } from 'src/components/typography'
|
||||||
|
|
||||||
|
import styles from './EditableNumber.styles'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const EditableNumber = ({
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
editing,
|
||||||
|
displayValue,
|
||||||
|
decoration,
|
||||||
|
className,
|
||||||
|
width = 80
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles({ width, editing })
|
||||||
|
const { values } = useFormikContext()
|
||||||
|
|
||||||
|
const classNames = {
|
||||||
|
[classes.fieldWrapper]: true,
|
||||||
|
className
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classnames(classNames)}>
|
||||||
|
{label && <Label1 className={classes.label}>{label}</Label1>}
|
||||||
|
<div className={classes.valueWrapper}>
|
||||||
|
{!editing && (
|
||||||
|
<Info1 className={classes.text}>{displayValue(values[name])}</Info1>
|
||||||
|
)}
|
||||||
|
{editing && (
|
||||||
|
<FormikField
|
||||||
|
id={name}
|
||||||
|
size="lg"
|
||||||
|
fullWidth
|
||||||
|
name={name}
|
||||||
|
component={TextInput}
|
||||||
|
textAlign="right"
|
||||||
|
type="text"
|
||||||
|
width={width}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<TL2 className={classes.decoration}>{decoration}</TL2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditableNumber
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
export default {
|
||||||
|
text: {
|
||||||
|
margin: [[7, 0, 7, 1]]
|
||||||
|
},
|
||||||
|
fieldWrapper: {
|
||||||
|
height: 53
|
||||||
|
},
|
||||||
|
valueWrapper: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'baseline'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
decoration: {
|
||||||
|
margin: [[0, 0, 0, 7]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { Form, Formik } from 'formik'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
|
import Header from './EditHeader'
|
||||||
|
import EditableNumber from './EditableNumber'
|
||||||
|
|
||||||
|
const SingleFieldEditableNumber = ({
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
width = 80,
|
||||||
|
name,
|
||||||
|
section,
|
||||||
|
className
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
save,
|
||||||
|
data,
|
||||||
|
currency,
|
||||||
|
isEditing,
|
||||||
|
isDisabled,
|
||||||
|
setEditing
|
||||||
|
} = useContext(NotificationsCtx)
|
||||||
|
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
[name]: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.required()
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
|
initialValues={{ [name]: (data && data[name]) ?? '' }}
|
||||||
|
validationSchema={schema}
|
||||||
|
onSubmit={it => save(section, it)}
|
||||||
|
onReset={() => {
|
||||||
|
setEditing(name, false)
|
||||||
|
}}>
|
||||||
|
<Form className={className}>
|
||||||
|
<Header
|
||||||
|
title={title}
|
||||||
|
editing={isEditing(name)}
|
||||||
|
disabled={isDisabled(name)}
|
||||||
|
setEditing={it => setEditing(name, it)}
|
||||||
|
/>
|
||||||
|
<EditableNumber
|
||||||
|
label={label}
|
||||||
|
name={name}
|
||||||
|
editing={isEditing(name)}
|
||||||
|
width={width}
|
||||||
|
displayValue={x => (x === '' ? '-' : x)}
|
||||||
|
decoration={currency}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SingleFieldEditableNumber
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
import SingleFieldEditableNumber from '../components/SingleFieldEditableNumber'
|
||||||
|
|
||||||
|
import styles from './CryptoBalanceAlerts.styles'
|
||||||
|
|
||||||
|
const LOW_BALANCE_KEY = 'cryptoLowBalance'
|
||||||
|
const HIGH_BALANCE_KEY = 'cryptoHighBalance'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const CryptoBalanceAlerts = ({ section }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
save,
|
||||||
|
currency,
|
||||||
|
setEditing,
|
||||||
|
isEditing,
|
||||||
|
isDisabled
|
||||||
|
} = useContext(NotificationsCtx)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.cryptoBalanceAlerts}>
|
||||||
|
<SingleFieldEditableNumber
|
||||||
|
name={LOW_BALANCE_KEY}
|
||||||
|
data={data}
|
||||||
|
save={save}
|
||||||
|
section={section}
|
||||||
|
decoration={currency}
|
||||||
|
className={classes.cryptoBalanceAlertsForm}
|
||||||
|
title="Default (Low Balance)"
|
||||||
|
label="Alert me under"
|
||||||
|
editing={isEditing(LOW_BALANCE_KEY)}
|
||||||
|
disabled={isDisabled(LOW_BALANCE_KEY)}
|
||||||
|
setEditing={it => setEditing(LOW_BALANCE_KEY, it)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={classes.vertSeparator} />
|
||||||
|
|
||||||
|
<SingleFieldEditableNumber
|
||||||
|
name={HIGH_BALANCE_KEY}
|
||||||
|
data={data}
|
||||||
|
section={section}
|
||||||
|
save={save}
|
||||||
|
decoration={currency}
|
||||||
|
className={classes.cryptoBalanceAlertsSecondForm}
|
||||||
|
title="Default (High Balance)"
|
||||||
|
label="Alert me over"
|
||||||
|
editing={isEditing(HIGH_BALANCE_KEY)}
|
||||||
|
disabled={isDisabled(HIGH_BALANCE_KEY)}
|
||||||
|
setEditing={it => setEditing(HIGH_BALANCE_KEY, it)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CryptoBalanceAlerts
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export default {
|
||||||
|
cryptoBalanceAlerts: {
|
||||||
|
display: 'flex',
|
||||||
|
marginBottom: 36,
|
||||||
|
height: 135,
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
cryptoBalanceAlertsForm: {
|
||||||
|
width: 222,
|
||||||
|
marginRight: 32
|
||||||
|
},
|
||||||
|
cryptoBalanceAlertsSecondForm: {
|
||||||
|
marginLeft: 50
|
||||||
|
},
|
||||||
|
vertSeparator: {
|
||||||
|
width: 1,
|
||||||
|
height: '100%',
|
||||||
|
borderRight: [[1, 'solid', 'black']]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
|
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
|
||||||
|
import TextInputFormik from 'src/components/inputs/formik/TextInput.js'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
|
const HIGH_BALANCE_KEY = 'highBalance'
|
||||||
|
const LOW_BALANCE_KEY = 'lowBalance'
|
||||||
|
const CRYPTOCURRENCY_KEY = 'cryptoCurrency'
|
||||||
|
const NAME = 'cryptoBalanceOverrides'
|
||||||
|
|
||||||
|
const CryptoBalanceOverrides = ({ section }) => {
|
||||||
|
const {
|
||||||
|
cryptoCurrencies,
|
||||||
|
data,
|
||||||
|
save,
|
||||||
|
currency,
|
||||||
|
isDisabled,
|
||||||
|
setEditing
|
||||||
|
} = useContext(NotificationsCtx)
|
||||||
|
const setupValues = data?.cryptoBalanceOverrides ?? []
|
||||||
|
const innerSetEditing = it => setEditing(NAME, it)
|
||||||
|
|
||||||
|
const onDelete = id => {
|
||||||
|
const newOverrides = {
|
||||||
|
cryptoBalanceOverrides: R.reject(it => it.id === id, setupValues)
|
||||||
|
}
|
||||||
|
return save(newOverrides)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSuggestions = () => {
|
||||||
|
const overridenCryptos = R.map(
|
||||||
|
override => override[CRYPTOCURRENCY_KEY],
|
||||||
|
setupValues
|
||||||
|
)
|
||||||
|
return R.without(overridenCryptos, cryptoCurrencies ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
[CRYPTOCURRENCY_KEY]: null,
|
||||||
|
[LOW_BALANCE_KEY]: '',
|
||||||
|
[HIGH_BALANCE_KEY]: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
[CRYPTOCURRENCY_KEY]: Yup.string().required(),
|
||||||
|
[LOW_BALANCE_KEY]: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.max(99999999)
|
||||||
|
.required(),
|
||||||
|
[HIGH_BALANCE_KEY]: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.max(99999999)
|
||||||
|
.required()
|
||||||
|
})
|
||||||
|
|
||||||
|
const suggestions = getSuggestions()
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
{
|
||||||
|
name: CRYPTOCURRENCY_KEY,
|
||||||
|
header: 'Cryptocurrency',
|
||||||
|
width: 166,
|
||||||
|
size: 'sm',
|
||||||
|
view: R.path(['display']),
|
||||||
|
input: Autocomplete,
|
||||||
|
inputProps: {
|
||||||
|
options: suggestions,
|
||||||
|
limit: null,
|
||||||
|
forceShowValue: true,
|
||||||
|
getOptionSelected: R.eqProps('display')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: LOW_BALANCE_KEY,
|
||||||
|
width: 155,
|
||||||
|
textAlign: 'right',
|
||||||
|
bold: true,
|
||||||
|
input: TextInputFormik,
|
||||||
|
suffix: currency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: HIGH_BALANCE_KEY,
|
||||||
|
width: 155,
|
||||||
|
textAlign: 'right',
|
||||||
|
bold: true,
|
||||||
|
input: TextInputFormik,
|
||||||
|
suffix: currency
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableTable
|
||||||
|
name={NAME}
|
||||||
|
title="Overrides"
|
||||||
|
enableDelete
|
||||||
|
enableEdit
|
||||||
|
enableCreate
|
||||||
|
save={it => save(section, it)}
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
forceDisable={isDisabled(NAME) || !cryptoCurrencies}
|
||||||
|
data={setupValues}
|
||||||
|
elements={elements}
|
||||||
|
disableAdd={!suggestions?.length}
|
||||||
|
onDelete={onDelete}
|
||||||
|
setEditing={innerSetEditing}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CryptoBalanceOverrides
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import { Form, Formik } from 'formik'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import { TL2 } from 'src/components/typography'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
import Header from '../components/EditHeader'
|
||||||
|
import EditableNumber from '../components/EditableNumber'
|
||||||
|
|
||||||
|
import styles from './FiatBalanceAlerts.styles.js'
|
||||||
|
|
||||||
|
const useStyles = makeStyles(styles)
|
||||||
|
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
fiatBalanceCassette1: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.required(),
|
||||||
|
fiatBalanceCassette2: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.required()
|
||||||
|
})
|
||||||
|
|
||||||
|
const NAME = 'fiatBalanceAlerts'
|
||||||
|
|
||||||
|
const FiatBalance = ({ section }) => {
|
||||||
|
const { isEditing, isDisabled, setEditing, data, save } = useContext(
|
||||||
|
NotificationsCtx
|
||||||
|
)
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const editing = isEditing(NAME)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
enableReinitialize
|
||||||
|
initialValues={{
|
||||||
|
fiatBalanceCassette1: data?.fiatBalanceCassette1 ?? '',
|
||||||
|
fiatBalanceCassette2: data?.fiatBalanceCassette2 ?? ''
|
||||||
|
}}
|
||||||
|
validationSchema={schema}
|
||||||
|
onSubmit={it => save(section, it)}
|
||||||
|
onReset={() => {
|
||||||
|
setEditing(NAME, false)
|
||||||
|
}}>
|
||||||
|
<Form className={classes.form}>
|
||||||
|
<Header
|
||||||
|
title="Cash out (Empty)"
|
||||||
|
editing={editing}
|
||||||
|
disabled={isDisabled(NAME)}
|
||||||
|
setEditing={it => setEditing(NAME, it)}
|
||||||
|
/>
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<div className={classes.first}>
|
||||||
|
<TL2 className={classes.title}>Cassette 1 (Top)</TL2>
|
||||||
|
<EditableNumber
|
||||||
|
label="Alert me under"
|
||||||
|
name="fiatBalanceCassette1"
|
||||||
|
editing={editing}
|
||||||
|
displayValue={x => (x === '' ? '-' : x)}
|
||||||
|
decoration="notes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<TL2 className={classes.title}>Cassette 2 (Bottom)</TL2>
|
||||||
|
<EditableNumber
|
||||||
|
label="Alert me under"
|
||||||
|
name="fiatBalanceCassette2"
|
||||||
|
editing={editing}
|
||||||
|
displayValue={x => (x === '' ? '-' : x)}
|
||||||
|
decoration="notes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FiatBalance
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
export default {
|
||||||
|
wrapper: {
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
marginBottom: 36
|
||||||
|
},
|
||||||
|
first: {
|
||||||
|
width: 200
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginTop: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
|
||||||
|
import { Table as EditableTable } from 'src/components/editableTable'
|
||||||
|
import Autocomplete from 'src/components/inputs/formik/Autocomplete'
|
||||||
|
import TextInputFormik from 'src/components/inputs/formik/TextInput.js'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
|
const CASSETTE_1_KEY = 'cassette1'
|
||||||
|
const CASSETTE_2_KEY = 'cassette2'
|
||||||
|
const MACHINE_KEY = 'machine'
|
||||||
|
const NAME = 'fiatBalanceOverrides'
|
||||||
|
|
||||||
|
const FiatBalanceOverrides = ({ section }) => {
|
||||||
|
const { machines, data, save, isDisabled, setEditing } = useContext(
|
||||||
|
NotificationsCtx
|
||||||
|
)
|
||||||
|
|
||||||
|
const setupValues = data?.fiatBalanceOverrides ?? []
|
||||||
|
const innerSetEditing = it => setEditing(NAME, it)
|
||||||
|
|
||||||
|
const getSuggestions = () => {
|
||||||
|
const overridenMachines = R.map(override => override.machine, setupValues)
|
||||||
|
return R.without(overridenMachines, machines ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
[MACHINE_KEY]: null,
|
||||||
|
[CASSETTE_1_KEY]: '',
|
||||||
|
[CASSETTE_2_KEY]: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
[MACHINE_KEY]: Yup.string().required(),
|
||||||
|
[CASSETTE_1_KEY]: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.required(),
|
||||||
|
[CASSETTE_2_KEY]: Yup.number()
|
||||||
|
.integer()
|
||||||
|
.min(0)
|
||||||
|
.required()
|
||||||
|
})
|
||||||
|
|
||||||
|
const suggestions = getSuggestions()
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
{
|
||||||
|
name: MACHINE_KEY,
|
||||||
|
width: 238,
|
||||||
|
size: 'sm',
|
||||||
|
view: R.path(['name']),
|
||||||
|
input: Autocomplete,
|
||||||
|
inputProps: {
|
||||||
|
options: suggestions,
|
||||||
|
limit: null,
|
||||||
|
forceShowValue: true,
|
||||||
|
getOptionSelected: R.eqProps('display')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CASSETTE_1_KEY,
|
||||||
|
display: 'Cash-out 1',
|
||||||
|
width: 155,
|
||||||
|
textAlign: 'right',
|
||||||
|
bold: true,
|
||||||
|
input: TextInputFormik,
|
||||||
|
suffix: 'notes'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CASSETTE_2_KEY,
|
||||||
|
display: 'Cash-out 2',
|
||||||
|
width: 155,
|
||||||
|
textAlign: 'right',
|
||||||
|
bold: true,
|
||||||
|
input: TextInputFormik,
|
||||||
|
suffix: 'notes'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableTable
|
||||||
|
name={NAME}
|
||||||
|
title="Overrides"
|
||||||
|
enableDelete
|
||||||
|
enableEdit
|
||||||
|
enableCreate
|
||||||
|
save={it => save(section, it)}
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
forceDisable={isDisabled(NAME) || !machines}
|
||||||
|
data={setupValues}
|
||||||
|
elements={elements}
|
||||||
|
disableAdd={!suggestions?.length}
|
||||||
|
setEditing={innerSetEditing}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FiatBalanceOverrides
|
||||||
87
new-lamassu-admin/src/pages/Notifications/sections/Setup.js
Normal file
87
new-lamassu-admin/src/pages/Notifications/sections/Setup.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
THead,
|
||||||
|
TBody,
|
||||||
|
Tr,
|
||||||
|
Td,
|
||||||
|
Th
|
||||||
|
} from 'src/components/fake-table/Table'
|
||||||
|
import { Switch } from 'src/components/inputs'
|
||||||
|
import { startCase } from 'src/utils/string'
|
||||||
|
|
||||||
|
import NotificationsCtx from '../NotificationsContext'
|
||||||
|
|
||||||
|
const channelSize = 129
|
||||||
|
const sizes = {
|
||||||
|
balance: 152,
|
||||||
|
transactions: 184,
|
||||||
|
compliance: 178,
|
||||||
|
errors: 142,
|
||||||
|
active: 263
|
||||||
|
}
|
||||||
|
const width = R.sum(R.values(sizes)) + channelSize
|
||||||
|
|
||||||
|
const Row = ({ namespace }) => {
|
||||||
|
const { data, save } = useContext(NotificationsCtx)
|
||||||
|
const disabled = !data || !data[`${namespace}_active`]
|
||||||
|
|
||||||
|
const Cell = ({ name, disabled }) => {
|
||||||
|
const namespaced = `${namespace}_${name}`
|
||||||
|
const value = !!(data && data[namespaced])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Td width={sizes[name]} textAlign="center">
|
||||||
|
<Switch
|
||||||
|
disabled={disabled}
|
||||||
|
checked={value}
|
||||||
|
onChange={event => {
|
||||||
|
save(null, { [namespaced]: event.target.checked })
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</Td>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tr>
|
||||||
|
<Td width={channelSize}>{startCase(namespace)}</Td>
|
||||||
|
<Cell name="balance" disabled={disabled} />
|
||||||
|
<Cell name="transactions" disabled={disabled} />
|
||||||
|
<Cell name="compliance" disabled={disabled} />
|
||||||
|
<Cell name="errors" disabled={disabled} />
|
||||||
|
<Cell name="active" />
|
||||||
|
</Tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
mainTable: {
|
||||||
|
width
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const Setup = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<Table className={classes.mainTable}>
|
||||||
|
<THead>
|
||||||
|
<Th width={channelSize}>Channel</Th>
|
||||||
|
{Object.keys(sizes).map(it => (
|
||||||
|
<Th key={it} width={sizes[it]} textAlign="center">
|
||||||
|
{startCase(it)}
|
||||||
|
</Th>
|
||||||
|
))}
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
<Row namespace="email" />
|
||||||
|
<Row namespace="sms" />
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Setup
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import SingleFieldEditableNumber from '../components/SingleFieldEditableNumber'
|
||||||
|
|
||||||
|
const NAME = 'highValueTransaction'
|
||||||
|
|
||||||
|
const TransactionAlerts = ({ section }) => {
|
||||||
|
return (
|
||||||
|
<SingleFieldEditableNumber
|
||||||
|
section={section}
|
||||||
|
title="High value transaction"
|
||||||
|
label="Alert me over"
|
||||||
|
name={NAME}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransactionAlerts
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import * as R from 'ramda'
|
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import * as R from 'ramda'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import Sidebar from 'src/components/Sidebar'
|
import Sidebar from 'src/components/Sidebar'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
|
|
@ -53,7 +53,6 @@ const OperatorInfo = () => {
|
||||||
{isSelected(CONTACT_INFORMATION) && <ContactInfo />}
|
{isSelected(CONTACT_INFORMATION) && <ContactInfo />}
|
||||||
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
||||||
{isSelected(COIN_ATM_RADAR) && <CoinAtmRadar />}
|
{isSelected(COIN_ATM_RADAR) && <CoinAtmRadar />}
|
||||||
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||||
import { makeStyles } from '@material-ui/core'
|
import { makeStyles } from '@material-ui/core'
|
||||||
|
import { gql } from 'apollo-boost'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
|
||||||
import { gql } from 'apollo-boost'
|
|
||||||
|
|
||||||
import typographyStyles from 'src/components/typography/styles'
|
|
||||||
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
|
||||||
import Title from 'src/components/Title'
|
import Title from 'src/components/Title'
|
||||||
import Uptime from 'src/components/Uptime'
|
import Uptime from 'src/components/Uptime'
|
||||||
|
|
@ -20,6 +19,7 @@ import {
|
||||||
TableCell
|
TableCell
|
||||||
} from 'src/components/table'
|
} from 'src/components/table'
|
||||||
import { Info3 } from 'src/components/typography'
|
import { Info3 } from 'src/components/typography'
|
||||||
|
import typographyStyles from 'src/components/typography/styles'
|
||||||
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
import { ReactComponent as DownloadActive } from 'src/styling/icons/button/download/white.svg'
|
||||||
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
import { ReactComponent as Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||||
import { offColor } from 'src/styling/variables'
|
import { offColor } from 'src/styling/variables'
|
||||||
|
|
|
||||||
|
|
@ -70,29 +70,29 @@ const Transactions = () => {
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
header: '',
|
header: '',
|
||||||
size: 62,
|
width: 62,
|
||||||
view: it => (it.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />)
|
view: it => (it.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Machine',
|
header: 'Machine',
|
||||||
name: 'machineName',
|
name: 'machineName',
|
||||||
size: 180,
|
width: 180,
|
||||||
view: R.path(['machineName'])
|
view: R.path(['machineName'])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Customer',
|
header: 'Customer',
|
||||||
size: 162,
|
width: 162,
|
||||||
view: getCustomerDisplayName
|
view: getCustomerDisplayName
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Cash',
|
header: 'Cash',
|
||||||
size: 110,
|
width: 110,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`
|
view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Crypto',
|
header: 'Crypto',
|
||||||
size: 141,
|
width: 141,
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
view: it =>
|
view: it =>
|
||||||
`${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat(5)} ${
|
`${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat(5)} ${
|
||||||
|
|
@ -103,27 +103,27 @@ const Transactions = () => {
|
||||||
header: 'Address',
|
header: 'Address',
|
||||||
view: R.path(['toAddress']),
|
view: R.path(['toAddress']),
|
||||||
className: classes.overflowTd,
|
className: classes.overflowTd,
|
||||||
size: 136
|
width: 136
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Date (UTC)',
|
header: 'Date (UTC)',
|
||||||
view: it => moment.utc(it.created).format('YYYY-MM-D'),
|
view: it => moment.utc(it.created).format('YYYY-MM-D'),
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
size: 124
|
width: 124
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Time (UTC)',
|
header: 'Time (UTC)',
|
||||||
view: it => moment.utc(it.created).format('HH:mm:ss'),
|
view: it => moment.utc(it.created).format('HH:mm:ss'),
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
size: 124
|
width: 124
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: '', // Trade
|
header: '', // Trade
|
||||||
view: () => {},
|
view: () => {},
|
||||||
size: 90
|
width: 90
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 71
|
width: 71
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { makeStyles } from '@material-ui/core'
|
|
||||||
import { useQuery } from '@apollo/react-hooks'
|
import { useQuery } from '@apollo/react-hooks'
|
||||||
|
import { makeStyles } from '@material-ui/core'
|
||||||
import { gql } from 'apollo-boost'
|
import { gql } from 'apollo-boost'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import ExpTable from '../../components/expandable-table/ExpTable'
|
|
||||||
import { MainStatus } from '../../components/Status'
|
import { MainStatus } from '../../components/Status'
|
||||||
import Title from '../../components/Title'
|
import Title from '../../components/Title'
|
||||||
|
import ExpTable from '../../components/expandable-table/ExpTable'
|
||||||
import { ReactComponent as WarningIcon } from '../../styling/icons/status/pumpkin.svg'
|
import { ReactComponent as WarningIcon } from '../../styling/icons/status/pumpkin.svg'
|
||||||
import { ReactComponent as ErrorIcon } from '../../styling/icons/status/tomato.svg'
|
import { ReactComponent as ErrorIcon } from '../../styling/icons/status/tomato.svg'
|
||||||
import { mainStyles } from '../Transactions/Transactions.styles'
|
import { mainStyles } from '../Transactions/Transactions.styles'
|
||||||
|
|
@ -41,36 +41,36 @@ const MachineStatus = () => {
|
||||||
const elements = [
|
const elements = [
|
||||||
{
|
{
|
||||||
header: 'Machine Name',
|
header: 'Machine Name',
|
||||||
size: 232,
|
width: 232,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.name
|
view: m => m.name
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Status',
|
header: 'Status',
|
||||||
size: 349,
|
width: 349,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => <MainStatus statuses={m.statuses} />
|
view: m => <MainStatus statuses={m.statuses} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Last ping',
|
header: 'Last ping',
|
||||||
size: 192,
|
width: 192,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => moment(m.lastPing).fromNow()
|
view: m => moment(m.lastPing).fromNow()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Ping Time',
|
header: 'Ping Time',
|
||||||
size: 155,
|
width: 155,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.pingTime || 'unknown'
|
view: m => m.pingTime || 'unknown'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Software Version',
|
header: 'Software Version',
|
||||||
size: 201,
|
width: 201,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.softwareVersion || 'unknown'
|
view: m => m.softwareVersion || 'unknown'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
size: 71
|
width: 71
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,25 +16,43 @@ import Transactions from 'src/pages/Transactions/Transactions'
|
||||||
import MachineStatus from 'src/pages/maintenance/MachineStatus'
|
import MachineStatus from 'src/pages/maintenance/MachineStatus'
|
||||||
|
|
||||||
const tree = [
|
const tree = [
|
||||||
{ key: 'transactions', label: 'Transactions', route: '/transactions' },
|
{
|
||||||
// maintenence: { label: 'Maintenence', children: [{ label: 'Locale', route: '/locale' }] },
|
key: 'transactions',
|
||||||
// analytics: { label: 'Analytics', children: [{ label: 'Locale', route: '/locale' }] },
|
label: 'Transactions',
|
||||||
|
route: '/transactions',
|
||||||
|
component: Transactions
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'maintenance',
|
key: 'maintenance',
|
||||||
label: 'Maintenance',
|
label: 'Maintenance',
|
||||||
route: '/maintenance',
|
route: '/maintenance',
|
||||||
|
get component() {
|
||||||
|
return () => <Redirect to={this.children[0].route} />
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{ key: 'logs', label: 'Logs', route: '/maintenance/logs' },
|
{
|
||||||
{ key: 'fuding', label: 'Funding', route: '/maintenance/funding' },
|
key: 'logs',
|
||||||
|
label: 'Logs',
|
||||||
|
route: '/maintenance/logs',
|
||||||
|
component: MachineLogs
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fuding',
|
||||||
|
label: 'Funding',
|
||||||
|
route: '/maintenance/funding',
|
||||||
|
component: Funding
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'server-logs',
|
key: 'server-logs',
|
||||||
label: 'Server',
|
label: 'Server',
|
||||||
route: '/maintenance/server-logs'
|
route: '/maintenance/server-logs',
|
||||||
|
component: ServerLogs
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'machine-status',
|
key: 'machine-status',
|
||||||
label: 'Machine Status',
|
label: 'Machine Status',
|
||||||
route: '/maintenance/machine-status'
|
route: '/maintenance/machine-status',
|
||||||
|
component: MachineStatus
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -42,87 +60,74 @@ const tree = [
|
||||||
key: 'settings',
|
key: 'settings',
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
route: '/settings',
|
route: '/settings',
|
||||||
|
get component() {
|
||||||
|
return () => <Redirect to={this.children[0].route} />
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: 'commissions',
|
key: 'commissions',
|
||||||
label: 'Commissions',
|
label: 'Commissions',
|
||||||
route: '/settings/commissions'
|
route: '/settings/commissions',
|
||||||
|
component: Commissions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'locale',
|
||||||
|
label: 'Locale',
|
||||||
|
route: '/settings/locale',
|
||||||
|
component: Locales
|
||||||
},
|
},
|
||||||
{ key: 'locale', label: 'Locale', route: '/settings/locale' },
|
|
||||||
{
|
{
|
||||||
key: 'services',
|
key: 'services',
|
||||||
label: '3rd party services',
|
label: '3rd party services',
|
||||||
route: '/settings/3rd-party-services'
|
route: '/settings/3rd-party-services',
|
||||||
|
component: Services
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'notifications',
|
key: 'notifications',
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
route: '/settings/notifications'
|
route: '/settings/notifications',
|
||||||
|
component: Notifications
|
||||||
},
|
},
|
||||||
{ key: 'info', label: 'Operator Info', route: '/settings/operator-info' }
|
{
|
||||||
|
key: 'info',
|
||||||
|
label: 'Operator Info',
|
||||||
|
route: '/settings/operator-info',
|
||||||
|
component: OperatorInfo
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'compliance',
|
key: 'compliance',
|
||||||
label: 'Compliance',
|
label: 'Compliance',
|
||||||
route: '/compliance',
|
route: '/compliance',
|
||||||
|
get component() {
|
||||||
|
return () => <Redirect to={this.children[0].route} />
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
// {
|
|
||||||
// key: 'triggers',
|
|
||||||
// label: 'Triggers',
|
|
||||||
// route: '/compliance/triggers'
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
key: 'customers',
|
key: 'customers',
|
||||||
label: 'Customers',
|
label: 'Customers',
|
||||||
route: '/compliance/customers'
|
route: '/compliance/customers',
|
||||||
|
component: Customers
|
||||||
}
|
}
|
||||||
// {
|
|
||||||
// key: 'blacklist',
|
|
||||||
// label: 'Blacklist',
|
|
||||||
// route: '/compliance/blacklist'
|
|
||||||
// }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// compliance: { label: 'Compliance', children: [{ label: 'Locale', route: '/locale' }] }
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const firstChild = key => {
|
const map = R.map(R.when(R.has('children'), R.prop('children')))
|
||||||
const getRoute = R.path(['children', 0, 'route'])
|
const leafRoutes = R.compose(R.flatten, map)(tree)
|
||||||
const withKey = R.find(R.propEq('key', key))
|
const parentRoutes = R.filter(R.has('children'))(tree)
|
||||||
return R.compose(getRoute, withKey)(tree)
|
const flattened = R.concat(leafRoutes, parentRoutes)
|
||||||
}
|
|
||||||
|
|
||||||
const Routes = () => (
|
const Routes = () => (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" />
|
<Route exact path="/" />
|
||||||
<Route
|
|
||||||
path="/settings"
|
|
||||||
exact
|
|
||||||
component={() => <Redirect to={firstChild('settings')} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/maintenance"
|
|
||||||
exact
|
|
||||||
component={() => <Redirect to={firstChild('maintenance')} />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/compliance"
|
|
||||||
exact
|
|
||||||
component={() => <Redirect to={firstChild('compliance')} />}
|
|
||||||
/>
|
|
||||||
<Route path="/settings/commissions" component={Commissions} />
|
|
||||||
<Route path="/settings/locale" component={Locales} />
|
|
||||||
<Route path="/settings/3rd-party-services" component={Services} />
|
|
||||||
<Route path="/settings/notifications" component={Notifications} />
|
|
||||||
<Route path="/settings/operator-info" component={OperatorInfo} />
|
|
||||||
<Route path="/maintenance/logs" component={MachineLogs} />
|
|
||||||
<Route path="/maintenance/funding" component={Funding} />
|
|
||||||
<Route path="/maintenance/server-logs" component={ServerLogs} />
|
|
||||||
<Route path="/transactions" component={Transactions} />
|
|
||||||
<Route path="/register" component={AuthRegister} />
|
<Route path="/register" component={AuthRegister} />
|
||||||
<Route path="/maintenance/machine-status" component={MachineStatus} />
|
{flattened.map(({ route, component: Page, key }) => (
|
||||||
<Route path="/compliance/customers" component={Customers} />
|
<Route path={route} key={key}>
|
||||||
|
<Page name={key} />
|
||||||
|
</Route>
|
||||||
|
))}
|
||||||
</Switch>
|
</Switch>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ import { create } from 'jss'
|
||||||
import extendJss from 'jss-plugin-extend'
|
import extendJss from 'jss-plugin-extend'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { ReactComponent as AuthorizeIconReversed } from 'src/styling/icons/button/authorize/white.svg'
|
|
||||||
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
|
||||||
import { ActionButton, Button, Link } from 'src/components/buttons'
|
import { ActionButton, Button, Link } from 'src/components/buttons'
|
||||||
import { Radio, TextInput, Switch } from 'src/components/inputs'
|
import { Radio, TextInput, Switch } from 'src/components/inputs'
|
||||||
|
import { ReactComponent as AuthorizeIconReversed } from 'src/styling/icons/button/authorize/white.svg'
|
||||||
|
import { ReactComponent as AuthorizeIcon } from 'src/styling/icons/button/authorize/zodiac.svg'
|
||||||
|
|
||||||
import ConfirmDialog from '../components/ConfirmDialog'
|
import ConfirmDialog from '../components/ConfirmDialog'
|
||||||
import {
|
import {
|
||||||
|
|
@ -29,7 +29,11 @@ import {
|
||||||
Info2,
|
Info2,
|
||||||
Mono
|
Mono
|
||||||
} from '../components/typography'
|
} from '../components/typography'
|
||||||
import { inputFontFamily, secondaryColor } from '../styling/variables'
|
import {
|
||||||
|
inputFontFamily,
|
||||||
|
secondaryColor,
|
||||||
|
fontColor
|
||||||
|
} from '../styling/variables'
|
||||||
|
|
||||||
const jss = create({
|
const jss = create({
|
||||||
plugins: [extendJss(), ...jssPreset().plugins]
|
plugins: [extendJss(), ...jssPreset().plugins]
|
||||||
|
|
@ -78,6 +82,19 @@ const theme = createMuiTheme({
|
||||||
dark: secondaryColor,
|
dark: secondaryColor,
|
||||||
main: secondaryColor
|
main: secondaryColor
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
MuiInputLabel: {
|
||||||
|
paddingLeft: 4
|
||||||
|
},
|
||||||
|
MuiFormLabel: {
|
||||||
|
root: {
|
||||||
|
color: fontColor,
|
||||||
|
'&$focused': {
|
||||||
|
color: 'blue'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,26 @@
|
||||||
|
import {
|
||||||
|
inputFontSize,
|
||||||
|
inputFontSizeLg,
|
||||||
|
inputFontSizeSm,
|
||||||
|
inputFontWeightBold
|
||||||
|
} from './variables'
|
||||||
|
|
||||||
const respondTo = breakpoint =>
|
const respondTo = breakpoint =>
|
||||||
`@media only screen and (max-width: ${breakpoint})`
|
`@media only screen and (max-width: ${breakpoint})`
|
||||||
|
|
||||||
export { respondTo }
|
const bySize = size => {
|
||||||
|
switch (size) {
|
||||||
|
case 'sm':
|
||||||
|
return { fontSize: inputFontSizeSm }
|
||||||
|
case 'lg':
|
||||||
|
return { fontSize: inputFontSizeLg, fontWeight: inputFontWeightBold }
|
||||||
|
default:
|
||||||
|
return { fontSize: inputFontSize }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bold = {
|
||||||
|
fontWeight: inputFontWeightBold
|
||||||
|
}
|
||||||
|
|
||||||
|
export { respondTo, bySize, bold }
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,29 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<svg
|
||||||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
width="22"
|
||||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
height="22"
|
||||||
<title>icon/action/delete/disabled</title>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<desc>Created with Sketch.</desc>
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<defs>
|
<defs>
|
||||||
<rect id="path-1" x="0" y="0" width="22" height="22"></rect>
|
<path id="a" d="M0 0h22v22H0z" />
|
||||||
</defs>
|
</defs>
|
||||||
<g id="icon/action/delete/disabled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g fill="none" fill-rule="evenodd">
|
||||||
<mask id="mask-2" fill="white">
|
<mask id="b" fill="#fff">
|
||||||
<use xlink:href="#path-1"></use>
|
<use xlink:href="#a" />
|
||||||
</mask>
|
</mask>
|
||||||
<g id="Background"></g>
|
<path
|
||||||
<line x1="9" y1="9" x2="9" y2="18" id="Stroke-1" stroke="#9B9B9B" stroke-width="2" stroke-linecap="round" mask="url(#mask-2)"></line>
|
stroke="#9B9B9B"
|
||||||
<line x1="13" y1="9" x2="13" y2="18" id="Stroke-2" stroke="#9B9B9B" stroke-width="2" stroke-linecap="round" mask="url(#mask-2)"></line>
|
stroke-width="2"
|
||||||
<g id="Group-9" mask="url(#mask-2)" stroke="#9B9B9B" stroke-linecap="round" stroke-width="2">
|
stroke-linecap="round"
|
||||||
<g transform="translate(1.000000, 1.000000)">
|
mask="url(#b)"
|
||||||
<polyline id="Stroke-3" stroke-linejoin="round" points="2 5 4 20 16 20 18 5"></polyline>
|
d="M9 9v9M13 9v9"
|
||||||
<line x1="0" y1="4" x2="20" y2="4" id="Stroke-5"></line>
|
/>
|
||||||
<path d="M13,3 C13,1.343 11.657,0 10,0 C8.343,0 7,1.343 7,3" id="Stroke-7"></path>
|
<g
|
||||||
</g>
|
mask="url(#b)"
|
||||||
</g>
|
stroke="#9B9B9B"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2">
|
||||||
|
<path stroke-linejoin="round" d="M3 6l2 15h12l2-15" />
|
||||||
|
<path d="M1 5h20M14 4a3 3 0 10-6 0" />
|
||||||
</g>
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 611 B |
|
|
@ -1,6 +1,14 @@
|
||||||
import { createMuiTheme } from '@material-ui/core/styles'
|
import { createMuiTheme } from '@material-ui/core/styles'
|
||||||
|
|
||||||
import { backgroundColor, inputFontFamily, secondaryColor } from './variables'
|
import {
|
||||||
|
backgroundColor,
|
||||||
|
inputFontFamily,
|
||||||
|
secondaryColor,
|
||||||
|
fontColor,
|
||||||
|
offColor,
|
||||||
|
subheaderColor,
|
||||||
|
fontSize5
|
||||||
|
} from './variables'
|
||||||
|
|
||||||
export default createMuiTheme({
|
export default createMuiTheme({
|
||||||
typography: {
|
typography: {
|
||||||
|
|
@ -23,5 +31,69 @@ export default createMuiTheme({
|
||||||
background: {
|
background: {
|
||||||
default: backgroundColor
|
default: backgroundColor
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
MuiAutocomplete: {
|
||||||
|
root: {
|
||||||
|
color: fontColor
|
||||||
|
},
|
||||||
|
noOptions: {
|
||||||
|
padding: [[6, 16]]
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
'&[data-focus="true"]': {
|
||||||
|
backgroundColor: subheaderColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
color: fontColor,
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
listbox: {
|
||||||
|
padding: 0
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
'&[data-tag-index="0"]': {
|
||||||
|
marginLeft: 0
|
||||||
|
},
|
||||||
|
backgroundColor: subheaderColor,
|
||||||
|
borderRadius: 4,
|
||||||
|
height: 18
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiChip: {
|
||||||
|
label: {
|
||||||
|
paddingLeft: 4,
|
||||||
|
paddingRight: 4,
|
||||||
|
color: fontColor,
|
||||||
|
fontSize: fontSize5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiInput: {
|
||||||
|
root: {
|
||||||
|
color: fontColor
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
'&:before': {
|
||||||
|
borderBottom: [[2, 'solid', fontColor]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiInputLabel: {
|
||||||
|
root: {
|
||||||
|
font: 'inherit',
|
||||||
|
color: offColor
|
||||||
|
},
|
||||||
|
shrink: {
|
||||||
|
color: fontColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MuiFormLabel: {
|
||||||
|
root: {
|
||||||
|
'&$focused': {
|
||||||
|
color: fontColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -78,10 +78,11 @@ if (version === 8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const smallestFontSize = fontSize5
|
const smallestFontSize = fontSize5
|
||||||
|
const inputFontSizeSm = fontSize4
|
||||||
const inputFontSize = fontSize3
|
const inputFontSize = fontSize3
|
||||||
const inputFontSizeLg = fontSize1
|
const inputFontSizeLg = fontSize1
|
||||||
const inputFontWeight = 500
|
const inputFontWeight = 500
|
||||||
const inputFontWeightLg = 700
|
const inputFontWeightBold = 700
|
||||||
const inputFontFamily = fontSecondary
|
const inputFontFamily = fontSecondary
|
||||||
|
|
||||||
// Breakpoints
|
// Breakpoints
|
||||||
|
|
@ -159,10 +160,11 @@ export {
|
||||||
// named font sizes
|
// named font sizes
|
||||||
smallestFontSize,
|
smallestFontSize,
|
||||||
inputFontSize,
|
inputFontSize,
|
||||||
|
inputFontSizeSm,
|
||||||
inputFontSizeLg,
|
inputFontSizeLg,
|
||||||
inputFontFamily,
|
inputFontFamily,
|
||||||
inputFontWeight,
|
inputFontWeight,
|
||||||
inputFontWeightLg,
|
inputFontWeightBold,
|
||||||
// screen sizes
|
// screen sizes
|
||||||
sm,
|
sm,
|
||||||
md,
|
md,
|
||||||
|
|
|
||||||
21
new-lamassu-admin/src/utils/config.js
Normal file
21
new-lamassu-admin/src/utils/config.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import * as R from 'ramda'
|
||||||
|
|
||||||
|
const mapKeys = R.curry((fn, obj) =>
|
||||||
|
R.fromPairs(R.map(R.adjust(0, fn), R.toPairs(obj)))
|
||||||
|
)
|
||||||
|
|
||||||
|
const filterByKey = R.curry((fn, obj) =>
|
||||||
|
R.fromPairs(R.filter(it => fn(it[0]), R.toPairs(obj)))
|
||||||
|
)
|
||||||
|
|
||||||
|
const stripl = R.curry((q, str) =>
|
||||||
|
R.startsWith(q, str) ? str.slice(q.length) : str
|
||||||
|
)
|
||||||
|
|
||||||
|
const filtered = key => filterByKey(R.startsWith(`${key}_`))
|
||||||
|
const stripped = key => mapKeys(stripl(`${key}_`))
|
||||||
|
|
||||||
|
const fromServer = key => R.compose(stripped(key), filtered(key))
|
||||||
|
const toServer = key => mapKeys(it => `${key}_${it}`)
|
||||||
|
|
||||||
|
export { fromServer, toServer }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue