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) {
|
||||
const currentState = db.getState()
|
||||
// TODO this should be _.assign
|
||||
// change after flattening of schema
|
||||
const newState = _.mergeWith((objValue, srcValue) => {
|
||||
if (_.isArray(objValue)) {
|
||||
return srcValue
|
||||
|
|
|
|||
541
new-lamassu-admin/package-lock.json
generated
541
new-lamassu-admin/package-lock.json
generated
|
|
@ -1840,7 +1840,8 @@
|
|||
"@emotion/hash": {
|
||||
"version": "0.7.3",
|
||||
"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": {
|
||||
"version": "0.8.3",
|
||||
|
|
@ -2226,48 +2227,40 @@
|
|||
}
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.5.0.tgz",
|
||||
"integrity": "sha512-UHVAjU+1uDtA+OMBNBHb4RlCZOu514XeYPafNJv+GTdXBDr1SCPK7yqRE6TV1/bulxlDusTgu5Q6BAUgpmO4MA==",
|
||||
"version": "4.9.8",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.8.tgz",
|
||||
"integrity": "sha512-4cslpG6oLoPWUfwPkX+hvbak4hAGiOfgXOu/UIYeeMrtsTEebC0Mirjoby7zhS4ny86YI3rXEFW6EZDmlj5n5w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.5.0",
|
||||
"@material-ui/system": "^4.5.0",
|
||||
"@material-ui/types": "^4.1.1",
|
||||
"@material-ui/utils": "^4.4.0",
|
||||
"@material-ui/styles": "^4.9.6",
|
||||
"@material-ui/system": "^4.9.6",
|
||||
"@material-ui/types": "^5.0.0",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"@types/react-transition-group": "^4.2.0",
|
||||
"clsx": "^1.0.2",
|
||||
"convert-css-length": "^2.0.1",
|
||||
"deepmerge": "^4.0.0",
|
||||
"hoist-non-react-statics": "^3.2.1",
|
||||
"is-plain-object": "^3.0.0",
|
||||
"normalize-scroll-left": "^0.2.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"popper.js": "^1.14.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.0",
|
||||
"react-transition-group": "^4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.2"
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz",
|
||||
"integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==",
|
||||
"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": {
|
||||
"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": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
|
||||
|
|
@ -2280,134 +2273,176 @@
|
|||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/icons": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.4.3.tgz",
|
||||
"integrity": "sha512-HVVvUyc/78kmaBd93LkfWyGkXMM+zOMKzUfulWXxaV/fFAZ3N0pD0oHjWUd94zrOoF3tZP9JC7EPlIpIcZSNow==",
|
||||
"version": "4.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz",
|
||||
"integrity": "sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.2"
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"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": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.5.0.tgz",
|
||||
"integrity": "sha512-O0NSAECHK9f3DZK6wy56PZzp8b/7KSdfpJs8DSC7vnXUAoMPCTtchBKLzMtUsNlijiJFeJjSxNdQfjWXgyur5A==",
|
||||
"version": "4.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.6.tgz",
|
||||
"integrity": "sha512-ijgwStEkw1OZ6gCz18hkjycpr/3lKs1hYPi88O/AUn4vMuuGEGAIrqKVFq/lADmZUNF3DOFIk8LDkp7zmjPxtA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@emotion/hash": "^0.7.1",
|
||||
"@material-ui/types": "^4.1.1",
|
||||
"@material-ui/utils": "^4.1.0",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@material-ui/types": "^5.0.0",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"clsx": "^1.0.2",
|
||||
"csstype": "^2.5.2",
|
||||
"deepmerge": "^4.0.0",
|
||||
"hoist-non-react-statics": "^3.2.1",
|
||||
"jss": "^10.0.0",
|
||||
"jss-plugin-camel-case": "^10.0.0",
|
||||
"jss-plugin-default-unit": "^10.0.0",
|
||||
"jss-plugin-global": "^10.0.0",
|
||||
"jss-plugin-nested": "^10.0.0",
|
||||
"jss-plugin-props-sort": "^10.0.0",
|
||||
"jss-plugin-rule-value-function": "^10.0.0",
|
||||
"jss-plugin-vendor-prefixer": "^10.0.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"jss": "^10.0.3",
|
||||
"jss-plugin-camel-case": "^10.0.3",
|
||||
"jss-plugin-default-unit": "^10.0.3",
|
||||
"jss-plugin-global": "^10.0.3",
|
||||
"jss-plugin-nested": "^10.0.3",
|
||||
"jss-plugin-props-sort": "^10.0.3",
|
||||
"jss-plugin-rule-value-function": "^10.0.3",
|
||||
"jss-plugin-vendor-prefixer": "^10.0.3",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.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": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/system": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.5.0.tgz",
|
||||
"integrity": "sha512-vR0PbMTzLnuuVCoYNQ13zyhLa/4s/UA9P9JbNuHBOOkfrHn53ShINiG0v05EgfwizfULLtc7mNvsGAgIyyp/hQ==",
|
||||
"version": "4.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.6.tgz",
|
||||
"integrity": "sha512-QtfoAePyqXoZ2HUVSwGb1Ro0kucMCvVjbI0CdYIR21t0Opgfm1Oer6ni9P5lfeXA39xSt0wCierw37j+YES48Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"deepmerge": "^4.0.0",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.2"
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/types": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-4.1.1.tgz",
|
||||
"integrity": "sha512-AN+GZNXytX9yxGi0JOfxHrRTbhFybjUJ05rnsBVjcB+16e466Z0Xe5IxawuOayVZgTBNDxmPKo5j4V6OnMtaSQ==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.0.0.tgz",
|
||||
"integrity": "sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg=="
|
||||
},
|
||||
"@material-ui/utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.4.0.tgz",
|
||||
"integrity": "sha512-UXoQVwArQEQWXxf2FPs0iJGT+MePQpKr0Qh0CPoLc1OdF0GSMTmQczcqCzwZkeHxHAOq/NkIKM1Pb/ih1Avicg==",
|
||||
"version": "4.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.6.tgz",
|
||||
"integrity": "sha512-gqlBn0JPPTUZeAktn1rgMcy9Iczrr74ecx31tyZLVGdBGGzsxzM6PP6zeS7FuoLS6vG4hoZP7hWnOoHtkR0Kvw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.6"
|
||||
"react-is": "^16.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.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": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -2509,6 +2544,12 @@
|
|||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz",
|
||||
"integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==",
|
||||
"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": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.3.tgz",
|
||||
"integrity": "sha512-Hk8jiuT7iLOHrcjKP/ZVSyCNXK73wJAUz60xm0mVhiRujrdiI++j4duLiL282VGxwAgxetHQFfqA29LgEeSkFA==",
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz",
|
||||
"integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
|
@ -6906,7 +6947,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -6927,12 +6969,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -6947,17 +6991,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -7074,7 +7121,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
|
@ -7086,6 +7134,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
|
@ -7100,6 +7149,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -7107,12 +7157,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
|
@ -7131,6 +7183,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
|
@ -7211,7 +7264,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -7223,6 +7277,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
|
@ -7308,7 +7363,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -7344,6 +7400,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
@ -7363,6 +7420,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
|
@ -7406,12 +7464,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -7991,11 +8051,6 @@
|
|||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"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": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
|
||||
|
|
@ -8464,17 +8519,17 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.3.tgz",
|
||||
"integrity": "sha512-kq6anf9JGjW8Nt5rYfEuGRaEAaH1mkv3Bbu6rYvLOpPh/RusSJXuKPEAoZ7L7gybZkchE8+NV5g9vKF4AGAtsA==",
|
||||
"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.2"
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -8762,11 +8817,6 @@
|
|||
"integrity": "sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==",
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.1.1.tgz",
|
||||
"integrity": "sha512-+qO5WbNBKBaZez95TffdUDnGIo4+r5kmsX8aOb7PDHvXsTbghAmleuxjs6ytNaf5Eg4FGBXDS5vqO61TRi6BMg=="
|
||||
},
|
||||
"default-gateway": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
|
||||
|
|
@ -11291,18 +11341,18 @@
|
|||
"dev": true
|
||||
},
|
||||
"formik": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.0.3.tgz",
|
||||
"integrity": "sha512-kYBvcxlsYSncY8OiJHD49C0UmoWXbgmIc9V1g3N1WwBJ7SMLk34QpcJDgroYd42K1cH+mSJlXhB7PlgTXTzlWg==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.1.4.tgz",
|
||||
"integrity": "sha512-oKz8S+yQBzuQVSEoxkqqJrKQS5XJASWGVn6mrs+oTWrBoHgByVwwI1qHiVc9GKDpZBU9vAxXYAKz2BvujlwunA==",
|
||||
"requires": {
|
||||
"deepmerge": "^2.1.1",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"lodash": "^4.17.14",
|
||||
"lodash-es": "^4.17.14",
|
||||
"react-fast-compare": "^2.0.1",
|
||||
"scheduler": "^0.14.0",
|
||||
"scheduler": "^0.18.0",
|
||||
"tiny-warning": "^1.0.2",
|
||||
"tslib": "^1.9.3"
|
||||
"tslib": "^1.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"deepmerge": {
|
||||
|
|
@ -11311,13 +11361,18 @@
|
|||
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.14.0.tgz",
|
||||
"integrity": "sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw==",
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
|
||||
"integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz",
|
||||
"integrity": "sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz",
|
||||
"integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz",
|
||||
"integrity": "sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz",
|
||||
"integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==",
|
||||
"requires": {
|
||||
"@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": {
|
||||
|
|
@ -14719,50 +14800,115 @@
|
|||
}
|
||||
},
|
||||
"jss-plugin-global": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz",
|
||||
"integrity": "sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz",
|
||||
"integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==",
|
||||
"requires": {
|
||||
"@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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz",
|
||||
"integrity": "sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz",
|
||||
"integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.0.0",
|
||||
"jss": "10.1.1",
|
||||
"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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz",
|
||||
"integrity": "sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz",
|
||||
"integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==",
|
||||
"requires": {
|
||||
"@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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz",
|
||||
"integrity": "sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz",
|
||||
"integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==",
|
||||
"requires": {
|
||||
"@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": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz",
|
||||
"integrity": "sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw==",
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz",
|
||||
"integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"css-vendor": "^2.0.6",
|
||||
"jss": "10.0.0"
|
||||
"css-vendor": "^2.0.7",
|
||||
"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": {
|
||||
|
|
@ -16348,11 +16494,6 @@
|
|||
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
|
||||
"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": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
|
||||
|
|
@ -22170,6 +22311,12 @@
|
|||
"psl": "^1.1.24",
|
||||
"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": {
|
||||
"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
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
|
||||
"dev": true
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz",
|
||||
"integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.0",
|
||||
|
|
@ -25267,7 +25419,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -25295,6 +25448,7 @@
|
|||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -25309,7 +25463,8 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
|
|
@ -25320,7 +25475,8 @@
|
|||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -25437,7 +25593,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
|
@ -25449,6 +25606,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
|
@ -25463,6 +25621,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -25470,12 +25629,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
|
@ -25494,6 +25655,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
|
@ -25583,7 +25745,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -25595,6 +25758,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
|
@ -25680,7 +25844,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -25716,6 +25881,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
@ -25735,6 +25901,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
|
@ -25778,12 +25945,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -25879,6 +26048,14 @@
|
|||
"requires": {
|
||||
"ansi-colors": "^3.0.0",
|
||||
"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": {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
"license": "unlicense",
|
||||
"dependencies": {
|
||||
"@apollo/react-hooks": "^3.1.3",
|
||||
"@material-ui/core": "4.5.0",
|
||||
"@material-ui/icons": "4.4.3",
|
||||
"@material-ui/core": "4.9.8",
|
||||
"@material-ui/icons": "4.9.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.47",
|
||||
"@use-hooks/axios": "1.3.0",
|
||||
"apollo-boost": "^0.4.7",
|
||||
"axios": "0.19.0",
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
"classnames": "2.2.6",
|
||||
"downshift": "3.3.4",
|
||||
"file-saver": "2.0.2",
|
||||
"formik": "2.0.3",
|
||||
"formik": "2.1.4",
|
||||
"fuse.js": "^3.4.6",
|
||||
"graphql": "^14.5.8",
|
||||
"jss-plugin-extend": "^10.0.0",
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
"react-virtualized": "^9.21.2",
|
||||
"sanctuary": "^2.0.1",
|
||||
"slugify": "^1.3.6",
|
||||
"uuid": "^7.0.2",
|
||||
"yup": "0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { ApolloProvider } from '@apollo/react-hooks'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import {
|
||||
StylesProvider,
|
||||
|
|
@ -5,13 +6,12 @@ import {
|
|||
MuiThemeProvider,
|
||||
makeStyles
|
||||
} from '@material-ui/core/styles'
|
||||
import ApolloClient from 'apollo-boost'
|
||||
import { setAutoFreeze } from 'immer'
|
||||
import { create } from 'jss'
|
||||
import extendJss from 'jss-plugin-extend'
|
||||
import React from 'react'
|
||||
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 { tree, Routes } from './routing/routes'
|
||||
|
|
@ -19,8 +19,23 @@ import global from './styling/global'
|
|||
import theme from './styling/theme'
|
||||
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({
|
||||
credentials: 'include',
|
||||
defaultOptions,
|
||||
uri:
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'https://localhost:8070/graphql/'
|
||||
|
|
@ -29,7 +44,7 @@ const client = new ApolloClient({
|
|||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const whyDidYouRender = require('@welldone-software/why-did-you-render')
|
||||
whyDidYouRender(React, { include: [/Logs/] })
|
||||
whyDidYouRender(React)
|
||||
}
|
||||
|
||||
// disable immer autofreeze for performance
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export const ConfirmDialog = memo(
|
|||
autoFocus
|
||||
id="confirm-input"
|
||||
type="text"
|
||||
large
|
||||
size="lg"
|
||||
fullWidth
|
||||
value={value}
|
||||
touched={{}}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import React, { useState, memo } from 'react'
|
||||
|
||||
import { H4 } from 'src/components/typography'
|
||||
import { Link } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
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 TrueIcon } from 'src/styling/icons/table/true.svg'
|
||||
import { ReactComponent as FalseIcon } from 'src/styling/icons/table/false.svg'
|
||||
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||
|
||||
import { booleanPropertiesTableStyles } from './BooleanPropertiesTable.styles'
|
||||
|
||||
|
|
@ -82,7 +81,7 @@ const BooleanPropertiesTable = memo(
|
|||
<TableRow key={idx} size="sm" className={classes.tableRow}>
|
||||
<TableCell className={classes.tableCell}>
|
||||
{element.display}
|
||||
{editing ? (
|
||||
{editing && (
|
||||
<RadioGroup
|
||||
options={radioButtonOptions}
|
||||
value={element.value}
|
||||
|
|
@ -94,11 +93,8 @@ const BooleanPropertiesTable = memo(
|
|||
}
|
||||
className={classes.radioButtons}
|
||||
/>
|
||||
) : element.value ? (
|
||||
<TrueIcon />
|
||||
) : (
|
||||
<FalseIcon />
|
||||
)}
|
||||
{!editing && <BooleanCell value={element.value} />}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { makeStyles } from '@material-ui/core/styles'
|
|||
import classnames from 'classnames'
|
||||
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 { zircon, zircon2, comet, fontColor, white } from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
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,7 +6,8 @@ import styles from './Link.styles'
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const Link = memo(({ submit, className, children, color, size, ...props }) => {
|
||||
const Link = memo(
|
||||
({ submit, className, children, color = 'primary', ...props }) => {
|
||||
const classes = useStyles()
|
||||
const classNames = {
|
||||
[classes.link]: true,
|
||||
|
|
@ -23,6 +24,7 @@ const Link = memo(({ submit, className, children, color, size, ...props }) => {
|
|||
{children}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export default Link
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
white,
|
||||
linkPrimaryColor,
|
||||
linkSecondaryColor
|
||||
} from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { h4 } = typographyStyles
|
||||
|
||||
|
|
@ -24,7 +24,8 @@ export default {
|
|||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
cursor: 'pointer',
|
||||
padding: '0'
|
||||
padding: '0',
|
||||
height: '100%'
|
||||
},
|
||||
primary: {
|
||||
extend: color(linkPrimaryColor)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import AddButton from './AddButton'
|
|||
import Button from './Button'
|
||||
import FeatureButton from './FeatureButton'
|
||||
import IDButton from './IDButton'
|
||||
import IconButton from './IconButton'
|
||||
import Link from './Link'
|
||||
import SimpleButton from './SimpleButton'
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ export {
|
|||
SimpleButton,
|
||||
ActionButton,
|
||||
FeatureButton,
|
||||
IconButton,
|
||||
IDButton,
|
||||
AddButton
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,18 @@ const DataTable = memo(({ elements, data }) => {
|
|||
<>
|
||||
<div>
|
||||
<THead>
|
||||
{elements.map(({ size, className, textAlign, header }, idx) => (
|
||||
{elements.map(
|
||||
({ width, size, className, textAlign, header }, idx) => (
|
||||
<Th
|
||||
key={idx}
|
||||
size={size}
|
||||
width={width}
|
||||
className={className}
|
||||
textAlign={textAlign}>
|
||||
{header}
|
||||
</Th>
|
||||
))}
|
||||
)
|
||||
)}
|
||||
</THead>
|
||||
</div>
|
||||
<div style={{ flex: '1 1 auto' }}>
|
||||
|
|
@ -52,8 +55,8 @@ const DataTable = memo(({ elements, data }) => {
|
|||
{elements.map(
|
||||
(
|
||||
{
|
||||
header,
|
||||
size,
|
||||
width,
|
||||
className,
|
||||
textAlign,
|
||||
view = it => it?.toString()
|
||||
|
|
@ -63,6 +66,7 @@ const DataTable = memo(({ elements, data }) => {
|
|||
<Td
|
||||
key={idx}
|
||||
size={size}
|
||||
width={width}
|
||||
className={className}
|
||||
textAlign={textAlign}>
|
||||
{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 classnames from 'classnames'
|
||||
import { Field, useFormikContext } from 'formik'
|
||||
import React from 'react'
|
||||
|
||||
import { Link } from 'src/components/buttons'
|
||||
import { Td, Tr, CellDoubleLevel } from 'src/components/fake-table/Table'
|
||||
import { TextInputDisplay } from 'src/components/inputs/base/TextInput'
|
||||
import { ReactComponent as DeleteIcon } from 'src/styling/icons/action/delete/enabled.svg'
|
||||
import { Link, IconButton } from 'src/components/buttons'
|
||||
import { Td, Tr } from 'src/components/fake-table/Table'
|
||||
import { TL2 } from 'src/components/typography'
|
||||
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 DisabledEditIcon } from 'src/styling/icons/action/edit/disabled.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 EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
|
||||
|
||||
const styles = {
|
||||
button: {
|
||||
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
|
||||
// }
|
||||
// }
|
||||
}
|
||||
import styles from './Row.styles'
|
||||
import { ACTION_COL_SIZE } from './consts'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const ERow = memo(
|
||||
({ elements, editing, setEditing, disableAction, action }) => {
|
||||
const ActionCol = ({
|
||||
editing,
|
||||
setEditing,
|
||||
enableEdit,
|
||||
disabled,
|
||||
onDelete,
|
||||
enableDelete
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const { values, submitForm, resetForm } = useFormikContext()
|
||||
|
||||
const Cell = ({
|
||||
const actionColSize =
|
||||
enableDelete && enableEdit ? ACTION_COL_SIZE / 2 : ACTION_COL_SIZE
|
||||
|
||||
return (
|
||||
<>
|
||||
{editing && (
|
||||
<Td textAlign="center" width={ACTION_COL_SIZE}>
|
||||
<Link
|
||||
className={classes.cancelButton}
|
||||
color="secondary"
|
||||
onClick={resetForm}>
|
||||
Cancel
|
||||
</Link>
|
||||
<Link color="primary" onClick={submitForm}>
|
||||
Save
|
||||
</Link>
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableEdit && (
|
||||
<Td textAlign="right" width={actionColSize}>
|
||||
<IconButton
|
||||
disabled={disabled}
|
||||
className={classes.editButton}
|
||||
onClick={() => setEditing && setEditing(values.id)}>
|
||||
{disabled ? <DisabledEditIcon /> : <EditIcon />}
|
||||
</IconButton>
|
||||
</Td>
|
||||
)}
|
||||
{!editing && enableDelete && (
|
||||
<Td textAlign="right" width={actionColSize}>
|
||||
<IconButton disabled={disabled} onClick={() => onDelete(values.id)}>
|
||||
{disabled ? <DisabledDeleteIcon /> : <DeleteIcon />}
|
||||
</IconButton>
|
||||
</Td>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ECol = ({ editing, config }) => {
|
||||
const {
|
||||
name,
|
||||
input,
|
||||
type,
|
||||
display,
|
||||
className,
|
||||
size,
|
||||
bold,
|
||||
width,
|
||||
textAlign,
|
||||
inputProps,
|
||||
editing
|
||||
}) => {
|
||||
suffix,
|
||||
view = it => it?.toString(),
|
||||
inputProps = {}
|
||||
} = config
|
||||
|
||||
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 size={size} textAlign={textAlign}>
|
||||
{editing && (
|
||||
<Field
|
||||
id={name}
|
||||
name={name}
|
||||
component={input}
|
||||
className={className}
|
||||
{...inputProps}
|
||||
/>
|
||||
)}
|
||||
{!editing && type === 'text' && (
|
||||
<TextInputDisplay display={display} {...inputProps} />
|
||||
<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 actionCol = R.last(elements)
|
||||
const { values, errors } = useFormikContext()
|
||||
|
||||
const actionColClasses = {
|
||||
[classes.actionCol]: true,
|
||||
[classes.actionColDisplayMode]: !editing,
|
||||
[classes.actionColEditMode]: editing
|
||||
}
|
||||
|
||||
const icon = (action, disabled) => {
|
||||
if (action === 'delete' && !disabled) return <DeleteIcon />
|
||||
if (action === 'delete' && disabled) return <DisabledDeleteIcon />
|
||||
}
|
||||
const ERow = ({
|
||||
elements,
|
||||
enableEdit,
|
||||
enableDelete,
|
||||
onDelete,
|
||||
editing,
|
||||
setEditing,
|
||||
disabled
|
||||
}) => {
|
||||
const { errors } = useFormikContext()
|
||||
|
||||
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}
|
||||
{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}
|
||||
/>
|
||||
// <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>
|
||||
</Tr>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
const ERowWithFormik = memo(
|
||||
({
|
||||
initialValues,
|
||||
validationSchema,
|
||||
save,
|
||||
reset,
|
||||
action,
|
||||
elements,
|
||||
editing,
|
||||
disableAction
|
||||
}) => {
|
||||
return (
|
||||
<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
|
||||
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 React, { useState } from 'react'
|
||||
import { v4 } from 'uuid'
|
||||
|
||||
import {
|
||||
Th,
|
||||
ThDoubleLevel,
|
||||
THead,
|
||||
TBody,
|
||||
Table,
|
||||
TDoubleLevelHead
|
||||
} from 'src/components/fake-table/Table'
|
||||
import { startCase } from 'src/utils/string'
|
||||
import Link from 'src/components/buttons/Link.js'
|
||||
import { AddButton } from 'src/components/buttons/index.js'
|
||||
import { TBody, Table } from 'src/components/fake-table/Table'
|
||||
import { Info2 } from 'src/components/typography'
|
||||
|
||||
import Header from './Header'
|
||||
import ERow from './Row'
|
||||
import styles from './Table.styles'
|
||||
import { DEFAULT_COL_SIZE, ACTION_COL_SIZE } from './consts'
|
||||
|
||||
const ETHead = memo(({ elements, className }) => {
|
||||
const action = R.last(elements)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
return (
|
||||
<THead className={className?.root}>
|
||||
{R.init(elements).map(({ name, size, display, textAlign }, idx) => (
|
||||
<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 getWidth = R.compose(
|
||||
R.reduce(R.add)(0),
|
||||
R.map(it => it.width ?? DEFAULT_COL_SIZE)
|
||||
)
|
||||
})
|
||||
|
||||
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(
|
||||
({
|
||||
const ETable = ({
|
||||
name,
|
||||
title,
|
||||
elements = [],
|
||||
data = [],
|
||||
save,
|
||||
reset,
|
||||
action,
|
||||
initialValues,
|
||||
validationSchema,
|
||||
editing,
|
||||
addingRow,
|
||||
disableAction,
|
||||
className,
|
||||
double
|
||||
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 (
|
||||
<Table className={className}>
|
||||
{!double && <ETHead elements={elements} />}
|
||||
{double && (
|
||||
<ETDoubleHead elements={elements} className={className?.head} />
|
||||
<div className={classes.wrapper}>
|
||||
{showButtonOnEmpty && (
|
||||
<AddButton disabled={!canAdd} onClick={addField}>
|
||||
{createText}
|
||||
</AddButton>
|
||||
)}
|
||||
<TBody>
|
||||
{addingRow && (
|
||||
<ERow
|
||||
{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}
|
||||
initialValues={initialValues}
|
||||
save={save}
|
||||
reset={reset}
|
||||
validationSchema={validationSchema}
|
||||
editing
|
||||
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) => (
|
||||
<ERow
|
||||
key={idx}
|
||||
<Formik
|
||||
key={it.id ?? idx}
|
||||
enableReinitialize
|
||||
initialValues={it}
|
||||
elements={elements}
|
||||
save={save}
|
||||
reset={it => reset(it)}
|
||||
action={action}
|
||||
onReset={onReset}
|
||||
validationSchema={validationSchema}
|
||||
disableAction={disableAction}
|
||||
editing={editing[idx]}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
} 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 ExpandOpenIcon } from 'src/styling/icons/action/expand/open.svg'
|
||||
import { mainWidth } from 'src/styling/variables'
|
||||
import { THead, Tr, Td, Th } from 'src/components/fake-table/Table'
|
||||
|
||||
const styles = {
|
||||
expandButton: {
|
||||
|
|
@ -48,25 +48,19 @@ const ExpRow = ({
|
|||
.slice(0, -1)
|
||||
.map(
|
||||
(
|
||||
{
|
||||
header,
|
||||
size,
|
||||
className,
|
||||
textAlign,
|
||||
view = it => it?.toString()
|
||||
},
|
||||
{ width, className, textAlign, view = it => it?.toString() },
|
||||
idx
|
||||
) => (
|
||||
<Td
|
||||
key={idx}
|
||||
size={size}
|
||||
width={width}
|
||||
className={className}
|
||||
textAlign={textAlign}>
|
||||
{view(data)}
|
||||
</Td>
|
||||
)
|
||||
)}
|
||||
<Td size={elements[elements.length - 1].size}>
|
||||
<Td width={elements[elements.length - 1].width}>
|
||||
<button
|
||||
onClick={() => expandRow(id)}
|
||||
className={classes.expandButton}>
|
||||
|
|
@ -77,7 +71,7 @@ const ExpRow = ({
|
|||
</Tr>
|
||||
{expanded && (
|
||||
<Tr className={classes.detailsRow}>
|
||||
<Td size={mainWidth}>
|
||||
<Td width={mainWidth}>
|
||||
<Details it={data} />
|
||||
</Td>
|
||||
</Tr>
|
||||
|
|
@ -86,8 +80,8 @@ const ExpRow = ({
|
|||
)
|
||||
}
|
||||
|
||||
/* rows = [{ columns = [{ name, value, className, textAlign, size }], details, className, error, errorMessage }]
|
||||
* Don't forget to include the size of the last (expand button) column!
|
||||
/* rows = [{ columns = [{ name, value, className, textAlign, width }], details, className, error, errorMessage }]
|
||||
* Don't forget to include the width of the last (expand button) column!
|
||||
*/
|
||||
const ExpTable = ({
|
||||
elements = [],
|
||||
|
|
@ -133,10 +127,10 @@ const ExpTable = ({
|
|||
<>
|
||||
<div>
|
||||
<THead>
|
||||
{elements.map(({ size, className, textAlign, header }, idx) => (
|
||||
{elements.map(({ width, className, textAlign, header }, idx) => (
|
||||
<Th
|
||||
key={idx}
|
||||
size={size}
|
||||
width={width}
|
||||
className={className}
|
||||
textAlign={textAlign}>
|
||||
{header}
|
||||
|
|
|
|||
|
|
@ -5,103 +5,10 @@ import classnames from 'classnames'
|
|||
import React from 'react'
|
||||
|
||||
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({
|
||||
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 useStyles = makeStyles(styles)
|
||||
|
||||
const Table = ({ children, className, ...props }) => (
|
||||
<div className={classnames(className)} {...props}>
|
||||
|
|
@ -129,21 +36,25 @@ const TBody = ({ children, className }) => {
|
|||
return <div className={classnames(className, classes.body)}>{children}</div>
|
||||
}
|
||||
|
||||
const Td = ({ children, header, className, size = 100, textAlign, action }) => {
|
||||
const classes = useStyles()
|
||||
const Td = ({
|
||||
children,
|
||||
header,
|
||||
className,
|
||||
width = 100,
|
||||
size,
|
||||
textAlign,
|
||||
action
|
||||
}) => {
|
||||
const classes = useStyles({ textAlign, width })
|
||||
const classNames = {
|
||||
[classes.td]: true,
|
||||
[classes.tdHeader]: header,
|
||||
[classes.actionCol]: action
|
||||
[classes.actionCol]: action,
|
||||
[classes.large]: size === 'lg' && !header,
|
||||
[classes.md]: size === 'md' && !header
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(className, classNames)}
|
||||
style={{ width: size, textAlign }}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
return <div className={classnames(className, classNames)}>{children}</div>
|
||||
}
|
||||
|
||||
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 classes = useStyles()
|
||||
|
||||
// const { name, onChange, value } = props.field
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
id={name}
|
||||
|
|
|
|||
|
|
@ -1,105 +1,12 @@
|
|||
import React, { memo } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
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 {
|
||||
fontColor,
|
||||
offColor,
|
||||
secondaryColor,
|
||||
inputFontSize,
|
||||
inputFontSizeLg,
|
||||
inputFontWeight,
|
||||
inputFontWeightLg
|
||||
} from 'src/styling/variables'
|
||||
import { TL2, Label2, Info1, Info2 } from 'src/components/typography'
|
||||
import styles from './TextInput.styles'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
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 useStyles = makeStyles(styles)
|
||||
|
||||
const TextInput = memo(
|
||||
({
|
||||
|
|
@ -109,22 +16,23 @@ const TextInput = memo(
|
|||
value,
|
||||
error,
|
||||
suffix,
|
||||
large,
|
||||
textAlign,
|
||||
width,
|
||||
// lg or sm
|
||||
size,
|
||||
bold,
|
||||
className,
|
||||
InputProps,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
const classes = useStyles({ textAlign, width, size })
|
||||
const filled = !error && value && !R.isEmpty(value)
|
||||
|
||||
const classNames = {
|
||||
[className]: true,
|
||||
[classes.filled]: !error && value,
|
||||
[classes.empty]: !value || value === ''
|
||||
const inputClasses = {
|
||||
[classes.bold]: bold
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<span>
|
||||
<TextField
|
||||
id={name}
|
||||
onChange={onChange}
|
||||
|
|
@ -132,24 +40,19 @@ const TextInput = memo(
|
|||
error={error}
|
||||
value={value}
|
||||
classes={{ root: classes.root }}
|
||||
className={classnames(classNames)}
|
||||
className={className}
|
||||
InputProps={{
|
||||
className: large ? classes.inputRootLg : classes.inputRoot,
|
||||
className: classnames(inputClasses),
|
||||
classes: {
|
||||
root: classes.size,
|
||||
underline: filled ? classes.underline : null
|
||||
},
|
||||
...InputProps
|
||||
}}
|
||||
InputLabelProps={{ className: classes.labelRoot }}
|
||||
{...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 { TextInput } from './TextInput'
|
||||
import Switch from './Switch'
|
||||
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'
|
||||
|
||||
const CheckboxInput = memo(({ label, ...props }) => {
|
||||
const CheckboxInput = memo(({ label, textAlign, fullWidth, ...props }) => {
|
||||
const { name, onChange, value } = props.field
|
||||
|
||||
return <Checkbox name={name} onChange={onChange} value={value} {...props} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import typographyStyles from 'src/components/typography/styles'
|
||||
import {
|
||||
fontColor,
|
||||
offColor,
|
||||
inputFontSize,
|
||||
inputFontWeight
|
||||
} from 'src/styling/variables'
|
||||
import typographyStyles from 'src/components/typography/styles'
|
||||
|
||||
const { info3 } = typographyStyles
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,8 @@
|
|||
import Autocomplete from './autocomplete/Autocomplete'
|
||||
import AutocompleteMultiple from './autocomplete/AutocompleteMultiple'
|
||||
import Checkbox from './base/Checkbox'
|
||||
import Radio from './base/Radio'
|
||||
import RadioGroup from './base/RadioGroup'
|
||||
import Select from './base/Select'
|
||||
import Switch from './base/Switch'
|
||||
import { TextInput } from './base/TextInput'
|
||||
import TextInput from './base/TextInput'
|
||||
|
||||
export {
|
||||
Autocomplete,
|
||||
AutocompleteMultiple,
|
||||
TextInput,
|
||||
Radio,
|
||||
Checkbox,
|
||||
Switch,
|
||||
Select,
|
||||
RadioGroup
|
||||
}
|
||||
export { 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 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 EditIcon } from 'src/styling/icons/action/edit/white.svg'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import {
|
||||
offColor,
|
||||
|
|
@ -11,8 +13,6 @@ import {
|
|||
tableNewDisabledHeaderColor,
|
||||
secondaryColorDarker
|
||||
} 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
|
||||
|
||||
|
|
@ -106,11 +106,11 @@ const SingleRowTable = ({
|
|||
{items && (
|
||||
<Table className={classnames(className, classes.wrapper)}>
|
||||
<THead className={classnames(headerClasses)}>
|
||||
<Th size={width - editButtonSize}>
|
||||
<Th width={width - editButtonSize}>
|
||||
{title}
|
||||
{newService && <span className={classes.spanNew}>New</span>}
|
||||
</Th>
|
||||
<Th size={editButtonSize} className={classes.buttonTh}>
|
||||
<Th width={editButtonSize} className={classes.buttonTh}>
|
||||
{!disabled && (
|
||||
<button className={classes.editButton} onClick={onEdit}>
|
||||
<EditIcon />
|
||||
|
|
@ -124,7 +124,7 @@ const SingleRowTable = ({
|
|||
</Th>
|
||||
</THead>
|
||||
<TBody className={classnames(bodyClasses)}>
|
||||
<Td size={width}>
|
||||
<Td width={width}>
|
||||
{!disabled && (
|
||||
<>
|
||||
{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 React from 'react'
|
||||
|
||||
import { useLocation, useHistory } from 'react-router-dom'
|
||||
|
||||
const useQuery = () => new URLSearchParams(useLocation().search)
|
||||
const url =
|
||||
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
|
||||
|
||||
const AuthRegister = () => {
|
||||
const history = useHistory()
|
||||
const query = useQuery()
|
||||
|
||||
useAxios({
|
||||
url: `/api/register?otp=${query.get('otp')}`,
|
||||
url: `${url}/api/register?otp=${query.get('otp')}`,
|
||||
method: 'GET',
|
||||
options: {
|
||||
withCredentials: true
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { parsePhoneNumberFromString } from 'libphonenumber-js'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
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 { DataTable } from 'src/components/dataTable'
|
||||
|
|
@ -38,34 +38,34 @@ const Customers = () => {
|
|||
const elements = [
|
||||
{
|
||||
header: 'Name',
|
||||
size: 277,
|
||||
width: 277,
|
||||
view: R.path(['name'])
|
||||
},
|
||||
{
|
||||
header: 'Phone',
|
||||
size: 166,
|
||||
width: 166,
|
||||
view: it => parsePhoneNumberFromString(it.phone).formatInternational()
|
||||
},
|
||||
{
|
||||
header: 'Total TXs',
|
||||
size: 174,
|
||||
width: 174,
|
||||
textAlign: 'right',
|
||||
view: it => `${Number.parseInt(it.totalTxs)}`
|
||||
},
|
||||
{
|
||||
header: 'Total spent',
|
||||
size: 188,
|
||||
width: 188,
|
||||
textAlign: 'right',
|
||||
view: it => `${Number.parseFloat(it.totalSpent)} ${it.lastTxFiatCode}`
|
||||
},
|
||||
{
|
||||
header: 'Last active',
|
||||
size: 197,
|
||||
width: 197,
|
||||
view: it => moment.utc(it.lastActive).format('YYYY-MM-D')
|
||||
},
|
||||
{
|
||||
header: 'Last transaction',
|
||||
size: 198,
|
||||
width: 198,
|
||||
textAlign: 'right',
|
||||
view: it => (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { gql } from 'apollo-boost'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classnames from 'classnames'
|
||||
import moment from 'moment'
|
||||
import QRCode from 'qrcode.react'
|
||||
import React, { useState } from 'react'
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
import Sidebar from 'src/components/Sidebar'
|
||||
import TableLabel from 'src/components/TableLabel'
|
||||
|
|
@ -200,22 +200,22 @@ const Funding = () => {
|
|||
/>
|
||||
<Table className={classes.table}>
|
||||
<THead>
|
||||
<Td header size={sizes.big}>
|
||||
<Td header width={sizes.big}>
|
||||
Amount Entered
|
||||
</Td>
|
||||
<Td header size={sizes.big}>
|
||||
<Td header width={sizes.big}>
|
||||
Balance After
|
||||
</Td>
|
||||
<Td header size={sizes.big}>
|
||||
<Td header width={sizes.big}>
|
||||
Cash Value
|
||||
</Td>
|
||||
<Td header size={sizes.date}>
|
||||
<Td header width={sizes.date}>
|
||||
Date
|
||||
</Td>
|
||||
<Td header size={sizes.time}>
|
||||
<Td header width={sizes.time}>
|
||||
Time (h:m:s)
|
||||
</Td>
|
||||
<Td header size={sizes.big}>
|
||||
<Td header width={sizes.big}>
|
||||
Performed By
|
||||
</Td>
|
||||
</THead>
|
||||
|
|
@ -224,22 +224,22 @@ const Funding = () => {
|
|||
<Tr
|
||||
key={idx}
|
||||
className={classnames({ [classes.pending]: it.pending })}>
|
||||
<Td size={sizes.big}>
|
||||
<Td width={sizes.big}>
|
||||
{it.cryptoAmount} {selected.cryptoCode}
|
||||
</Td>
|
||||
<Td size={sizes.big}>
|
||||
<Td width={sizes.big}>
|
||||
{it.balance} {selected.cryptoCode}
|
||||
</Td>
|
||||
<Td size={sizes.big}>
|
||||
<Td width={sizes.big}>
|
||||
{it.fiatValue} {selected.fiatCode}
|
||||
</Td>
|
||||
<Td size={sizes.date}>
|
||||
<Td width={sizes.date}>
|
||||
{moment(it.date).format('YYYY-MM-DD')}
|
||||
</Td>
|
||||
<Td size={sizes.time}>
|
||||
<Td width={sizes.time}>
|
||||
{moment(it.date).format('hh:mm:ss')}
|
||||
</Td>
|
||||
<Td size={sizes.big}>add</Td>
|
||||
<Td width={sizes.big}>add</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</TBody>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { gql } from 'apollo-boost'
|
||||
import React, { memo } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import Subtitle from 'src/components/Subtitle'
|
||||
import Title from 'src/components/Title'
|
||||
|
|
|
|||
|
|
@ -20,41 +20,48 @@ const MainForm = memo(({ value, save, auxData, validationSchema }) => {
|
|||
|
||||
return (
|
||||
<EditableTable
|
||||
name="locale"
|
||||
title="Default Settings"
|
||||
altTitleColor
|
||||
initialValues={{ country: null }}
|
||||
enableEdit
|
||||
onDelete={() => {}}
|
||||
setEditing={() => {}}
|
||||
save={save}
|
||||
validationSchema={validationSchema}
|
||||
data={R.of(value)}
|
||||
elements={[
|
||||
{
|
||||
name: 'country',
|
||||
size: sizes.country,
|
||||
width: sizes.country,
|
||||
view: R.path(['display']),
|
||||
input: Autocomplete,
|
||||
inputProps: { suggestions: getData(['countries']) }
|
||||
},
|
||||
{
|
||||
name: 'fiatCurrency',
|
||||
size: sizes.fiatCurrency,
|
||||
width: sizes.fiatCurrency,
|
||||
view: R.path(['code']),
|
||||
input: Autocomplete,
|
||||
inputProps: { suggestions: getData(['currencies']) }
|
||||
},
|
||||
{
|
||||
name: 'languages',
|
||||
size: sizes.languages,
|
||||
width: sizes.languages,
|
||||
view: displayCodeArray,
|
||||
input: AutocompleteMultiple,
|
||||
inputProps: { suggestions: getData(['languages']) }
|
||||
},
|
||||
{
|
||||
name: 'cryptoCurrencies',
|
||||
size: sizes.cryptoCurrencies,
|
||||
width: sizes.cryptoCurrencies,
|
||||
view: displayCodeArray,
|
||||
input: AutocompleteMultiple,
|
||||
inputProps: { suggestions: getData(['cryptoCurrencies']) }
|
||||
},
|
||||
{
|
||||
name: 'showRates',
|
||||
size: sizes.showRates,
|
||||
width: sizes.showRates,
|
||||
textAlign: 'center',
|
||||
view: it => (it ? 'true' : 'false'),
|
||||
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 { gql } from 'apollo-boost'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { TL1 } from 'src/components/typography'
|
||||
import Title from 'src/components/Title'
|
||||
import ErrorMessage from 'src/components/ErrorMessage'
|
||||
import commonStyles from 'src/pages/common.styles'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import { fromServer, toServer } from 'src/utils/config'
|
||||
|
||||
import { localStyles } from './Notifications.styles'
|
||||
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'
|
||||
import Section from '../../components/layout/Section'
|
||||
|
||||
const fiatBalanceAlertsInitialValues = {
|
||||
[CASH_IN_FULL_KEY]: {
|
||||
[PERCENTAGE_KEY]: '',
|
||||
[NUMERARY_KEY]: ''
|
||||
},
|
||||
[CASH_OUT_EMPTY_KEY]: {
|
||||
[CASSETTE_1_KEY]: '',
|
||||
[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
|
||||
}
|
||||
import NotificationsCtx from './NotificationsContext'
|
||||
import CryptoBalanceAlerts from './sections/CryptoBalanceAlerts'
|
||||
import CryptoBalanceOverrides from './sections/CryptoBalanceOverrides'
|
||||
import FiatBalanceAlerts from './sections/FiatBalanceAlerts'
|
||||
import FiatBalanceOverrides from './sections/FiatBalanceOverrides'
|
||||
import Setup from './sections/Setup'
|
||||
import TransactionAlerts from './sections/TransactionAlerts'
|
||||
|
||||
const GET_INFO = gql`
|
||||
{
|
||||
query getData {
|
||||
config
|
||||
machines {
|
||||
name
|
||||
deviceId
|
||||
}
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
|
|
@ -107,125 +36,81 @@ const SAVE_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const styles = R.merge(commonStyles, localStyles)
|
||||
|
||||
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 Notifications = ({ name: SCREEN_KEY }) => {
|
||||
const [section, setSection] = 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, {
|
||||
onCompleted: data => {
|
||||
const { notifications } = data.saveConfig
|
||||
setState(notifications)
|
||||
setEditingState(R.map(x => false, editingState))
|
||||
setTryingToSave(null)
|
||||
refetchQueries: ['getData'],
|
||||
onCompleted: () => setEditingKey(null),
|
||||
onError: error => setError({ error })
|
||||
})
|
||||
|
||||
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)
|
||||
},
|
||||
onError: e => {
|
||||
setError({ section: tryingToSave, error: e })
|
||||
}
|
||||
})
|
||||
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 } } })
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
const handleEditingClick = (key, state) => {
|
||||
setEditingState(R.merge(editingState, { [key]: state }))
|
||||
const setEditing = (key, state) => {
|
||||
if (!state) {
|
||||
setError(null)
|
||||
}
|
||||
setEditingKey(state ? key : null)
|
||||
}
|
||||
|
||||
const curriedSave = R.curry((key, values) => {
|
||||
setTryingToSave(key)
|
||||
save(R.mergeDeepRight(state)({ [key]: values }))
|
||||
})
|
||||
const isEditing = key => editingKey === key
|
||||
const isDisabled = key => editingKey && editingKey !== key
|
||||
|
||||
if (!state) return null
|
||||
const contextValue = {
|
||||
save,
|
||||
error,
|
||||
editingKey,
|
||||
data: config,
|
||||
currency,
|
||||
isEditing,
|
||||
isDisabled,
|
||||
setEditing,
|
||||
setSection,
|
||||
machines,
|
||||
cryptoCurrencies
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={classes.titleWrapper}>
|
||||
<div className={classes.titleAndButtonsContainer}>
|
||||
<Title>Notifications</Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.section}>
|
||||
<SectionHeader error={error?.section === SETUP_KEY}>
|
||||
Setup
|
||||
</SectionHeader>
|
||||
<Setup values={state.setup} save={curriedSave(SETUP_KEY)} />
|
||||
</div>
|
||||
<div className={classes.section}>
|
||||
<SectionHeader error={error?.section === TRANSACTION_ALERTS_KEY}>
|
||||
Transaction alerts
|
||||
</SectionHeader>
|
||||
<TransactionAlerts
|
||||
value={state[TRANSACTION_ALERTS_KEY]}
|
||||
editingState={editingState}
|
||||
handleEditingClick={handleEditingClick}
|
||||
save={curriedSave(TRANSACTION_ALERTS_KEY)}
|
||||
setError={setError}
|
||||
/>
|
||||
</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>
|
||||
</>
|
||||
<NotificationsCtx.Provider value={contextValue}>
|
||||
<TitleSection title="Notifications" />
|
||||
|
||||
<Section title="Setup" error={error && !section}>
|
||||
<Setup />
|
||||
</Section>
|
||||
|
||||
<Section title="Transaction alerts" error={error && section === 'tx'}>
|
||||
<TransactionAlerts section="tx" />
|
||||
</Section>
|
||||
|
||||
<Section title="Fiat balance alerts" error={error && section === 'fiat'}>
|
||||
<FiatBalanceAlerts section="fiat" />
|
||||
<FiatBalanceOverrides section="fiat" />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Crypto balance alerts"
|
||||
error={error && section === 'crypto'}>
|
||||
<CryptoBalanceAlerts section="crypto" />
|
||||
<CryptoBalanceOverrides section="crypto" />
|
||||
</Section>
|
||||
</NotificationsCtx.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,252 +1,20 @@
|
|||
import { offColor, primaryColor } from 'src/styling/variables'
|
||||
import theme from 'src/styling/theme'
|
||||
|
||||
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': {
|
||||
export default {
|
||||
cryptoBalanceAlerts: {
|
||||
display: 'flex',
|
||||
marginBottom: 36,
|
||||
height: 135,
|
||||
alignItems: 'center'
|
||||
},
|
||||
'& > div:first-child': {
|
||||
borderRight: [['solid', 1, primaryColor]]
|
||||
cryptoBalanceAlertsForm: {
|
||||
width: 222,
|
||||
marginRight: 32
|
||||
},
|
||||
'& > div:not(:first-child)': {
|
||||
marginLeft: 56
|
||||
}
|
||||
cryptoBalanceAlertsSecondForm: {
|
||||
marginLeft: 50
|
||||
},
|
||||
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
|
||||
vertSeparator: {
|
||||
width: 1,
|
||||
height: '100%',
|
||||
borderRight: [[1, 'solid', 'black']]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
alignItems: 'center'
|
||||
}
|
||||
}
|
||||
|
||||
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 * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import Sidebar from 'src/components/Sidebar'
|
||||
import Title from 'src/components/Title'
|
||||
|
|
@ -53,7 +53,6 @@ const OperatorInfo = () => {
|
|||
{isSelected(CONTACT_INFORMATION) && <ContactInfo />}
|
||||
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
||||
{isSelected(COIN_ATM_RADAR) && <CoinAtmRadar />}
|
||||
{isSelected(TERMS_CONDITIONS) && <TermsConditions />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
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 Title from 'src/components/Title'
|
||||
import Uptime from 'src/components/Uptime'
|
||||
|
|
@ -20,6 +19,7 @@ import {
|
|||
TableCell
|
||||
} from 'src/components/table'
|
||||
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 Download } from 'src/styling/icons/button/download/zodiac.svg'
|
||||
import { offColor } from 'src/styling/variables'
|
||||
|
|
|
|||
|
|
@ -70,29 +70,29 @@ const Transactions = () => {
|
|||
const elements = [
|
||||
{
|
||||
header: '',
|
||||
size: 62,
|
||||
width: 62,
|
||||
view: it => (it.txClass === 'cashOut' ? <TxOutIcon /> : <TxInIcon />)
|
||||
},
|
||||
{
|
||||
header: 'Machine',
|
||||
name: 'machineName',
|
||||
size: 180,
|
||||
width: 180,
|
||||
view: R.path(['machineName'])
|
||||
},
|
||||
{
|
||||
header: 'Customer',
|
||||
size: 162,
|
||||
width: 162,
|
||||
view: getCustomerDisplayName
|
||||
},
|
||||
{
|
||||
header: 'Cash',
|
||||
size: 110,
|
||||
width: 110,
|
||||
textAlign: 'right',
|
||||
view: it => `${Number.parseFloat(it.fiat)} ${it.fiatCode}`
|
||||
},
|
||||
{
|
||||
header: 'Crypto',
|
||||
size: 141,
|
||||
width: 141,
|
||||
textAlign: 'right',
|
||||
view: it =>
|
||||
`${toUnit(new BigNumber(it.cryptoAtoms), it.cryptoCode).toFormat(5)} ${
|
||||
|
|
@ -103,27 +103,27 @@ const Transactions = () => {
|
|||
header: 'Address',
|
||||
view: R.path(['toAddress']),
|
||||
className: classes.overflowTd,
|
||||
size: 136
|
||||
width: 136
|
||||
},
|
||||
{
|
||||
header: 'Date (UTC)',
|
||||
view: it => moment.utc(it.created).format('YYYY-MM-D'),
|
||||
textAlign: 'right',
|
||||
size: 124
|
||||
width: 124
|
||||
},
|
||||
{
|
||||
header: 'Time (UTC)',
|
||||
view: it => moment.utc(it.created).format('HH:mm:ss'),
|
||||
textAlign: 'right',
|
||||
size: 124
|
||||
width: 124
|
||||
},
|
||||
{
|
||||
header: '', // Trade
|
||||
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 { makeStyles } from '@material-ui/core'
|
||||
import { gql } from 'apollo-boost'
|
||||
import moment from 'moment'
|
||||
import * as R from 'ramda'
|
||||
import React from 'react'
|
||||
|
||||
import ExpTable from '../../components/expandable-table/ExpTable'
|
||||
import { MainStatus } from '../../components/Status'
|
||||
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 ErrorIcon } from '../../styling/icons/status/tomato.svg'
|
||||
import { mainStyles } from '../Transactions/Transactions.styles'
|
||||
|
|
@ -41,36 +41,36 @@ const MachineStatus = () => {
|
|||
const elements = [
|
||||
{
|
||||
header: 'Machine Name',
|
||||
size: 232,
|
||||
width: 232,
|
||||
textAlign: 'left',
|
||||
view: m => m.name
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
size: 349,
|
||||
width: 349,
|
||||
textAlign: 'left',
|
||||
view: m => <MainStatus statuses={m.statuses} />
|
||||
},
|
||||
{
|
||||
header: 'Last ping',
|
||||
size: 192,
|
||||
width: 192,
|
||||
textAlign: 'left',
|
||||
view: m => moment(m.lastPing).fromNow()
|
||||
},
|
||||
{
|
||||
header: 'Ping Time',
|
||||
size: 155,
|
||||
width: 155,
|
||||
textAlign: 'left',
|
||||
view: m => m.pingTime || 'unknown'
|
||||
},
|
||||
{
|
||||
header: 'Software Version',
|
||||
size: 201,
|
||||
width: 201,
|
||||
textAlign: 'left',
|
||||
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'
|
||||
|
||||
const tree = [
|
||||
{ key: 'transactions', label: 'Transactions', route: '/transactions' },
|
||||
// maintenence: { label: 'Maintenence', children: [{ label: 'Locale', route: '/locale' }] },
|
||||
// analytics: { label: 'Analytics', children: [{ label: 'Locale', route: '/locale' }] },
|
||||
{
|
||||
key: 'transactions',
|
||||
label: 'Transactions',
|
||||
route: '/transactions',
|
||||
component: Transactions
|
||||
},
|
||||
{
|
||||
key: 'maintenance',
|
||||
label: 'Maintenance',
|
||||
route: '/maintenance',
|
||||
get component() {
|
||||
return () => <Redirect to={this.children[0].route} />
|
||||
},
|
||||
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',
|
||||
label: 'Server',
|
||||
route: '/maintenance/server-logs'
|
||||
route: '/maintenance/server-logs',
|
||||
component: ServerLogs
|
||||
},
|
||||
{
|
||||
key: 'machine-status',
|
||||
label: 'Machine Status',
|
||||
route: '/maintenance/machine-status'
|
||||
route: '/maintenance/machine-status',
|
||||
component: MachineStatus
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -42,87 +60,74 @@ const tree = [
|
|||
key: 'settings',
|
||||
label: 'Settings',
|
||||
route: '/settings',
|
||||
get component() {
|
||||
return () => <Redirect to={this.children[0].route} />
|
||||
},
|
||||
children: [
|
||||
{
|
||||
key: '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',
|
||||
label: '3rd party services',
|
||||
route: '/settings/3rd-party-services'
|
||||
route: '/settings/3rd-party-services',
|
||||
component: Services
|
||||
},
|
||||
{
|
||||
key: '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',
|
||||
label: 'Compliance',
|
||||
route: '/compliance',
|
||||
get component() {
|
||||
return () => <Redirect to={this.children[0].route} />
|
||||
},
|
||||
children: [
|
||||
// {
|
||||
// key: 'triggers',
|
||||
// label: 'Triggers',
|
||||
// route: '/compliance/triggers'
|
||||
// },
|
||||
{
|
||||
key: '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 getRoute = R.path(['children', 0, 'route'])
|
||||
const withKey = R.find(R.propEq('key', key))
|
||||
return R.compose(getRoute, withKey)(tree)
|
||||
}
|
||||
const map = R.map(R.when(R.has('children'), R.prop('children')))
|
||||
const leafRoutes = R.compose(R.flatten, map)(tree)
|
||||
const parentRoutes = R.filter(R.has('children'))(tree)
|
||||
const flattened = R.concat(leafRoutes, parentRoutes)
|
||||
|
||||
const Routes = () => (
|
||||
<Switch>
|
||||
<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="/maintenance/machine-status" component={MachineStatus} />
|
||||
<Route path="/compliance/customers" component={Customers} />
|
||||
{flattened.map(({ route, component: Page, key }) => (
|
||||
<Route path={route} key={key}>
|
||||
<Page name={key} />
|
||||
</Route>
|
||||
))}
|
||||
</Switch>
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import { create } from 'jss'
|
|||
import extendJss from 'jss-plugin-extend'
|
||||
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 { 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 {
|
||||
|
|
@ -29,7 +29,11 @@ import {
|
|||
Info2,
|
||||
Mono
|
||||
} from '../components/typography'
|
||||
import { inputFontFamily, secondaryColor } from '../styling/variables'
|
||||
import {
|
||||
inputFontFamily,
|
||||
secondaryColor,
|
||||
fontColor
|
||||
} from '../styling/variables'
|
||||
|
||||
const jss = create({
|
||||
plugins: [extendJss(), ...jssPreset().plugins]
|
||||
|
|
@ -78,6 +82,19 @@ const theme = createMuiTheme({
|
|||
dark: 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 =>
|
||||
`@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 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">
|
||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
||||
<title>icon/action/delete/disabled</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<svg
|
||||
width="22"
|
||||
height="22"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<rect id="path-1" x="0" y="0" width="22" height="22"></rect>
|
||||
<path id="a" d="M0 0h22v22H0z" />
|
||||
</defs>
|
||||
<g id="icon/action/delete/disabled" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<mask id="b" fill="#fff">
|
||||
<use xlink:href="#a" />
|
||||
</mask>
|
||||
<g id="Background"></g>
|
||||
<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>
|
||||
<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>
|
||||
<g id="Group-9" mask="url(#mask-2)" stroke="#9B9B9B" stroke-linecap="round" stroke-width="2">
|
||||
<g transform="translate(1.000000, 1.000000)">
|
||||
<polyline id="Stroke-3" stroke-linejoin="round" points="2 5 4 20 16 20 18 5"></polyline>
|
||||
<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>
|
||||
<path
|
||||
stroke="#9B9B9B"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
mask="url(#b)"
|
||||
d="M9 9v9M13 9v9"
|
||||
/>
|
||||
<g
|
||||
mask="url(#b)"
|
||||
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>
|
||||
</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 { backgroundColor, inputFontFamily, secondaryColor } from './variables'
|
||||
import {
|
||||
backgroundColor,
|
||||
inputFontFamily,
|
||||
secondaryColor,
|
||||
fontColor,
|
||||
offColor,
|
||||
subheaderColor,
|
||||
fontSize5
|
||||
} from './variables'
|
||||
|
||||
export default createMuiTheme({
|
||||
typography: {
|
||||
|
|
@ -23,5 +31,69 @@ export default createMuiTheme({
|
|||
background: {
|
||||
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 inputFontSizeSm = fontSize4
|
||||
const inputFontSize = fontSize3
|
||||
const inputFontSizeLg = fontSize1
|
||||
const inputFontWeight = 500
|
||||
const inputFontWeightLg = 700
|
||||
const inputFontWeightBold = 700
|
||||
const inputFontFamily = fontSecondary
|
||||
|
||||
// Breakpoints
|
||||
|
|
@ -159,10 +160,11 @@ export {
|
|||
// named font sizes
|
||||
smallestFontSize,
|
||||
inputFontSize,
|
||||
inputFontSizeSm,
|
||||
inputFontSizeLg,
|
||||
inputFontFamily,
|
||||
inputFontWeight,
|
||||
inputFontWeightLg,
|
||||
inputFontWeightBold,
|
||||
// screen sizes
|
||||
sm,
|
||||
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