style: add eslint configuration and fixed linter issues (#35)
* style: add eslint configuration as per bee-js * chore: add `plugin:react/reocommended` in `.eslintrc` Co-authored-by: nugaon <50576770+nugaon@users.noreply.github.com> * chore: add `consistent` to `array-bracket-newline` as per review * style: after automatic fixes with `npm run lint` * style: fixed all linter errors * refactor: fixed all linter warnings * chore: added missing new line at end of `.prettierrc` file Co-authored-by: nugaon <50576770+nugaon@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"plugin:react/recommended"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true,
|
||||||
|
"jest": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2018
|
||||||
|
},
|
||||||
|
"plugins": ["jest"],
|
||||||
|
"rules": {
|
||||||
|
"array-bracket-newline": ["error", "consistent"],
|
||||||
|
"strict": ["error", "safe"],
|
||||||
|
"block-scoped-var": "error",
|
||||||
|
"complexity": "warn",
|
||||||
|
"default-case": "error",
|
||||||
|
"dot-notation": "warn",
|
||||||
|
"eqeqeq": "error",
|
||||||
|
"guard-for-in": "warn",
|
||||||
|
"linebreak-style": ["warn", "unix"],
|
||||||
|
"no-alert": "error",
|
||||||
|
"no-case-declarations": "error",
|
||||||
|
"no-console": "error",
|
||||||
|
"no-constant-condition": "error",
|
||||||
|
"no-continue": "warn",
|
||||||
|
"no-div-regex": "error",
|
||||||
|
"no-empty": "warn",
|
||||||
|
"no-empty-pattern": "error",
|
||||||
|
"no-implicit-coercion": "error",
|
||||||
|
"prefer-arrow-callback": "warn",
|
||||||
|
"no-labels": "error",
|
||||||
|
"no-loop-func": "error",
|
||||||
|
"no-nested-ternary": "warn",
|
||||||
|
"no-script-url": "error",
|
||||||
|
"quote-props": ["error", "as-needed"],
|
||||||
|
"require-yield": "error",
|
||||||
|
"max-nested-callbacks": ["error", 4],
|
||||||
|
"max-depth": ["error", 4],
|
||||||
|
"require-await": "error",
|
||||||
|
"space-before-function-paren": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"anonymous": "never",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"padding-line-between-statements": [
|
||||||
|
"error",
|
||||||
|
{ "blankLine": "always", "prev": "*", "next": "if" },
|
||||||
|
{ "blankLine": "always", "prev": "*", "next": "function" },
|
||||||
|
{ "blankLine": "always", "prev": "*", "next": "return" }
|
||||||
|
],
|
||||||
|
"no-useless-constructor": "off",
|
||||||
|
"no-dupe-class-members": "off",
|
||||||
|
"no-unused-expressions": "off",
|
||||||
|
"curly": ["error", "multi-line"],
|
||||||
|
"object-curly-spacing": ["error", "always"],
|
||||||
|
"comma-dangle": ["error", "always-multiline"],
|
||||||
|
"@typescript-eslint/no-useless-constructor": "error",
|
||||||
|
"@typescript-eslint/no-unused-expressions": "error",
|
||||||
|
"@typescript-eslint/member-delimiter-style": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"multiline": {
|
||||||
|
"delimiter": "none",
|
||||||
|
"requireLast": true
|
||||||
|
},
|
||||||
|
"singleline": {
|
||||||
|
"delimiter": "semi",
|
||||||
|
"requireLast": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"react/react-in-jsx-scope": "off" // React v17+ does not need React in scope for JSX elements
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.spec.ts", "*.spec.tsx", "*.test.ts", "*.test.tsx"],
|
||||||
|
"rules": {
|
||||||
|
"max-nested-callbacks": ["error", 10] // allow describe/it/test nesting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"trailingComma": "all",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"proseWrap": "always"
|
||||||
|
}
|
||||||
Generated
+248
-70
@@ -37,6 +37,12 @@
|
|||||||
"@types/react-copy-to-clipboard": "^5.0.0",
|
"@types/react-copy-to-clipboard": "^5.0.0",
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/react-syntax-highlighter": "^13.5.0",
|
"@types/react-syntax-highlighter": "^13.5.0",
|
||||||
|
"eslint": "^7.23.0",
|
||||||
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
"eslint-plugin-jest": "^24.3.2",
|
||||||
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-plugin-react": "^7.23.1",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"typescript": "^4.2.3",
|
"typescript": "^4.2.3",
|
||||||
"web-vitals": "^1.1.1"
|
"web-vitals": "^1.1.1"
|
||||||
@@ -1393,9 +1399,9 @@
|
|||||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
|
||||||
"integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
|
"integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
@@ -1405,7 +1411,6 @@
|
|||||||
"ignore": "^4.0.6",
|
"ignore": "^4.0.6",
|
||||||
"import-fresh": "^3.2.1",
|
"import-fresh": "^3.2.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"lodash": "^4.17.20",
|
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
},
|
},
|
||||||
@@ -1423,6 +1428,9 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
"node_modules/@eslint/eslintrc/node_modules/ignore": {
|
||||||
@@ -3174,7 +3182,10 @@
|
|||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
|
||||||
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
|
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn-walk": {
|
"node_modules/acorn-walk": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
@@ -6868,13 +6879,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "7.20.0",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz",
|
||||||
"integrity": "sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw==",
|
"integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "7.12.11",
|
"@babel/code-frame": "7.12.11",
|
||||||
"@eslint/eslintrc": "^0.3.0",
|
"@eslint/eslintrc": "^0.4.0",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.2",
|
"cross-spawn": "^7.0.2",
|
||||||
@@ -6887,10 +6898,10 @@
|
|||||||
"espree": "^7.3.1",
|
"espree": "^7.3.1",
|
||||||
"esquery": "^1.4.0",
|
"esquery": "^1.4.0",
|
||||||
"esutils": "^2.0.2",
|
"esutils": "^2.0.2",
|
||||||
"file-entry-cache": "^6.0.0",
|
"file-entry-cache": "^6.0.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"glob-parent": "^5.0.0",
|
"glob-parent": "^5.0.0",
|
||||||
"globals": "^12.1.0",
|
"globals": "^13.6.0",
|
||||||
"ignore": "^4.0.6",
|
"ignore": "^4.0.6",
|
||||||
"import-fresh": "^3.0.0",
|
"import-fresh": "^3.0.0",
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
@@ -6898,7 +6909,7 @@
|
|||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
"levn": "^0.4.1",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.21",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"optionator": "^0.9.1",
|
"optionator": "^0.9.1",
|
||||||
@@ -6916,6 +6927,21 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10.12.0 || >=12.0.0"
|
"node": "^10.12.0 || >=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-config-prettier": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-config-react-app": {
|
"node_modules/eslint-config-react-app": {
|
||||||
@@ -7128,15 +7154,24 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jest": {
|
"node_modules/eslint-plugin-jest": {
|
||||||
"version": "24.1.5",
|
"version": "24.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.2.tgz",
|
||||||
"integrity": "sha512-FIP3lwC8EzEG+rOs1y96cOJmMVpdFNreoDJv29B5vIupVssRi8zrSY3QadogT0K3h1Y8TMxJ6ZSAzYUmFCp2hg==",
|
"integrity": "sha512-cicWDr+RvTAOKS3Q/k03+Z3odt3VCiWamNUHWd6QWbVQWcYJyYgUTu8x0mx9GfeDEimawU5kQC+nQ3MFxIM6bw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/experimental-utils": "^4.0.1"
|
"@typescript-eslint/experimental-utils": "^4.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": ">= 4",
|
||||||
|
"eslint": ">=5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@typescript-eslint/eslint-plugin": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-jsx-a11y": {
|
"node_modules/eslint-plugin-jsx-a11y": {
|
||||||
@@ -7167,26 +7202,51 @@
|
|||||||
"integrity": "sha512-117l1H6U4X3Krn+MrzYrL57d5H7siRHWraBs7s+LjRuFK7Fe7hJqnJ0skWlinqsycVLU5YAo6L8CsEYQ0V5prg==",
|
"integrity": "sha512-117l1H6U4X3Krn+MrzYrL57d5H7siRHWraBs7s+LjRuFK7Fe7hJqnJ0skWlinqsycVLU5YAo6L8CsEYQ0V5prg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react": {
|
"node_modules/eslint-plugin-prettier": {
|
||||||
"version": "7.22.0",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz",
|
||||||
"integrity": "sha512-p30tuX3VS+NWv9nQot9xIGAHBXR0+xJVaZriEsHoJrASGCJZDJ8JLNM0YqKqI0AKm6Uxaa1VUHoNEibxRCMQHA==",
|
"integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array-includes": "^3.1.1",
|
"prettier-linter-helpers": "^1.0.0"
|
||||||
"array.prototype.flatmap": "^1.2.3",
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=5.0.0",
|
||||||
|
"prettier": ">=1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/eslint-plugin-react": {
|
||||||
|
"version": "7.23.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.1.tgz",
|
||||||
|
"integrity": "sha512-MvFGhZjI8Z4HusajmSw0ougGrq3Gs4vT/0WgwksZgf5RrLrRa2oYAw56okU4tZJl8+j7IYNuTM+2RnFEuTSdRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"array-includes": "^3.1.3",
|
||||||
|
"array.prototype.flatmap": "^1.2.4",
|
||||||
"doctrine": "^2.1.0",
|
"doctrine": "^2.1.0",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
||||||
"object.entries": "^1.1.2",
|
"minimatch": "^3.0.4",
|
||||||
"object.fromentries": "^2.0.2",
|
"object.entries": "^1.1.3",
|
||||||
"object.values": "^1.1.1",
|
"object.fromentries": "^2.0.4",
|
||||||
|
"object.values": "^1.1.3",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"resolve": "^1.18.1",
|
"resolve": "^2.0.0-next.3",
|
||||||
"string.prototype.matchall": "^4.0.2"
|
"string.prototype.matchall": "^4.0.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": "^3 || ^4 || ^5 || ^6 || ^7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-react-hooks": {
|
"node_modules/eslint-plugin-react-hooks": {
|
||||||
@@ -7210,6 +7270,19 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
||||||
|
"version": "2.0.0-next.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||||
|
"integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-core-module": "^2.2.0",
|
||||||
|
"path-parse": "^1.0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-testing-library": {
|
"node_modules/eslint-plugin-testing-library": {
|
||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.1.tgz",
|
||||||
@@ -7398,15 +7471,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/globals": {
|
"node_modules/eslint/node_modules/globals": {
|
||||||
"version": "12.4.0",
|
"version": "13.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz",
|
||||||
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
|
"integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"type-fest": "^0.8.1"
|
"type-fest": "^0.20.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint/node_modules/ignore": {
|
"node_modules/eslint/node_modules/ignore": {
|
||||||
@@ -7448,6 +7524,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint/node_modules/type-fest": {
|
||||||
|
"version": "0.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||||
|
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint/node_modules/which": {
|
"node_modules/eslint/node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -7931,6 +8019,12 @@
|
|||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-diff": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.2.5",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
||||||
@@ -12977,18 +13071,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/object.values": {
|
"node_modules/object.values": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
|
||||||
"integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
|
"integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.0",
|
"call-bind": "^1.0.2",
|
||||||
"define-properties": "^1.1.3",
|
"define-properties": "^1.1.3",
|
||||||
"es-abstract": "^1.18.0-next.1",
|
"es-abstract": "^1.18.0-next.2",
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/obuf": {
|
"node_modules/obuf": {
|
||||||
@@ -15008,6 +15105,30 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier-linter-helpers": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fast-diff": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pretty-bytes": {
|
"node_modules/pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
@@ -18037,6 +18158,9 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/style-loader": {
|
"node_modules/style-loader": {
|
||||||
@@ -22515,9 +22639,9 @@
|
|||||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||||
},
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.3.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz",
|
||||||
"integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
|
"integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
@@ -22527,7 +22651,6 @@
|
|||||||
"ignore": "^4.0.6",
|
"ignore": "^4.0.6",
|
||||||
"import-fresh": "^3.2.1",
|
"import-fresh": "^3.2.1",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"lodash": "^4.17.20",
|
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"strip-json-comments": "^3.1.1"
|
"strip-json-comments": "^3.1.1"
|
||||||
},
|
},
|
||||||
@@ -24080,7 +24203,8 @@
|
|||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
|
||||||
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
|
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"acorn-walk": {
|
"acorn-walk": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
@@ -27277,13 +27401,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "7.20.0",
|
"version": "7.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz",
|
||||||
"integrity": "sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw==",
|
"integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/code-frame": "7.12.11",
|
"@babel/code-frame": "7.12.11",
|
||||||
"@eslint/eslintrc": "^0.3.0",
|
"@eslint/eslintrc": "^0.4.0",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"cross-spawn": "^7.0.2",
|
"cross-spawn": "^7.0.2",
|
||||||
@@ -27296,10 +27420,10 @@
|
|||||||
"espree": "^7.3.1",
|
"espree": "^7.3.1",
|
||||||
"esquery": "^1.4.0",
|
"esquery": "^1.4.0",
|
||||||
"esutils": "^2.0.2",
|
"esutils": "^2.0.2",
|
||||||
"file-entry-cache": "^6.0.0",
|
"file-entry-cache": "^6.0.1",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
"functional-red-black-tree": "^1.0.1",
|
||||||
"glob-parent": "^5.0.0",
|
"glob-parent": "^5.0.0",
|
||||||
"globals": "^12.1.0",
|
"globals": "^13.6.0",
|
||||||
"ignore": "^4.0.6",
|
"ignore": "^4.0.6",
|
||||||
"import-fresh": "^3.0.0",
|
"import-fresh": "^3.0.0",
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
@@ -27307,7 +27431,7 @@
|
|||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
"levn": "^0.4.1",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.21",
|
||||||
"minimatch": "^3.0.4",
|
"minimatch": "^3.0.4",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
"optionator": "^0.9.1",
|
"optionator": "^0.9.1",
|
||||||
@@ -27352,12 +27476,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"version": "12.4.0",
|
"version": "13.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz",
|
||||||
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
|
"integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"type-fest": "^0.8.1"
|
"type-fest": "^0.20.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ignore": {
|
"ignore": {
|
||||||
@@ -27387,6 +27511,12 @@
|
|||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"type-fest": {
|
||||||
|
"version": "0.20.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||||
|
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
@@ -27398,6 +27528,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eslint-config-prettier": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"eslint-config-react-app": {
|
"eslint-config-react-app": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-6.0.0.tgz",
|
||||||
@@ -27578,9 +27715,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-jest": {
|
"eslint-plugin-jest": {
|
||||||
"version": "24.1.5",
|
"version": "24.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.2.tgz",
|
||||||
"integrity": "sha512-FIP3lwC8EzEG+rOs1y96cOJmMVpdFNreoDJv29B5vIupVssRi8zrSY3QadogT0K3h1Y8TMxJ6ZSAzYUmFCp2hg==",
|
"integrity": "sha512-cicWDr+RvTAOKS3Q/k03+Z3odt3VCiWamNUHWd6QWbVQWcYJyYgUTu8x0mx9GfeDEimawU5kQC+nQ3MFxIM6bw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@typescript-eslint/experimental-utils": "^4.0.1"
|
"@typescript-eslint/experimental-utils": "^4.0.1"
|
||||||
@@ -27613,23 +27750,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-react": {
|
"eslint-plugin-prettier": {
|
||||||
"version": "7.22.0",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz",
|
||||||
"integrity": "sha512-p30tuX3VS+NWv9nQot9xIGAHBXR0+xJVaZriEsHoJrASGCJZDJ8JLNM0YqKqI0AKm6Uxaa1VUHoNEibxRCMQHA==",
|
"integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"array-includes": "^3.1.1",
|
"prettier-linter-helpers": "^1.0.0"
|
||||||
"array.prototype.flatmap": "^1.2.3",
|
}
|
||||||
|
},
|
||||||
|
"eslint-plugin-react": {
|
||||||
|
"version": "7.23.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.1.tgz",
|
||||||
|
"integrity": "sha512-MvFGhZjI8Z4HusajmSw0ougGrq3Gs4vT/0WgwksZgf5RrLrRa2oYAw56okU4tZJl8+j7IYNuTM+2RnFEuTSdRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-includes": "^3.1.3",
|
||||||
|
"array.prototype.flatmap": "^1.2.4",
|
||||||
"doctrine": "^2.1.0",
|
"doctrine": "^2.1.0",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
"jsx-ast-utils": "^2.4.1 || ^3.0.0",
|
||||||
"object.entries": "^1.1.2",
|
"minimatch": "^3.0.4",
|
||||||
"object.fromentries": "^2.0.2",
|
"object.entries": "^1.1.3",
|
||||||
"object.values": "^1.1.1",
|
"object.fromentries": "^2.0.4",
|
||||||
|
"object.values": "^1.1.3",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"resolve": "^1.18.1",
|
"resolve": "^2.0.0-next.3",
|
||||||
"string.prototype.matchall": "^4.0.2"
|
"string.prototype.matchall": "^4.0.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"doctrine": {
|
"doctrine": {
|
||||||
@@ -27640,6 +27787,16 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"esutils": "^2.0.2"
|
"esutils": "^2.0.2"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"resolve": {
|
||||||
|
"version": "2.0.0-next.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||||
|
"integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"is-core-module": "^2.2.0",
|
||||||
|
"path-parse": "^1.0.6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -28162,6 +28319,12 @@
|
|||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fast-diff": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fast-glob": {
|
"fast-glob": {
|
||||||
"version": "3.2.5",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
|
||||||
@@ -32413,14 +32576,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"object.values": {
|
"object.values": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
|
||||||
"integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
|
"integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"call-bind": "^1.0.0",
|
"call-bind": "^1.0.2",
|
||||||
"define-properties": "^1.1.3",
|
"define-properties": "^1.1.3",
|
||||||
"es-abstract": "^1.18.0-next.1",
|
"es-abstract": "^1.18.0-next.2",
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -34125,6 +34288,21 @@
|
|||||||
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"prettier-linter-helpers": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-diff": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pretty-bytes": {
|
"pretty-bytes": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||||
|
|||||||
+9
-1
@@ -50,6 +50,12 @@
|
|||||||
"@types/react-copy-to-clipboard": "^5.0.0",
|
"@types/react-copy-to-clipboard": "^5.0.0",
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/react-syntax-highlighter": "^13.5.0",
|
"@types/react-syntax-highlighter": "^13.5.0",
|
||||||
|
"eslint": "^7.23.0",
|
||||||
|
"eslint-config-prettier": "^8.1.0",
|
||||||
|
"eslint-plugin-jest": "^24.3.2",
|
||||||
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-plugin-react": "^7.23.1",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"typescript": "^4.2.3",
|
"typescript": "^4.2.3",
|
||||||
"web-vitals": "^1.1.1"
|
"web-vitals": "^1.1.1"
|
||||||
@@ -58,7 +64,9 @@
|
|||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"lint": "eslint --fix \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --write \"src/**/*.ts\" \"src/**/*.tsx\"",
|
||||||
|
"lint:check": "eslint \"src/**/*.ts\" \"src/**/*.tsx\" && prettier --check \"src/**/*.ts\" \"src/**/*.tsx\""
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
declare module 'react-identicons' {
|
||||||
|
interface Props {
|
||||||
|
string: string
|
||||||
|
size?: number
|
||||||
|
padding?: number
|
||||||
|
bg?: string
|
||||||
|
fg?: string
|
||||||
|
palette?: string[]
|
||||||
|
count?: number
|
||||||
|
getColor?: () => string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Identicon = (props: Props): JSXElementConstructor => ReactNode
|
||||||
|
export default Identicon
|
||||||
|
}
|
||||||
+6
-6
@@ -1,5 +1,5 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
|
|
||||||
// Mocks methods that are not implemented in JSDOM
|
// Mocks methods that are not implemented in JSDOM
|
||||||
// see https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
|
// see https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
|
||||||
@@ -15,9 +15,9 @@ Object.defineProperty(window, 'matchMedia', {
|
|||||||
removeEventListener: jest.fn(),
|
removeEventListener: jest.fn(),
|
||||||
dispatchEvent: jest.fn(),
|
dispatchEvent: jest.fn(),
|
||||||
})),
|
})),
|
||||||
});
|
})
|
||||||
|
|
||||||
// TODO: this is not a good test and should be removed. Keeping it in to make sure the whole app renders (to be used in CI)
|
// TODO: this is not a good test and should be removed. Keeping it in to make sure the whole app renders (to be used in CI)
|
||||||
test('should render the app', async () => {
|
test('should render the app', () => {
|
||||||
render(<App />);
|
render(<App />)
|
||||||
});
|
})
|
||||||
|
|||||||
+18
-18
@@ -1,18 +1,18 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { ReactElement, useEffect, useState } from 'react'
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
import './App.css';
|
import './App.css'
|
||||||
|
|
||||||
import { ThemeProvider } from '@material-ui/styles';
|
import { ThemeProvider } from '@material-ui/styles'
|
||||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||||
|
|
||||||
import BaseRouter from './routes/routes';
|
import BaseRouter from './routes/routes'
|
||||||
import { lightTheme, darkTheme } from './theme';
|
import { lightTheme, darkTheme } from './theme'
|
||||||
|
|
||||||
function App() {
|
const App = (): ReactElement => {
|
||||||
const [themeMode, toggleThemeMode] = useState('light');
|
const [themeMode, toggleThemeMode] = useState('light')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let theme = localStorage.getItem('theme')
|
const theme = localStorage.getItem('theme')
|
||||||
|
|
||||||
if (theme) {
|
if (theme) {
|
||||||
toggleThemeMode(String(localStorage.getItem('theme')))
|
toggleThemeMode(String(localStorage.getItem('theme')))
|
||||||
@@ -21,14 +21,14 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window?.matchMedia('(prefers-color-scheme: dark)')?.addEventListener('change', e => {
|
window?.matchMedia('(prefers-color-scheme: dark)')?.addEventListener('change', e => {
|
||||||
toggleThemeMode(e?.matches ? "dark" : "light")
|
toggleThemeMode(e?.matches ? 'dark' : 'light')
|
||||||
});
|
|
||||||
|
|
||||||
return () => window?.matchMedia('(prefers-color-scheme: dark)')?.removeEventListener('change', e => {
|
|
||||||
toggleThemeMode(e?.matches ? "dark" : "light")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}, []);
|
return () =>
|
||||||
|
window?.matchMedia('(prefers-color-scheme: dark)')?.removeEventListener('change', e => {
|
||||||
|
toggleThemeMode(e?.matches ? 'dark' : 'light')
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
@@ -39,7 +39,7 @@ function App() {
|
|||||||
</Router>
|
</Router>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App
|
||||||
|
|||||||
@@ -1,98 +1,92 @@
|
|||||||
import React from 'react';
|
import { ReactElement, useState } from 'react'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import Input from '@material-ui/core/Input';
|
import Input from '@material-ui/core/Input'
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import { Snackbar, Container, CircularProgress } from '@material-ui/core';
|
import { Snackbar, Container, CircularProgress } from '@material-ui/core'
|
||||||
|
|
||||||
import { beeDebugApi } from '../services/bee';
|
import { beeDebugApi } from '../services/bee'
|
||||||
|
|
||||||
import EthereumAddress from './EthereumAddress';
|
import EthereumAddress from './EthereumAddress'
|
||||||
|
|
||||||
export default function DepositModal() {
|
export default function DepositModal(): ReactElement {
|
||||||
const [open, setOpen] = React.useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false)
|
||||||
const [peerId, setPeerId] = React.useState('');
|
const [peerId, setPeerId] = useState('')
|
||||||
const [loadingCashout, setLoadingCashout] = React.useState<boolean>(false);
|
const [loadingCashout, setLoadingCashout] = useState<boolean>(false)
|
||||||
const [showToast, setToastVisibility] = React.useState<boolean>(false);
|
const [showToast, setToastVisibility] = useState<boolean>(false)
|
||||||
const [toastContent, setToastContent] = React.useState<JSX.Element | null>(null);
|
const [toastContent, setToastContent] = useState<JSX.Element | null>(null)
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCashout = () => {
|
const handleCashout = () => {
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
setLoadingCashout(true)
|
setLoadingCashout(true)
|
||||||
beeDebugApi.chequebook.peerCashout(peerId)
|
beeDebugApi.chequebook
|
||||||
|
.peerCashout(peerId)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
handleToast(<span>Successfully cashed out cheque. Transaction
|
handleToast(
|
||||||
<EthereumAddress
|
<span>
|
||||||
hideBlockie
|
Successfully cashed out cheque. Transaction
|
||||||
transaction
|
<EthereumAddress hideBlockie transaction address={res.transactionHash} network={'goerli'} />
|
||||||
address={res.transactionHash}
|
</span>,
|
||||||
network={'goerli'}
|
)
|
||||||
/>
|
|
||||||
</span>)
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
|
// FIXME: handle errors more gracefully
|
||||||
handleToast(<span>Error with cashout</span>)
|
handleToast(<span>Error with cashout</span>)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoadingCashout(false)
|
setLoadingCashout(false)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
handleToast(<span>Peer Id invalid</span>)
|
handleToast(<span>Peer Id invalid</span>)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleToast = (text: JSX.Element) => {
|
const handleToast = (text: JSX.Element) => {
|
||||||
setToastContent(text)
|
setToastContent(text)
|
||||||
setToastVisibility(true);
|
setToastVisibility(true)
|
||||||
setTimeout(
|
setTimeout(() => setToastVisibility(false), 7000)
|
||||||
() => setToastVisibility(false),
|
}
|
||||||
7000
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button variant="contained" color="primary" onClick={handleClickOpen} style={{marginLeft:'7px'}}>
|
<Button variant="contained" color="primary" onClick={handleClickOpen} style={{ marginLeft: '7px' }}>
|
||||||
Cashout
|
Cashout
|
||||||
</Button>
|
</Button>
|
||||||
<Snackbar
|
<Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'center' }} open={showToast} message={toastContent} />
|
||||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
||||||
open={showToast}
|
|
||||||
message={toastContent}
|
|
||||||
/>
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
||||||
<DialogTitle id="form-dialog-title">Cashout Cheque</DialogTitle>
|
<DialogTitle id="form-dialog-title">Cashout Cheque</DialogTitle>
|
||||||
{loadingCashout ?
|
{loadingCashout ? (
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
:
|
) : (
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText style={{marginTop: '20px'}}>
|
<DialogContentText style={{ marginTop: '20px' }}>
|
||||||
Specify the peer Id of the peer you would like to cashout.
|
Specify the peer Id of the peer you would like to cashout.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="peerId"
|
id="peerId"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder='Peer Id'
|
placeholder="Peer Id"
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={(e) => setPeerId(e.target.value)}
|
onChange={e => setPeerId(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</DialogContent>}
|
</DialogContent>
|
||||||
|
)}
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={handleClose} color="primary">
|
<Button onClick={handleClose} color="primary">
|
||||||
Cancel
|
Cancel
|
||||||
@@ -103,5 +97,5 @@ export default function DepositModal() {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,28 @@
|
|||||||
import React from 'react';
|
import { ReactElement, useState } from 'react'
|
||||||
import { IconButton, Snackbar } from '@material-ui/core';
|
import { IconButton, Snackbar } from '@material-ui/core'
|
||||||
import {CopyToClipboard} from 'react-copy-to-clipboard';
|
import { CopyToClipboard } from 'react-copy-to-clipboard'
|
||||||
import { Clipboard } from 'react-feather';
|
import { Clipboard } from 'react-feather'
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
value: string,
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ClipboardCopy(props: IProps) {
|
export default function ClipboardCopy(props: Props): ReactElement {
|
||||||
const [copied, setCopied] = React.useState(false);
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
setCopied(true);
|
setCopied(true)
|
||||||
setTimeout(
|
setTimeout(() => setCopied(false), 3000)
|
||||||
() => setCopied(false),
|
|
||||||
3000
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{marginRight:'3px', marginLeft:'3px'}}>
|
|
||||||
<Snackbar
|
|
||||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
||||||
open={copied}
|
|
||||||
message="Copied"
|
|
||||||
/>
|
|
||||||
<IconButton color="primary" size='small' onClick={handleCopy}>
|
|
||||||
<CopyToClipboard text={props.value}
|
|
||||||
>
|
|
||||||
<Clipboard style={{height:'20px'}}/>
|
|
||||||
</CopyToClipboard>
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ marginRight: '3px', marginLeft: '3px' }}>
|
||||||
|
<Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'center' }} open={copied} message="Copied" />
|
||||||
|
<IconButton color="primary" size="small" onClick={handleCopy}>
|
||||||
|
<CopyToClipboard text={props.value}>
|
||||||
|
<Clipboard style={{ height: '20px' }} />
|
||||||
|
</CopyToClipboard>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
import type { ReactElement } from 'react'
|
||||||
|
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
code: string,
|
code: string
|
||||||
language: string,
|
language: string
|
||||||
showLineNumbers?: boolean,
|
showLineNumbers?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const CodeBlock = (props: IProps) => {
|
const CodeBlock = (props: Props): ReactElement => {
|
||||||
return (
|
return (
|
||||||
<div style={{textAlign:'left'}}>
|
<div style={{ textAlign: 'left' }}>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter language={props.language} showLineNumbers={props.showLineNumbers}>
|
||||||
language={props.language}
|
|
||||||
showLineNumbers={props.showLineNumbers}
|
|
||||||
>
|
|
||||||
{props.code}
|
{props.code}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default CodeBlock;
|
export default CodeBlock
|
||||||
|
|||||||
+129
-140
@@ -1,158 +1,147 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
import { withStyles, Theme, createStyles } from '@material-ui/core/styles';
|
import { withStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
import { Tabs, Tab, Box, Typography } from '@material-ui/core';
|
import { Tabs, Tab, Box, Typography } from '@material-ui/core'
|
||||||
import CodeBlock from './CodeBlock';
|
import CodeBlock from './CodeBlock'
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode
|
||||||
index: any;
|
index: number
|
||||||
value: any;
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
linux: string;
|
linux: string
|
||||||
mac: string;
|
mac: string
|
||||||
showLineNumbers?: boolean
|
showLineNumbers?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function a11yProps(index: any) {
|
function a11yProps(index: number) {
|
||||||
return {
|
return {
|
||||||
id: `simple-tab-${index}`,
|
id: `simple-tab-${index}`,
|
||||||
'aria-controls': `simple-tabpanel-${index}`,
|
'aria-controls': `simple-tabpanel-${index}`,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOS() {
|
function getOS() {
|
||||||
var userAgent = window.navigator.userAgent,
|
const userAgent = window.navigator.userAgent
|
||||||
platform = window.navigator.platform,
|
const platform = window.navigator.platform
|
||||||
macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],
|
const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K']
|
||||||
windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
|
const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE']
|
||||||
iosPlatforms = ['iPhone', 'iPad', 'iPod'],
|
const iosPlatforms = ['iPhone', 'iPad', 'iPod']
|
||||||
os = null;
|
|
||||||
|
|
||||||
if (macosPlatforms.indexOf(platform) !== -1) {
|
if (macosPlatforms.includes(platform)) return 'macOS'
|
||||||
os = 'macOS';
|
|
||||||
} else if (iosPlatforms.indexOf(platform) !== -1) {
|
if (iosPlatforms.includes(platform)) return 'iOS'
|
||||||
os = 'iOS';
|
|
||||||
} else if (windowsPlatforms.indexOf(platform) !== -1) {
|
if (windowsPlatforms.includes(platform)) return 'windows'
|
||||||
os = 'windows';
|
|
||||||
} else if (/Android/.test(userAgent)) {
|
if (/Android/.test(userAgent)) return 'android'
|
||||||
os = 'android';
|
|
||||||
} else if (!os && /Linux/.test(platform)) {
|
if (/Linux/.test(platform)) return 'linux'
|
||||||
os = 'linux';
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CodeBlockTabs(props: Props): ReactElement {
|
||||||
|
const [value, setValue] = React.useState(0)
|
||||||
|
|
||||||
|
const handleChange = (event: React.ChangeEvent<unknown>, newValue: number) => {
|
||||||
|
setValue(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return os;
|
useEffect(() => {
|
||||||
}
|
const os = getOS()
|
||||||
|
|
||||||
export default function CodeBlockTabs(props: IProps) {
|
if (os === 'windows') {
|
||||||
const [value, setValue] = React.useState(0);
|
setValue(0)
|
||||||
|
} else if (os === 'linux') {
|
||||||
|
setValue(0)
|
||||||
|
} else if (os === 'macOS') {
|
||||||
|
setValue(1)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
function TabPanel(props: TabPanelProps) {
|
||||||
setValue(newValue);
|
const { children, value, index, ...other } = props
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let os = getOS()
|
|
||||||
if(os === 'windows') {
|
|
||||||
setValue(0)
|
|
||||||
} else if (os === 'linux') {
|
|
||||||
setValue(0)
|
|
||||||
} else if (os === 'macOS') {
|
|
||||||
setValue(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
function TabPanel(props: TabPanelProps) {
|
|
||||||
const { children, value, index, ...other } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="tabpanel"
|
|
||||||
hidden={value !== index}
|
|
||||||
id={`simple-tabpanel-${index}`}
|
|
||||||
aria-labelledby={`simple-tab-${index}`}
|
|
||||||
{...other}
|
|
||||||
>
|
|
||||||
{value === index && (
|
|
||||||
<Box style={{ marginTop: '-12px' }}>
|
|
||||||
<Typography component="div">{children}</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AntTabs = withStyles({
|
|
||||||
root: {
|
|
||||||
borderBottom: '1px solid #e8e8e8',
|
|
||||||
},
|
|
||||||
indicator: {
|
|
||||||
backgroundColor: '#3f51b5',
|
|
||||||
},
|
|
||||||
})(Tabs);
|
|
||||||
|
|
||||||
interface StyledTabProps {
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AntTab = withStyles((theme: Theme) =>
|
|
||||||
createStyles({
|
|
||||||
root: {
|
|
||||||
textTransform: 'none',
|
|
||||||
minWidth: 72,
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
fontWeight: theme.typography.fontWeightRegular,
|
|
||||||
marginRight: theme.spacing(4),
|
|
||||||
fontFamily: [
|
|
||||||
'-apple-system',
|
|
||||||
'BlinkMacSystemFont',
|
|
||||||
'"Segoe UI"',
|
|
||||||
'Roboto',
|
|
||||||
'"Helvetica Neue"',
|
|
||||||
'Arial',
|
|
||||||
'sans-serif',
|
|
||||||
'"Apple Color Emoji"',
|
|
||||||
'"Segoe UI Emoji"',
|
|
||||||
'"Segoe UI Symbol"',
|
|
||||||
].join(','),
|
|
||||||
'&:hover': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
'&$selected': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
fontWeight: theme.typography.fontWeightMedium,
|
|
||||||
},
|
|
||||||
'&:focus': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
selected: {},
|
|
||||||
}),
|
|
||||||
)((props: StyledTabProps) => <Tab disableRipple {...props} />);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
|
role="tabpanel"
|
||||||
<AntTab label="Linux" {...a11yProps(0)} />
|
hidden={value !== index}
|
||||||
<AntTab label="MacOS" {...a11yProps(1)} />
|
id={`simple-tabpanel-${index}`}
|
||||||
</AntTabs>
|
aria-labelledby={`simple-tab-${index}`}
|
||||||
<TabPanel value={value} index={0}>
|
{...other}
|
||||||
<CodeBlock
|
>
|
||||||
showLineNumbers={props.showLineNumbers}
|
{value === index && (
|
||||||
language='bash'
|
<Box style={{ marginTop: '-12px' }}>
|
||||||
code={props.linux}
|
<Typography component="div">{children}</Typography>
|
||||||
/>
|
</Box>
|
||||||
</TabPanel>
|
)}
|
||||||
<TabPanel value={value} index={1}>
|
</div>
|
||||||
<CodeBlock
|
|
||||||
showLineNumbers={props.showLineNumbers}
|
|
||||||
language='bash'
|
|
||||||
code={props.mac}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AntTabs = withStyles({
|
||||||
|
root: {
|
||||||
|
borderBottom: '1px solid #e8e8e8',
|
||||||
|
},
|
||||||
|
indicator: {
|
||||||
|
backgroundColor: '#3f51b5',
|
||||||
|
},
|
||||||
|
})(Tabs)
|
||||||
|
|
||||||
|
interface StyledTabProps {
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AntTab = withStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
textTransform: 'none',
|
||||||
|
minWidth: 72,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
fontWeight: theme.typography.fontWeightRegular,
|
||||||
|
marginRight: theme.spacing(4),
|
||||||
|
fontFamily: [
|
||||||
|
'-apple-system',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'"Segoe UI"',
|
||||||
|
'Roboto',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
'Arial',
|
||||||
|
'sans-serif',
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"',
|
||||||
|
].join(','),
|
||||||
|
'&:hover': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
'&$selected': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {},
|
||||||
|
}),
|
||||||
|
)((props: StyledTabProps) => <Tab disableRipple {...props} />)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
|
||||||
|
<AntTab label="Linux" {...a11yProps(0)} />
|
||||||
|
<AntTab label="MacOS" {...a11yProps(1)} />
|
||||||
|
</AntTabs>
|
||||||
|
<TabPanel value={value} index={0}>
|
||||||
|
<CodeBlock showLineNumbers={props.showLineNumbers} language="bash" code={props.linux} />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={value} index={1}>
|
||||||
|
<CodeBlock showLineNumbers={props.showLineNumbers} language="bash" code={props.mac} />
|
||||||
|
</TabPanel>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,62 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { TextField, Button, CircularProgress, Container } from '@material-ui/core';
|
import { TextField, Button, CircularProgress, Container } from '@material-ui/core'
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
defaultHost?: string,
|
defaultHost?: string
|
||||||
hostName: string,
|
hostName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ConnectToHost(props: IProps) {
|
export default function ConnectToHost(props: Props): ReactElement {
|
||||||
const [hostInputVisible, toggleHostInputVisibility] = useState(false)
|
const [hostInputVisible, toggleHostInputVisibility] = useState(false)
|
||||||
const [connectingToHost, setConnectingToHost] = useState(false)
|
const [connectingToHost, setConnectingToHost] = useState(false)
|
||||||
const [host, setHost] = useState('')
|
const [host, setHost] = useState('')
|
||||||
|
|
||||||
const handleNewHostConnection = () => {
|
const handleNewHostConnection = () => {
|
||||||
if (host) {
|
if (host) {
|
||||||
setConnectingToHost(true)
|
setConnectingToHost(true)
|
||||||
sessionStorage.setItem(props.hostName, host)
|
sessionStorage.setItem(props.hostName, host)
|
||||||
toggleHostInputVisibility(!hostInputVisible)
|
toggleHostInputVisibility(!hostInputVisible)
|
||||||
window.location.reload();
|
window.location.reload()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{hostInputVisible ?
|
{
|
||||||
<div style={{display:'flex'}}>
|
// FIXME: this should be broken up
|
||||||
<TextField
|
/* eslint-disable no-nested-ternary */
|
||||||
defaultValue={props.defaultHost}
|
hostInputVisible ? (
|
||||||
label="Enter host"
|
<div style={{ display: 'flex' }}>
|
||||||
variant="outlined"
|
<TextField
|
||||||
size='small'
|
defaultValue={props.defaultHost}
|
||||||
onChange={(e) => setHost(e.target.value)}
|
label="Enter host"
|
||||||
style={{marginRight:'15px', minWidth:'300px'}}
|
variant="outlined"
|
||||||
/>
|
size="small"
|
||||||
<Button onClick={() => handleNewHostConnection()} size='small' variant="outlined">Connect</Button>
|
onChange={e => setHost(e.target.value)}
|
||||||
<Button style={{marginLeft:'7px'}} onClick={() => toggleHostInputVisibility(!hostInputVisible)} size='small'>Cancel</Button>
|
style={{ marginRight: '15px', minWidth: '300px' }}
|
||||||
</div>
|
/>
|
||||||
:
|
<Button onClick={() => handleNewHostConnection()} size="small" variant="outlined">
|
||||||
connectingToHost ?
|
Connect
|
||||||
<Container style={{textAlign:'center', padding:'0px'}}>
|
</Button>
|
||||||
<CircularProgress size={20} />
|
<Button
|
||||||
</Container>
|
style={{ marginLeft: '7px' }}
|
||||||
:
|
onClick={() => toggleHostInputVisibility(!hostInputVisible)}
|
||||||
<Button onClick={() => toggleHostInputVisibility(!hostInputVisible)} size='small' variant="outlined">Change host</Button>
|
size="small"
|
||||||
}
|
>
|
||||||
</div>
|
Cancel
|
||||||
)
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : connectingToHost ? (
|
||||||
|
<Container style={{ textAlign: 'center', padding: '0px' }}>
|
||||||
|
<CircularProgress size={20} />
|
||||||
|
</Container>
|
||||||
|
) : (
|
||||||
|
<Button onClick={() => toggleHostInputVisibility(!hostInputVisible)} size="small" variant="outlined">
|
||||||
|
Change host
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
/* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,69 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement } from 'react'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import Input from '@material-ui/core/Input';
|
import Input from '@material-ui/core/Input'
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import { Snackbar } from '@material-ui/core';
|
import { Snackbar } from '@material-ui/core'
|
||||||
|
|
||||||
import { beeDebugApi } from '../services/bee';
|
import { beeDebugApi } from '../services/bee'
|
||||||
|
|
||||||
export default function DepositModal() {
|
export default function DepositModal(): ReactElement {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false)
|
||||||
const [amount, setAmount] = React.useState(BigInt(0));
|
const [amount, setAmount] = React.useState(BigInt(0))
|
||||||
const [showToast, setToastVisibility] = React.useState(false);
|
const [showToast, setToastVisibility] = React.useState(false)
|
||||||
const [toastContent, setToastContent] = React.useState('');
|
const [toastContent, setToastContent] = React.useState('')
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWithdraw = () => {
|
const handleWithdraw = () => {
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
beeDebugApi.chequebook.deposit(amount)
|
beeDebugApi.chequebook
|
||||||
|
.deposit(amount)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
handleToast(`Successful Deposit. Transaction ${res.transactionHash}`)
|
handleToast(`Successful Deposit. Transaction ${res.transactionHash}`)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
handleToast('Error with Deposit')
|
handleToast('Error with Deposit')
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
handleToast('Must be amount of greater than 0')
|
handleToast('Must be amount of greater than 0')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleToast = (text: string) => {
|
const handleToast = (text: string) => {
|
||||||
setToastContent(text)
|
setToastContent(text)
|
||||||
setToastVisibility(true);
|
setToastVisibility(true)
|
||||||
setTimeout(
|
setTimeout(() => setToastVisibility(false), 7000)
|
||||||
() => setToastVisibility(false),
|
}
|
||||||
7000
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button variant="outlined" color="primary" onClick={handleClickOpen} style={{marginLeft:'7px'}}>
|
<Button variant="outlined" color="primary" onClick={handleClickOpen} style={{ marginLeft: '7px' }}>
|
||||||
Deposit
|
Deposit
|
||||||
</Button>
|
</Button>
|
||||||
<Snackbar
|
<Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'center' }} open={showToast} message={toastContent} />
|
||||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
||||||
open={showToast}
|
|
||||||
message={toastContent}
|
|
||||||
/>
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
||||||
<DialogTitle id="form-dialog-title">Deposit Funds</DialogTitle>
|
<DialogTitle id="form-dialog-title">Deposit Funds</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>Specify the amount you would like to deposit to your node.</DialogContentText>
|
||||||
Specify the amount you would like to deposit to your node.
|
|
||||||
</DialogContentText>
|
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="name"
|
id="name"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder='Amount'
|
placeholder="Amount"
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={(e) => setAmount(BigInt(e.target.value))}
|
onChange={e => setAmount(BigInt(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
@@ -84,5 +76,5 @@ export default function DepositModal() {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,57 @@
|
|||||||
import React from 'react';
|
import { Typography } from '@material-ui/core/'
|
||||||
|
import QRCodeModal from './QRCodeModal'
|
||||||
|
import ClipboardCopy from './ClipboardCopy'
|
||||||
|
|
||||||
import { Typography } from '@material-ui/core/';
|
import Identicon from 'react-identicons'
|
||||||
import QRCodeModal from './QRCodeModal';
|
import { ReactElement } from 'react'
|
||||||
import ClipboardCopy from './ClipboardCopy';
|
|
||||||
|
|
||||||
// @ts-ignore
|
interface Props {
|
||||||
import Identicon from 'react-identicons';
|
address: string | undefined
|
||||||
|
network?: string
|
||||||
interface IProps {
|
hideBlockie?: boolean
|
||||||
address: string | undefined,
|
transaction?: boolean
|
||||||
network?: string,
|
truncate?: boolean
|
||||||
hideBlockie?: boolean,
|
|
||||||
transaction?: boolean,
|
|
||||||
truncate?: boolean,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EthereumAddress(props: IProps) {
|
export default function EthereumAddress(props: Props): ReactElement {
|
||||||
return (
|
return (
|
||||||
<Typography component="div" variant="subtitle1">
|
<Typography component="div" variant="subtitle1">
|
||||||
{props.address ?
|
{props.address ? (
|
||||||
<div style={{display:'flex'}}>
|
<div style={{ display: 'flex' }}>
|
||||||
{props.hideBlockie ?
|
{props.hideBlockie ? null : (
|
||||||
null
|
<div style={{ paddingTop: '5px', marginRight: '10px' }}>
|
||||||
:
|
<Identicon size={20} string={props.address} />
|
||||||
<div style={{paddingTop:'5px', marginRight: '10px', }}>
|
|
||||||
<Identicon size='20' string={props.address} />
|
|
||||||
</div>}
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
style={props.truncate ?
|
|
||||||
{ marginRight:'7px', maxWidth:'200px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display:'block'}
|
|
||||||
:
|
|
||||||
{ marginRight:'7px'}
|
|
||||||
}
|
|
||||||
href={`https://${props.network}.${process.env.REACT_APP_ETHERSCAN_HOST}/${props.transaction ? 'tx' : 'address' }/${props.address}`}
|
|
||||||
target='_blank'
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{ props.address }
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<QRCodeModal
|
|
||||||
value={ props.address }
|
|
||||||
label={'Ethereum Address'}
|
|
||||||
/>
|
|
||||||
<ClipboardCopy
|
|
||||||
value={ props.address }
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
: '-' }
|
)}
|
||||||
</Typography>
|
<div>
|
||||||
)
|
<a
|
||||||
|
style={
|
||||||
|
props.truncate
|
||||||
|
? {
|
||||||
|
marginRight: '7px',
|
||||||
|
maxWidth: '200px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
display: 'block',
|
||||||
|
}
|
||||||
|
: { marginRight: '7px' }
|
||||||
|
}
|
||||||
|
href={`https://${props.network}.${process.env.REACT_APP_ETHERSCAN_HOST}/${
|
||||||
|
props.transaction ? 'tx' : 'address'
|
||||||
|
}/${props.address}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{props.address}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<QRCodeModal value={props.address} label={'Ethereum Address'} />
|
||||||
|
<ClipboardCopy value={props.address} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
|
|
||||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
import { Card, CardContent, Typography } from '@material-ui/core/';
|
import { Card, CardContent, Typography } from '@material-ui/core/'
|
||||||
|
|
||||||
import EthereumAddress from '../components/EthereumAddress';
|
import EthereumAddress from '../components/EthereumAddress'
|
||||||
import { Skeleton } from '@material-ui/lab';
|
import { Skeleton } from '@material-ui/lab'
|
||||||
|
|
||||||
import type { ChequebookAddressResponse, NodeAddresses } from '@ethersphere/bee-js';
|
import type { ChequebookAddressResponse, NodeAddresses } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -22,58 +22,58 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
flex: '1 0 auto',
|
flex: '1 0 auto',
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
backgroundColor: '#76a9fa',
|
backgroundColor: '#76a9fa',
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
interface IProps{
|
interface Props {
|
||||||
nodeAddresses: NodeAddresses | null,
|
nodeAddresses: NodeAddresses | null
|
||||||
isLoadingNodeAddresses: boolean,
|
isLoadingNodeAddresses: boolean
|
||||||
chequebookAddress: ChequebookAddressResponse | null,
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
isLoadingChequebookAddress: boolean,
|
isLoadingChequebookAddress: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function EthereumAddressCard(props: IProps) {
|
function EthereumAddressCard(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card className={classes.root}>
|
<Card className={classes.root}>
|
||||||
{props.isLoadingNodeAddresses ?
|
{props.isLoadingNodeAddresses ? (
|
||||||
<div style={{padding: '16px'}}>
|
<div style={{ padding: '16px' }}>
|
||||||
<Skeleton width={300} height={30} animation="wave" />
|
<Skeleton width={300} height={30} animation="wave" />
|
||||||
<Skeleton width={300} height={50} animation="wave" />
|
<Skeleton width={300} height={50} animation="wave" />
|
||||||
</div>
|
</div>
|
||||||
:
|
) : (
|
||||||
<div className={classes.details}>
|
<div className={classes.details}>
|
||||||
<CardContent className={classes.content}>
|
<CardContent className={classes.content}>
|
||||||
<Typography variant="subtitle1" gutterBottom>Ethereum Address</Typography>
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
<EthereumAddress
|
Ethereum Address
|
||||||
address={props.nodeAddresses?.ethereum}
|
</Typography>
|
||||||
network={'goerli'}
|
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
|
||||||
/>
|
</CardContent>
|
||||||
</CardContent>
|
</div>
|
||||||
</div>}
|
)}
|
||||||
{props.isLoadingChequebookAddress ?
|
{props.isLoadingChequebookAddress ? (
|
||||||
<div style={{padding: '16px'}}>
|
<div style={{ padding: '16px' }}>
|
||||||
<Skeleton width={300} height={30} animation="wave" />
|
<Skeleton width={300} height={30} animation="wave" />
|
||||||
<Skeleton width={300} height={50} animation="wave" />
|
<Skeleton width={300} height={50} animation="wave" />
|
||||||
</div>
|
</div>
|
||||||
:
|
) : (
|
||||||
<div className={classes.details}>
|
<div className={classes.details}>
|
||||||
<CardContent className={classes.content}>
|
<CardContent className={classes.content}>
|
||||||
<Typography variant="subtitle1" gutterBottom>Chequebook Contract Address</Typography>
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
<EthereumAddress
|
Chequebook Contract Address
|
||||||
address={props.chequebookAddress?.chequebookaddress}
|
</Typography>
|
||||||
network={'goerli'}
|
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
|
||||||
/>
|
</CardContent>
|
||||||
</CardContent>
|
</div>
|
||||||
</div>}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EthereumAddressCard
|
export default EthereumAddressCard
|
||||||
|
|||||||
+20
-28
@@ -1,30 +1,31 @@
|
|||||||
import React, { useState } from 'react';
|
import { useState, ReactElement } from 'react'
|
||||||
|
|
||||||
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
import { Toolbar, Chip, IconButton } from '@material-ui/core/';
|
import { Toolbar, Chip, IconButton } from '@material-ui/core/'
|
||||||
|
|
||||||
import { Sun, Moon } from 'react-feather';
|
import { Sun, Moon } from 'react-feather'
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
appBar: {
|
appBar: {
|
||||||
width: `calc(100% - ${drawerWidth}px)`,
|
width: `calc(100% - ${drawerWidth}px)`,
|
||||||
marginLeft: drawerWidth,
|
marginLeft: drawerWidth,
|
||||||
},
|
},
|
||||||
network: {
|
network: {},
|
||||||
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
interface Props {
|
||||||
|
themeMode: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SideBar(props: Props): ReactElement {
|
||||||
export default function SideBar(props: any) {
|
const [darkMode, toggleDarkMode] = useState(false)
|
||||||
const [darkMode, toggleDarkMode] = useState(false);
|
|
||||||
|
|
||||||
const switchTheme = () => {
|
const switchTheme = () => {
|
||||||
let theme = localStorage.getItem('theme')
|
const theme = localStorage.getItem('theme')
|
||||||
|
|
||||||
if (theme) {
|
if (theme) {
|
||||||
localStorage.setItem('theme', theme === 'light' ? 'dark' : 'light')
|
localStorage.setItem('theme', theme === 'light' ? 'dark' : 'light')
|
||||||
} else {
|
} else {
|
||||||
@@ -35,26 +36,17 @@ export default function SideBar(props: any) {
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'fixed' }} className={classes.appBar}>
|
<div style={{ display: 'fixed' }} className={classes.appBar}>
|
||||||
<Toolbar style={{ display: 'flex' }}>
|
<Toolbar style={{ display: 'flex' }}>
|
||||||
<Chip
|
<Chip style={{ marginLeft: '7px' }} size="small" label="Goerli" className={classes.network} />
|
||||||
style={{ marginLeft: '7px' }}
|
|
||||||
size="small"
|
|
||||||
label='Goerli'
|
|
||||||
className={classes.network}
|
|
||||||
/>
|
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%' }}>
|
||||||
<div style={{ float: 'right' }} >
|
<div style={{ float: 'right' }}>
|
||||||
<IconButton style={{ marginRight: '10px' }} aria-label="dark-mode" onClick={() => switchTheme()}>
|
<IconButton style={{ marginRight: '10px' }} aria-label="dark-mode" onClick={() => switchTheme()}>
|
||||||
{props.themeMode === 'dark' ?
|
{props.themeMode === 'dark' ? <Moon /> : <Sun />}
|
||||||
<Moon />
|
|
||||||
:
|
|
||||||
<Sun />
|
|
||||||
}
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{/* <Chip
|
{/* <Chip
|
||||||
label="Connect Wallet"
|
label="Connect Wallet"
|
||||||
@@ -65,5 +57,5 @@ export default function SideBar(props: any) {
|
|||||||
</Toolbar>
|
</Toolbar>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
import React,{ useState } from 'react';
|
import { ReactElement, useState } from 'react'
|
||||||
import QRCode from 'qrcode.react';
|
import QRCode from 'qrcode.react'
|
||||||
import { IconButton, Dialog, DialogTitle } from '@material-ui/core';
|
import { IconButton, Dialog, DialogTitle } from '@material-ui/core'
|
||||||
import { FilterCenterFocusSharp } from '@material-ui/icons';
|
import { FilterCenterFocusSharp } from '@material-ui/icons'
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
value: string,
|
value: string
|
||||||
label: string,
|
label: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QRCodeModal(props: IProps) {
|
export default function QRCodeModal(props: Props): ReactElement {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true)
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<IconButton color="primary" size='small' onClick={handleOpen}>
|
|
||||||
<FilterCenterFocusSharp/>
|
|
||||||
</IconButton>
|
|
||||||
<Dialog onClose={handleClose} aria-labelledby="simple-dialog-title" open={open}>
|
|
||||||
<div style={{padding: '30px', textAlign: 'center'}}>
|
|
||||||
<DialogTitle id="simple-dialog-title">{ props.label }</DialogTitle>
|
|
||||||
<QRCode
|
|
||||||
value={props.value}
|
|
||||||
size={150}
|
|
||||||
bgColor={"#ffffff"}
|
|
||||||
fgColor={"#000000"}
|
|
||||||
level={"L"}
|
|
||||||
includeMargin={false}
|
|
||||||
renderAs={"svg"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<IconButton color="primary" size="small" onClick={handleOpen}>
|
||||||
|
<FilterCenterFocusSharp />
|
||||||
|
</IconButton>
|
||||||
|
<Dialog onClose={handleClose} aria-labelledby="simple-dialog-title" open={open}>
|
||||||
|
<div style={{ padding: '30px', textAlign: 'center' }}>
|
||||||
|
<DialogTitle id="simple-dialog-title">{props.label}</DialogTitle>
|
||||||
|
<QRCode
|
||||||
|
value={props.value}
|
||||||
|
size={150}
|
||||||
|
bgColor={'#ffffff'}
|
||||||
|
fgColor={'#000000'}
|
||||||
|
level={'L'}
|
||||||
|
includeMargin={false}
|
||||||
|
renderAs={'svg'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
|
|
||||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
|
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
import { Paper, InputBase, IconButton } from '@material-ui/core';
|
import { Paper, InputBase, IconButton } from '@material-ui/core'
|
||||||
import { Search } from '@material-ui/icons';
|
import { Search } from '@material-ui/icons'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -24,27 +24,23 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
margin: 4,
|
margin: 4,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
interface IProps {
|
export default function SearchBar() {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
}
|
return (
|
||||||
|
<div>
|
||||||
export default function SearchBar(props: IProps) {
|
<Paper component="form" className={classes.root}>
|
||||||
const classes = useStyles();
|
<InputBase
|
||||||
|
className={classes.input}
|
||||||
return (
|
placeholder="Enter hash e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
|
||||||
<div>
|
inputProps={{ 'aria-label': 'search google maps' }}
|
||||||
<Paper component="form" className={classes.root}>
|
/>
|
||||||
<InputBase
|
<IconButton type="submit" className={classes.iconButton} aria-label="search">
|
||||||
className={classes.input}
|
<Search />
|
||||||
placeholder="Enter hash e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
|
</IconButton>
|
||||||
inputProps={{ 'aria-label': 'search google maps' }}
|
</Paper>
|
||||||
/>
|
</div>
|
||||||
<IconButton type="submit" className={classes.iconButton} aria-label="search">
|
)
|
||||||
<Search />
|
|
||||||
</IconButton>
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-53
@@ -1,46 +1,47 @@
|
|||||||
import React from 'react';
|
import { ReactElement } from 'react'
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, RouteComponentProps } from 'react-router-dom'
|
||||||
|
|
||||||
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
|
||||||
import { ListItemText, ListItemIcon, ListItem, Divider, List, Drawer, Link as MUILink } from '@material-ui/core';
|
import { ListItemText, ListItemIcon, ListItem, Divider, List, Drawer, Link as MUILink } from '@material-ui/core'
|
||||||
import { OpenInNewSharp } from '@material-ui/icons';
|
import { OpenInNewSharp } from '@material-ui/icons'
|
||||||
import { Activity, FileText, DollarSign, Share2, Settings } from 'react-feather';
|
import { Activity, FileText, DollarSign, Share2, Settings } from 'react-feather'
|
||||||
|
|
||||||
import SwarmLogoOrange from '../assets/swarm-logo-orange.svg'
|
import SwarmLogoOrange from '../assets/swarm-logo-orange.svg'
|
||||||
|
import { Health } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240
|
||||||
|
|
||||||
const navBarItems = [
|
const navBarItems = [
|
||||||
{
|
{
|
||||||
'label': 'Status',
|
label: 'Status',
|
||||||
'id': 'status',
|
id: 'status',
|
||||||
'path': '/',
|
path: '/',
|
||||||
'icon': 'activity'
|
icon: Activity,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': 'Files',
|
label: 'Files',
|
||||||
'id': 'files',
|
id: 'files',
|
||||||
'path': '/files/',
|
path: '/files/',
|
||||||
'icon': 'file-text'
|
icon: FileText,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': 'Accounting',
|
label: 'Accounting',
|
||||||
'id': 'accounting',
|
id: 'accounting',
|
||||||
'path': '/accounting/',
|
path: '/accounting/',
|
||||||
'icon': 'dollar-sign'
|
icon: DollarSign,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': 'Peers',
|
label: 'Peers',
|
||||||
'id': 'peers',
|
id: 'peers',
|
||||||
'path': '/peers/',
|
path: '/peers/',
|
||||||
'icon': 'share-2'
|
icon: Share2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': 'Settings',
|
label: 'Settings',
|
||||||
'id': 'settings',
|
id: 'settings',
|
||||||
'path': '/settings/',
|
path: '/settings/',
|
||||||
'icon': 'settings'
|
icon: Settings,
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
@@ -68,29 +69,20 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
},
|
},
|
||||||
activeSideBarItem: {
|
activeSideBarItem: {
|
||||||
borderLeft: '4px solid #dd7700',
|
borderLeft: '4px solid #dd7700',
|
||||||
backgroundColor: 'inherit !important'
|
backgroundColor: 'inherit !important',
|
||||||
},
|
},
|
||||||
toolbar: theme.mixins.toolbar,
|
toolbar: theme.mixins.toolbar,
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
const getIcon = (iconPath: string) => {
|
interface Props extends RouteComponentProps {
|
||||||
switch (iconPath) {
|
themeMode: string
|
||||||
case 'activity':
|
health: boolean
|
||||||
return <Activity style={{ height: '20px' }} />
|
nodeHealth: Health | null
|
||||||
case 'file-text':
|
|
||||||
return <FileText style={{ height: '20px' }} />
|
|
||||||
case 'dollar-sign':
|
|
||||||
return <DollarSign style={{ height: '20px' }} />
|
|
||||||
case 'share-2':
|
|
||||||
return <Share2 style={{ height: '20px' }} />
|
|
||||||
case 'settings':
|
|
||||||
return <Settings style={{ height: '20px' }} />
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SideBar(props: any) {
|
export default function SideBar(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
@@ -103,18 +95,30 @@ export default function SideBar(props: any) {
|
|||||||
anchor="left"
|
anchor="left"
|
||||||
>
|
>
|
||||||
<div className={classes.toolbar} style={{ textAlign: 'left', marginLeft: 20 }}>
|
<div className={classes.toolbar} style={{ textAlign: 'left', marginLeft: 20 }}>
|
||||||
<Link to='/'>
|
<Link to="/">
|
||||||
<img alt='swarm' className={classes.logo} src={props.themeMode === 'light' ? SwarmLogoOrange : SwarmLogoOrange} style={{ maxHeight: '30px', alignItems: 'center' }} />
|
<img
|
||||||
|
alt="swarm"
|
||||||
|
className={classes.logo}
|
||||||
|
src={props.themeMode === 'light' ? SwarmLogoOrange : SwarmLogoOrange}
|
||||||
|
style={{ maxHeight: '30px', alignItems: 'center' }}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<List>
|
<List>
|
||||||
{navBarItems.map(item => (
|
{navBarItems.map(item => (
|
||||||
<Link to={item.path} key={item.id} style={{ color: 'inherit', textDecoration: 'none' }}>
|
<Link to={item.path} key={item.id} style={{ color: 'inherit', textDecoration: 'none' }}>
|
||||||
<ListItem button selected={props.location.pathname === item.path} className={props.location.pathname === item.path ? classes.activeSideBarItem : ''}>
|
<ListItem
|
||||||
|
button
|
||||||
|
selected={props.location.pathname === item.path}
|
||||||
|
className={props.location.pathname === item.path ? classes.activeSideBarItem : ''}
|
||||||
|
>
|
||||||
<ListItemIcon className={props.location.pathname === item.path ? classes.activeSideBar : ''}>
|
<ListItemIcon className={props.location.pathname === item.path ? classes.activeSideBar : ''}>
|
||||||
{getIcon(item.icon)}
|
<item.icon style={{ height: '20px' }} />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={item.label} className={props.location.pathname === item.path ? classes.activeSideBar : ''} />
|
<ListItemText
|
||||||
|
primary={item.label}
|
||||||
|
className={props.location.pathname === item.path ? classes.activeSideBar : ''}
|
||||||
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@@ -131,16 +135,34 @@ export default function SideBar(props: any) {
|
|||||||
<div style={{ position: 'fixed', bottom: 0, width: 'inherit', padding: '10px' }}>
|
<div style={{ position: 'fixed', bottom: 0, width: 'inherit', padding: '10px' }}>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<div style={{ marginRight: '30px' }}>
|
<div style={{ marginRight: '30px' }}>
|
||||||
<div style={{ backgroundColor: props.health ? '#32c48d' : '#c9201f', marginRight: '7px', height: '10px', width: '10px', borderRadius: '50%', display: 'inline-block' }} />
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: props.health ? '#32c48d' : '#c9201f',
|
||||||
|
marginRight: '7px',
|
||||||
|
height: '10px',
|
||||||
|
width: '10px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<span>API</span>
|
<span>API</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ backgroundColor: props.nodeHealth?.status === 'ok' ? '#32c48d' : '#c9201f', marginRight: '7px', height: '10px', width: '10px', borderRadius: '50%', display: 'inline-block' }} />
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: props.nodeHealth?.status === 'ok' ? '#32c48d' : '#c9201f',
|
||||||
|
marginRight: '7px',
|
||||||
|
height: '10px',
|
||||||
|
width: '10px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<span>Debug API</span>
|
<span>Debug API</span>
|
||||||
</div>
|
</div>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-45
@@ -1,53 +1,50 @@
|
|||||||
import React from 'react'
|
import type { ReactElement } from 'react'
|
||||||
|
|
||||||
import { makeStyles, } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Card, CardContent, Typography } from '@material-ui/core/';
|
import { Card, CardContent, Typography } from '@material-ui/core/'
|
||||||
import { Skeleton } from '@material-ui/lab';
|
import { Skeleton } from '@material-ui/lab'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
root: {
|
root: {
|
||||||
minWidth: 275,
|
minWidth: 275,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
},
|
},
|
||||||
pos: {
|
pos: {
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
label: string
|
label: string
|
||||||
statistic?: string
|
statistic?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StatCard({loading, label, statistic}: IProps) {
|
export default function StatCard({ loading, label, statistic }: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
|
return (
|
||||||
return (
|
<Card className={classes.root}>
|
||||||
<Card className={classes.root}>
|
<CardContent>
|
||||||
<CardContent>
|
{loading && (
|
||||||
{loading && (
|
<>
|
||||||
<>
|
<Skeleton width={180} height={25} animation="wave" />
|
||||||
<Skeleton width={180} height={25} animation="wave" />
|
<Skeleton width={180} height={35} animation="wave" />
|
||||||
<Skeleton width={180} height={35} animation="wave" />
|
</>
|
||||||
</>
|
)}
|
||||||
)
|
{!loading && (
|
||||||
}
|
<>
|
||||||
{!loading && (
|
<Typography className={classes.title} color="textSecondary" gutterBottom>
|
||||||
<>
|
{label}
|
||||||
<Typography className={classes.title} color="textSecondary" gutterBottom>
|
</Typography>
|
||||||
{label}
|
<Typography variant="h5" component="h2">
|
||||||
</Typography>
|
{statistic}
|
||||||
<Typography variant="h5" component="h2">
|
</Typography>
|
||||||
{statistic}
|
</>
|
||||||
</Typography>
|
)}
|
||||||
</>
|
</CardContent>
|
||||||
)
|
</Card>
|
||||||
}
|
)
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,28 @@
|
|||||||
import type { Topology } from '@ethersphere/bee-js';
|
import type { Topology } from '@ethersphere/bee-js'
|
||||||
import { Grid } from '@material-ui/core/';
|
import { Grid } from '@material-ui/core/'
|
||||||
import StatCard from './StatCard';
|
import type { ReactElement } from 'react'
|
||||||
|
import StatCard from './StatCard'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
topology: Topology | null
|
topology: Topology | null
|
||||||
error: Error | null // FIXME: should display error
|
error: Error | null // FIXME: should display error
|
||||||
}
|
}
|
||||||
|
|
||||||
const TopologyStats = ({isLoading, topology, error}: Props) => (
|
const TopologyStats = ({ isLoading, topology }: Props): ReactElement => (
|
||||||
<Grid style={{ marginBottom: '20px', flexGrow: 1 }}>
|
<Grid style={{ marginBottom: '20px', flexGrow: 1 }}>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid key={1} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
<Grid key={1} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
||||||
<StatCard
|
<StatCard label="Connected Peers" statistic={topology?.connected.toString()} loading={isLoading} />
|
||||||
label='Connected Peers'
|
</Grid>
|
||||||
statistic={topology?.connected.toString()}
|
<Grid key={2} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
||||||
loading={isLoading}
|
<StatCard label="Population" statistic={topology?.population.toString()} loading={isLoading} />
|
||||||
/>
|
</Grid>
|
||||||
</Grid>
|
<Grid key={3} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
||||||
<Grid key={2} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
<StatCard label="Depth" statistic={topology?.depth.toString()} loading={isLoading} />
|
||||||
<StatCard
|
</Grid>
|
||||||
label='Population'
|
|
||||||
statistic={topology?.population.toString()}
|
|
||||||
loading={isLoading}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
<Grid key={3} item xs={12} sm={12} md={6} lg={4} xl={4}>
|
|
||||||
<StatCard
|
|
||||||
label='Depth'
|
|
||||||
statistic={topology?.depth.toString()}
|
|
||||||
loading={isLoading}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default TopologyStats
|
export default TopologyStats
|
||||||
|
|||||||
@@ -1,39 +1,47 @@
|
|||||||
import React from 'react';
|
import type { ReactElement } from 'react'
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import { makeStyles, } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Card, CardContent, Typography } from '@material-ui/core/';
|
import { Card, CardContent, Typography } from '@material-ui/core/'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
root: {
|
root: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
marginTop: '20px'
|
marginTop: '20px',
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
textAlign:'center',
|
textAlign: 'center',
|
||||||
fontSize: 26,
|
fontSize: 26,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
|
export default function TroubleshootConnectionCard(): ReactElement {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
export default function TroubleshootConnectionCard() {
|
return (
|
||||||
const classes = useStyles();
|
<Card className={classes.root}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography className={classes.title} gutterBottom>
|
||||||
|
Looks like your node is not connected
|
||||||
|
</Typography>
|
||||||
|
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
|
||||||
|
<strong>
|
||||||
|
<Link to="/">Click to run status checks</Link> on your nodes connection or check out the{' '}
|
||||||
|
<a href={process.env.REACT_APP_BEE_DOCS_HOST} target="_blank" rel="noreferrer">
|
||||||
|
Swarm Bee Docs
|
||||||
|
</a>
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
<div style={{ marginBottom: '20px', textAlign: 'center' }}>
|
||||||
<Card className={classes.root}>
|
<p style={{ marginTop: '50px' }}>
|
||||||
<CardContent>
|
Still not working? Drop us a message on the Ethereum Swarm{' '}
|
||||||
<Typography className={classes.title} gutterBottom>
|
<a href={process.env.REACT_APP_BEE_DISCORD_HOST} target="_blank" rel="noreferrer">
|
||||||
Looks like your node is not connected
|
Discord
|
||||||
</Typography>
|
</a>
|
||||||
<div style={{marginBottom:'20px', textAlign:'center'}}>
|
</p>
|
||||||
<strong><Link to='/' >Click to run status checks</Link> on your nodes connection or check out the <a href={process.env.REACT_APP_BEE_DOCS_HOST} target='_blank' rel="noreferrer">Swarm Bee Docs</a></strong>
|
</div>
|
||||||
</div>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
<div style={{marginBottom:'20px', textAlign:'center'}}>
|
)
|
||||||
<p style={{marginTop:'50px'}}>Still not working? Drop us a message on the Ethereum Swarm <a href={process.env.REACT_APP_BEE_DISCORD_HOST} target='_blank' rel="noreferrer">Discord</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,70 @@
|
|||||||
import React from 'react';
|
import { ReactElement, useState } from 'react'
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button'
|
||||||
import Input from '@material-ui/core/Input';
|
import Input from '@material-ui/core/Input'
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogActions from '@material-ui/core/DialogActions';
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import DialogContent from '@material-ui/core/DialogContent';
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import { Snackbar } from '@material-ui/core';
|
import { Snackbar } from '@material-ui/core'
|
||||||
|
|
||||||
import { beeDebugApi } from '../services/bee';
|
import { beeDebugApi } from '../services/bee'
|
||||||
|
|
||||||
export default function WithdrawlModal() {
|
export default function WithdrawlModal(): ReactElement {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = useState(false)
|
||||||
const [amount, setAmount] = React.useState(BigInt(0));
|
const [amount, setAmount] = useState(BigInt(0))
|
||||||
const [showToast, setToastVisibility] = React.useState(false);
|
const [showToast, setToastVisibility] = useState(false)
|
||||||
const [toastContent, setToastContent] = React.useState('');
|
const [toastContent, setToastContent] = useState('')
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleWithdraw = () => {
|
const handleWithdraw = () => {
|
||||||
if (amount > 0) {
|
if (amount > 0) {
|
||||||
beeDebugApi.chequebook.withdraw(amount)
|
beeDebugApi.chequebook
|
||||||
|
.withdraw(amount)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setOpen(false);
|
setOpen(false)
|
||||||
handleToast(`Successful withdrawl. Transaction ${res.transactionHash}`)
|
handleToast(`Successful withdrawl. Transaction ${res.transactionHash}`)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
handleToast('Error with withdrawl')
|
// FIXME: should probably detail the error
|
||||||
|
handleToast('Error with withdrawing')
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
handleToast('Must be amount of greater than 0')
|
handleToast('Must be amount of greater than 0')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleToast = (text: string) => {
|
const handleToast = (text: string) => {
|
||||||
setToastContent(text)
|
setToastContent(text)
|
||||||
setToastVisibility(true);
|
setToastVisibility(true)
|
||||||
setTimeout(
|
setTimeout(() => setToastVisibility(false), 7000)
|
||||||
() => setToastVisibility(false),
|
}
|
||||||
7000
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
|
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
|
||||||
Withdraw
|
Withdraw
|
||||||
</Button>
|
</Button>
|
||||||
<Snackbar
|
<Snackbar anchorOrigin={{ vertical: 'top', horizontal: 'center' }} open={showToast} message={toastContent} />
|
||||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
|
||||||
open={showToast}
|
|
||||||
message={toastContent}
|
|
||||||
/>
|
|
||||||
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
|
||||||
<DialogTitle id="form-dialog-title">Withdraw Funds</DialogTitle>
|
<DialogTitle id="form-dialog-title">Withdraw Funds</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>Specify the amount you would like to withdraw from your node.</DialogContentText>
|
||||||
Specify the amount you would like to withdraw from your node.
|
|
||||||
</DialogContentText>
|
|
||||||
<Input
|
<Input
|
||||||
autoFocus
|
autoFocus
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="name"
|
id="name"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder='Amount'
|
placeholder="Amount"
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={(e) => setAmount(BigInt(e.target.value))}
|
onChange={e => setAmount(BigInt(e.target.value))}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
@@ -84,5 +77,5 @@ export default function WithdrawlModal() {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+351
-251
@@ -1,293 +1,393 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
import { NodeAddresses, ChequebookAddressResponse, ChequebookBalanceResponse, BalanceResponse,
|
import {
|
||||||
LastChequesResponse, AllSettlements, LastCashoutActionResponse, Health, Peer, Topology, PingResponse, LastChequesForPeerResponse } from '@ethersphere/bee-js'
|
NodeAddresses,
|
||||||
|
ChequebookAddressResponse,
|
||||||
|
ChequebookBalanceResponse,
|
||||||
|
BalanceResponse,
|
||||||
|
LastChequesResponse,
|
||||||
|
AllSettlements,
|
||||||
|
LastCashoutActionResponse,
|
||||||
|
Health,
|
||||||
|
Peer,
|
||||||
|
Topology,
|
||||||
|
PingResponse,
|
||||||
|
LastChequesForPeerResponse,
|
||||||
|
} from '@ethersphere/bee-js'
|
||||||
|
|
||||||
import { beeDebugApi, beeApi } from '../services/bee';
|
import { beeDebugApi, beeApi } from '../services/bee'
|
||||||
|
|
||||||
export const useApiHealth = () => {
|
export interface HealthHook {
|
||||||
const [health, setHealth] = useState<boolean>(false)
|
health: boolean
|
||||||
const [isLoadingHealth, setLoading] = useState<boolean>(false)
|
isLoadingHealth: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
}
|
||||||
|
export const useApiHealth = (): HealthHook => {
|
||||||
|
const [health, setHealth] = useState<boolean>(false)
|
||||||
|
const [isLoadingHealth, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeApi.status.health()
|
beeApi.status
|
||||||
.then(res => {
|
.health()
|
||||||
setHealth(res)
|
.then(res => {
|
||||||
})
|
setHealth(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { health, isLoadingHealth, error } ;
|
return { health, isLoadingHealth, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDebugApiHealth = () => {
|
export interface DebugHealthHook {
|
||||||
const [nodeHealth, setNodeHealth] = useState<Health | null>(null)
|
nodeHealth: Health | null
|
||||||
const [isLoadingNodeHealth, setLoading] = useState<boolean>(false)
|
isLoadingNodeHealth: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
|
||||||
beeDebugApi.status.nodeHealth()
|
|
||||||
.then(res => {
|
|
||||||
setNodeHealth(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { nodeHealth, isLoadingNodeHealth, error } ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiNodeAddresses = () => {
|
export const useDebugApiHealth = (): DebugHealthHook => {
|
||||||
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
const [nodeHealth, setNodeHealth] = useState<Health | null>(null)
|
||||||
const [isLoadingNodeAddresses, setLoading] = useState<boolean>(false)
|
const [isLoadingNodeHealth, setLoading] = useState<boolean>(false)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeDebugApi.connectivity.addresses()
|
beeDebugApi.status
|
||||||
.then(res => {
|
.nodeHealth()
|
||||||
setNodeAddresses(res)
|
.then(res => {
|
||||||
})
|
setNodeHealth(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { nodeAddresses, isLoadingNodeAddresses, error } ;
|
return { nodeHealth, isLoadingNodeHealth, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiNodeTopology = () => {
|
export interface NodeAddressesHook {
|
||||||
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
nodeAddresses: NodeAddresses | null
|
||||||
const [isLoading, setLoading] = useState<boolean>(false)
|
isLoadingNodeAddresses: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
|
||||||
beeDebugApi.connectivity.topology()
|
|
||||||
.then(res => {
|
|
||||||
setNodeTopology(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { topology, isLoading, error } ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiChequebookAddress = () => {
|
export const useApiNodeAddresses = (): NodeAddressesHook => {
|
||||||
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
|
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
|
||||||
const [isLoadingChequebookAddress, setLoading] = useState<boolean>(false)
|
const [isLoadingNodeAddresses, setLoading] = useState<boolean>(false)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeDebugApi.chequebook.address()
|
beeDebugApi.connectivity
|
||||||
.then(res => {
|
.addresses()
|
||||||
setChequebookAddress(res)
|
.then(res => {
|
||||||
})
|
setNodeAddresses(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { chequebookAddress, isLoadingChequebookAddress, error };
|
return { nodeAddresses, isLoadingNodeAddresses, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiNodePeers = () => {
|
export interface NodeTopologyHook {
|
||||||
const [peers, setPeers] = useState<Peer[] | null>(null)
|
topology: Topology | null
|
||||||
const [isLoading, setLoading] = useState<boolean>(false)
|
isLoading: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
|
||||||
beeDebugApi.connectivity.listPeers()
|
|
||||||
.then(res => {
|
|
||||||
setPeers(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { peers, isLoading, error };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiChequebookBalance = () => {
|
export const useApiNodeTopology = (): NodeTopologyHook => {
|
||||||
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalanceResponse | null>(null)
|
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
||||||
const [isLoadingChequebookBalance, setLoading] = useState<boolean>(false)
|
const [isLoading, setLoading] = useState<boolean>(false)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeDebugApi.chequebook.balance()
|
beeDebugApi.connectivity
|
||||||
.then(res => {
|
.topology()
|
||||||
setChequebookBalance(res)
|
.then(res => {
|
||||||
})
|
setNodeTopology(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { chequebookBalance, isLoadingChequebookBalance, error };
|
return { topology, isLoading, error }
|
||||||
|
}
|
||||||
|
export interface ChequebookAddressHook {
|
||||||
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
|
isLoadingChequebookAddress: boolean
|
||||||
|
error: Error | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiPeerBalances = () => {
|
export const useApiChequebookAddress = (): ChequebookAddressHook => {
|
||||||
const [peerBalances, setPeerBalances] = useState<BalanceResponse | null>(null)
|
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
|
||||||
const [isLoadingPeerBalances, setLoading] = useState<boolean>(false)
|
const [isLoadingChequebookAddress, setLoading] = useState<boolean>(false)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeDebugApi.balance.balances()
|
beeDebugApi.chequebook
|
||||||
.then(res => {
|
.address()
|
||||||
setPeerBalances(res)
|
.then(res => {
|
||||||
})
|
setChequebookAddress(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { peerBalances, isLoadingPeerBalances, error };
|
return { chequebookAddress, isLoadingChequebookAddress, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiPeerCheques = () => {
|
export interface NodePeersHook {
|
||||||
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
peers: Peer[] | null
|
||||||
const [isLoadingPeerCheques, setLoading] = useState<boolean>(false)
|
isLoading: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
|
||||||
beeDebugApi.chequebook.getLastCheques()
|
|
||||||
.then(res => {
|
|
||||||
setPeerCheques(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { peerCheques, isLoadingPeerCheques, error };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiPeerLastCheque = (peerId: string) => {
|
export const useApiNodePeers = (): NodePeersHook => {
|
||||||
const [peerCheque, setPeerCheque] = useState<LastChequesForPeerResponse | null>(null)
|
const [peers, setPeers] = useState<Peer[] | null>(null)
|
||||||
const [isLoadingPeerCheque, setLoading] = useState<boolean>(false)
|
const [isLoading, setLoading] = useState<boolean>(false)
|
||||||
const [error, setError] = useState<Error | null>(null)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
beeDebugApi.chequebook.getPeerLastCheques(peerId)
|
beeDebugApi.connectivity
|
||||||
.then(res => {
|
.listPeers()
|
||||||
setPeerCheque(res)
|
.then(res => {
|
||||||
})
|
setPeers(res)
|
||||||
.catch(error => {
|
})
|
||||||
setError(error)
|
.catch(error => {
|
||||||
})
|
setError(error)
|
||||||
.finally(() => {
|
})
|
||||||
setLoading(false)
|
.finally(() => {
|
||||||
})
|
setLoading(false)
|
||||||
}, [peerId])
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return { peerCheque, isLoadingPeerCheque, error };
|
return { peers, isLoading, error }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiSettlements = () => {
|
export interface ChequebookBalanceHook {
|
||||||
const [settlements, setSettlements] = useState<AllSettlements | null>(null)
|
chequebookBalance: ChequebookBalanceResponse | null
|
||||||
const [isLoadingSettlements, setLoading] = useState<boolean>(false)
|
isLoadingChequebookBalance: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
|
||||||
beeDebugApi.settlements.getSettlements()
|
|
||||||
.then(res => {
|
|
||||||
setSettlements(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return { settlements, isLoadingSettlements, error };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useApiChequebookBalance = (): ChequebookBalanceHook => {
|
||||||
|
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalanceResponse | null>(null)
|
||||||
|
const [isLoadingChequebookBalance, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
export const useApiPingPeer = (peerId: string) => {
|
useEffect(() => {
|
||||||
const [peerRTP, setPeerRTP] = useState<PingResponse | null>()
|
setLoading(true)
|
||||||
const [isPingingPeer, setPingingPeer] = useState<boolean>(false)
|
beeDebugApi.chequebook
|
||||||
const [error, setError] = useState<Error | null>(null)
|
.balance()
|
||||||
|
.then(res => {
|
||||||
|
setChequebookBalance(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
return { chequebookBalance, isLoadingChequebookBalance, error }
|
||||||
setPingingPeer(true)
|
|
||||||
beeDebugApi.connectivity.ping(peerId)
|
|
||||||
.then(res => {
|
|
||||||
setPeerRTP(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
setError(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setPingingPeer(false)
|
|
||||||
})
|
|
||||||
}, [peerId])
|
|
||||||
|
|
||||||
return { peerRTP, isPingingPeer, error };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useApiPeerLastCashout = (peerId: string) => {
|
export interface PeerBalanceHook {
|
||||||
const [peerCashout, setPeerCashout] = useState<LastCashoutActionResponse | null>(null)
|
peerBalances: BalanceResponse | null
|
||||||
const [isLoadingPeerCashout, setLoading] = useState<boolean>(false)
|
isLoadingPeerBalances: boolean
|
||||||
const [error, setError] = useState<Error | null>(null)
|
error: Error | null
|
||||||
|
}
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true)
|
export const useApiPeerBalances = (): PeerBalanceHook => {
|
||||||
beeDebugApi.chequebook.getPeerLastCashout(peerId)
|
const [peerBalances, setPeerBalances] = useState<BalanceResponse | null>(null)
|
||||||
.then(res => {
|
const [isLoadingPeerBalances, setLoading] = useState<boolean>(false)
|
||||||
setPeerCashout(res)
|
const [error, setError] = useState<Error | null>(null)
|
||||||
})
|
|
||||||
.catch(error => {
|
useEffect(() => {
|
||||||
setError(error)
|
setLoading(true)
|
||||||
})
|
beeDebugApi.balance
|
||||||
.finally(() => {
|
.balances()
|
||||||
setLoading(false)
|
.then(res => {
|
||||||
})
|
setPeerBalances(res)
|
||||||
}, [peerId])
|
})
|
||||||
|
.catch(error => {
|
||||||
return { peerCashout, isLoadingPeerCashout, error };
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { peerBalances, isLoadingPeerBalances, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeerChequesHook {
|
||||||
|
peerCheques: LastChequesResponse | null
|
||||||
|
isLoadingPeerCheques: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiPeerCheques = (): PeerChequesHook => {
|
||||||
|
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
||||||
|
const [isLoadingPeerCheques, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
beeDebugApi.chequebook
|
||||||
|
.getLastCheques()
|
||||||
|
.then(res => {
|
||||||
|
setPeerCheques(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { peerCheques, isLoadingPeerCheques, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeerLastChequesHook {
|
||||||
|
peerCheque: LastChequesForPeerResponse | null
|
||||||
|
isLoadingPeerCheque: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiPeerLastCheque = (peerId: string): PeerLastChequesHook => {
|
||||||
|
const [peerCheque, setPeerCheque] = useState<LastChequesForPeerResponse | null>(null)
|
||||||
|
const [isLoadingPeerCheque, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
beeDebugApi.chequebook
|
||||||
|
.getPeerLastCheques(peerId)
|
||||||
|
.then(res => {
|
||||||
|
setPeerCheque(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [peerId])
|
||||||
|
|
||||||
|
return { peerCheque, isLoadingPeerCheque, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SettlementsHook {
|
||||||
|
settlements: AllSettlements | null
|
||||||
|
isLoadingSettlements: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiSettlements = (): SettlementsHook => {
|
||||||
|
const [settlements, setSettlements] = useState<AllSettlements | null>(null)
|
||||||
|
const [isLoadingSettlements, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
beeDebugApi.settlements
|
||||||
|
.getSettlements()
|
||||||
|
.then(res => {
|
||||||
|
setSettlements(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { settlements, isLoadingSettlements, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PingPeerHook {
|
||||||
|
peerRTP: PingResponse | null
|
||||||
|
isPingingPeer: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiPingPeer = (peerId: string): PingPeerHook => {
|
||||||
|
const [peerRTP, setPeerRTP] = useState<PingResponse | null>(null)
|
||||||
|
const [isPingingPeer, setPingingPeer] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPingingPeer(true)
|
||||||
|
beeDebugApi.connectivity
|
||||||
|
.ping(peerId)
|
||||||
|
.then(res => {
|
||||||
|
setPeerRTP(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setPingingPeer(false)
|
||||||
|
})
|
||||||
|
}, [peerId])
|
||||||
|
|
||||||
|
return { peerRTP, isPingingPeer, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeerLastCashoutHook {
|
||||||
|
peerCashout: LastCashoutActionResponse | null
|
||||||
|
isLoadingPeerCashout: boolean
|
||||||
|
error: Error | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useApiPeerLastCashout = (peerId: string): PeerLastCashoutHook => {
|
||||||
|
const [peerCashout, setPeerCashout] = useState<LastCashoutActionResponse | null>(null)
|
||||||
|
const [isLoadingPeerCashout, setLoading] = useState<boolean>(false)
|
||||||
|
const [error, setError] = useState<Error | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
beeDebugApi.chequebook
|
||||||
|
.getPeerLastCashout(peerId)
|
||||||
|
.then(res => {
|
||||||
|
setPeerCashout(res)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
setError(error)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
|
}, [peerId])
|
||||||
|
|
||||||
|
return { peerCashout, isLoadingPeerCashout, error }
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -1,17 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom'
|
||||||
import './index.css';
|
import './index.css'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals'
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root'),
|
||||||
);
|
)
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
reportWebVitals();
|
reportWebVitals()
|
||||||
|
|||||||
+41
-40
@@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, ReactElement } from 'react'
|
||||||
|
|
||||||
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'
|
||||||
|
|
||||||
import SideBar from '../components/SideBar';
|
import SideBar from '../components/SideBar'
|
||||||
import NavBar from '../components/NavBar';
|
import NavBar from '../components/NavBar'
|
||||||
|
|
||||||
import { useApiHealth, useDebugApiHealth } from '../hooks/apiHooks';
|
|
||||||
|
|
||||||
|
import { useApiHealth, useDebugApiHealth } from '../hooks/apiHooks'
|
||||||
|
import { RouteComponentProps } from 'react-router'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -16,7 +16,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
paddingBottom:'65px',
|
paddingBottom: '65px',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
marginLeft: '240px',
|
marginLeft: '240px',
|
||||||
@@ -24,55 +24,56 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
width:'-webkit-fill-available',
|
width: '-webkit-fill-available',
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
textAlign:'center'
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
logo: {
|
logo: {
|
||||||
height: '20px',
|
height: '20px',
|
||||||
marginRight: '7px',
|
marginRight: '7px',
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
|
interface Props extends RouteComponentProps {
|
||||||
|
children?: ReactElement
|
||||||
|
}
|
||||||
|
|
||||||
const Dashboard = (props: any) => {
|
const Dashboard = (props: Props): ReactElement => {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
const [themeMode, toggleThemeMode] = useState('light');
|
const [themeMode, toggleThemeMode] = useState('light')
|
||||||
|
|
||||||
const { health, isLoadingHealth } = useApiHealth()
|
// FIXME: handle errrors and loading
|
||||||
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
|
const { health } = useApiHealth()
|
||||||
|
const { nodeHealth } = useDebugApiHealth()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let theme = localStorage.getItem('theme')
|
const theme = localStorage.getItem('theme')
|
||||||
|
|
||||||
if (theme) {
|
if (theme) {
|
||||||
toggleThemeMode(String(localStorage.getItem('theme')))
|
toggleThemeMode(String(localStorage.getItem('theme')))
|
||||||
} else if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) {
|
} else if (window?.matchMedia('(prefers-color-scheme: dark)')?.matches) {
|
||||||
toggleThemeMode('dark')
|
toggleThemeMode('dark')
|
||||||
}
|
}
|
||||||
|
|
||||||
window?.matchMedia('(prefers-color-scheme: dark)')?.addEventListener('change', e => {
|
window?.matchMedia('(prefers-color-scheme: dark)')?.addEventListener('change', e => {
|
||||||
toggleThemeMode(e?.matches ? "dark" : "light")
|
toggleThemeMode(e?.matches ? 'dark' : 'light')
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => window?.matchMedia('(prefers-color-scheme: dark)')?.removeEventListener('change', e => {
|
return () =>
|
||||||
toggleThemeMode(e?.matches ? "dark" : "light")
|
window?.matchMedia('(prefers-color-scheme: dark)')?.removeEventListener('change', e => {
|
||||||
|
toggleThemeMode(e?.matches ? 'dark' : 'light')
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
let childrenInjectedWithProps = React.cloneElement(props.children, { health, nodeHealth, isLoadingHealth, isLoadingNodeHealth })
|
return (
|
||||||
|
<div>
|
||||||
return (
|
<SideBar {...props} themeMode={themeMode} health={health} nodeHealth={nodeHealth} />
|
||||||
<div>
|
<NavBar themeMode={themeMode} />
|
||||||
<SideBar {...props} themeMode={themeMode} health={health} nodeHealth={nodeHealth} />
|
<main className={classes.content}>{props.children}</main>
|
||||||
<NavBar themeMode={themeMode} />
|
</div>
|
||||||
<main className={classes.content} >
|
)
|
||||||
{childrenInjectedWithProps}
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard
|
export default Dashboard
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import React from 'react'
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
import { Card, CardContent, Typography, Grid } from '@material-ui/core/';
|
import { Card, CardContent, Typography, Grid } from '@material-ui/core/'
|
||||||
import { Skeleton } from '@material-ui/lab';
|
import { Skeleton } from '@material-ui/lab'
|
||||||
import WithdrawlModal from '../../components/WithdrawlModal';
|
import WithdrawlModal from '../../components/WithdrawlModal'
|
||||||
import DepositModal from '../../components/DepositModal';
|
import DepositModal from '../../components/DepositModal'
|
||||||
import CashoutModal from '../../components/CashoutModal';
|
import CashoutModal from '../../components/CashoutModal'
|
||||||
|
|
||||||
import { ConvertBalanceToBZZ } from '../../utils/common';
|
import { ConvertBalanceToBZZ } from '../../utils/common'
|
||||||
|
|
||||||
import type { ChequebookAddressResponse } from '@ethersphere/bee-js';
|
import type { AllSettlements, ChequebookAddressResponse } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -20,93 +20,107 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
},
|
},
|
||||||
address: {
|
address: {
|
||||||
color: 'grey',
|
color: 'grey',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
backgroundColor: '#76a9fa',
|
backgroundColor: '#76a9fa',
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
|
|
||||||
interface ChequebookBalance {
|
interface ChequebookBalance {
|
||||||
totalBalance: number,
|
totalBalance: number
|
||||||
availableBalance: number
|
availableBalance: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps{
|
interface Props {
|
||||||
chequebookAddress: ChequebookAddressResponse | null,
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
isLoadingChequebookAddress: boolean,
|
isLoadingChequebookAddress: boolean
|
||||||
chequebookBalance: ChequebookBalance | null,
|
chequebookBalance: ChequebookBalance | null
|
||||||
isLoadingChequebookBalance: boolean,
|
isLoadingChequebookBalance: boolean
|
||||||
settlements: any,
|
settlements: AllSettlements | null
|
||||||
isLoadingSettlements: boolean,
|
isLoadingSettlements: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AccountCard(props: Props): ReactElement {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
function AccountCard(props: IProps) {
|
return (
|
||||||
const classes = useStyles();
|
<div>
|
||||||
|
<div style={{ justifyContent: 'space-between', display: 'flex' }}>
|
||||||
return (
|
<h2 style={{ marginTop: '0px' }}>Accounting</h2>
|
||||||
<div>
|
<div style={{ display: 'flex' }}>
|
||||||
<div style={{justifyContent: 'space-between', display: 'flex'}}>
|
<WithdrawlModal />
|
||||||
<h2 style={{ marginTop: '0px' }}>Accounting</h2>
|
<DepositModal />
|
||||||
<div style={{display:'flex'}}>
|
<CashoutModal />
|
||||||
<WithdrawlModal />
|
|
||||||
<DepositModal />
|
|
||||||
<CashoutModal />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card className={classes.root}>
|
|
||||||
{ !props.isLoadingChequebookBalance && !props.isLoadingSettlements && props.chequebookBalance ?
|
|
||||||
<div className={classes.details}>
|
|
||||||
<CardContent className={classes.content}>
|
|
||||||
<Grid container spacing={5}>
|
|
||||||
<Grid item>
|
|
||||||
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
|
||||||
Total Balance
|
|
||||||
</Typography>
|
|
||||||
<Typography component="p" variant="h5">
|
|
||||||
{ConvertBalanceToBZZ(props.chequebookBalance.totalBalance)}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
|
||||||
Available Balance
|
|
||||||
</Typography>
|
|
||||||
<Typography component="p" variant="h5">
|
|
||||||
{ConvertBalanceToBZZ(props.chequebookBalance.availableBalance)}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
|
||||||
Total Sent / Received
|
|
||||||
</Typography>
|
|
||||||
<Typography component="div" variant="h5" >
|
|
||||||
<span style={{marginRight:'7px'}}>{ConvertBalanceToBZZ(props.settlements.totalsent)} / {ConvertBalanceToBZZ(props.settlements.totalreceived)}</span>
|
|
||||||
<span style={{ color: props.settlements.totalsent > props.settlements.totalreceived ? '#c9201f' : '#32c48d' }}>({ConvertBalanceToBZZ(props.settlements.totalsent - props.settlements.totalreceived)})</span>
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</CardContent>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div className={classes.details}>
|
|
||||||
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px'}} />
|
|
||||||
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px'}} />
|
|
||||||
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px'}} />
|
|
||||||
<Skeleton width={180} height={110} animation="wave" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
|
|
||||||
|
<Card className={classes.root}>
|
||||||
|
{!props.isLoadingChequebookBalance && !props.isLoadingSettlements && props.chequebookBalance ? (
|
||||||
|
<div className={classes.details}>
|
||||||
|
<CardContent className={classes.content}>
|
||||||
|
<Grid container spacing={5}>
|
||||||
|
<Grid item>
|
||||||
|
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
||||||
|
Total Balance
|
||||||
|
</Typography>
|
||||||
|
<Typography component="p" variant="h5">
|
||||||
|
{ConvertBalanceToBZZ(props.chequebookBalance.totalBalance)}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
||||||
|
Available Balance
|
||||||
|
</Typography>
|
||||||
|
<Typography component="p" variant="h5">
|
||||||
|
{ConvertBalanceToBZZ(props.chequebookBalance.availableBalance)}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography component="h2" variant="h6" color="primary" gutterBottom>
|
||||||
|
Total Sent / Received
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div" variant="h5">
|
||||||
|
<span style={{ marginRight: '7px' }}>
|
||||||
|
{ConvertBalanceToBZZ(props.settlements?.totalsent || 0)} /{' '}
|
||||||
|
{ConvertBalanceToBZZ(props.settlements?.totalreceived || 0)}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
props.settlements && props.settlements?.totalsent > props.settlements?.totalreceived
|
||||||
|
? '#c9201f'
|
||||||
|
: '#32c48d',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
(
|
||||||
|
{ConvertBalanceToBZZ(
|
||||||
|
(props.settlements && props.settlements?.totalsent - props.settlements?.totalreceived) || 0,
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</CardContent>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={classes.details}>
|
||||||
|
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px' }} />
|
||||||
|
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px' }} />
|
||||||
|
<Skeleton width={180} height={110} animation="wave" style={{ marginLeft: '12px', marginRight: '12px' }} />
|
||||||
|
<Skeleton width={180} height={110} animation="wave" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AccountCard;
|
export default AccountCard
|
||||||
|
|||||||
@@ -1,64 +1,80 @@
|
|||||||
import React from 'react';
|
import type { ReactElement } from 'react'
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableRow, TableHead, Paper, Container, CircularProgress } from '@material-ui/core';
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
Paper,
|
||||||
|
Container,
|
||||||
|
CircularProgress,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
import { ConvertBalanceToBZZ } from '../../utils/common';
|
import { ConvertBalanceToBZZ } from '../../utils/common'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
table: {
|
table: {
|
||||||
minWidth: 650,
|
minWidth: 650,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
interface PeerBalance {
|
interface PeerBalance {
|
||||||
balance: number,
|
balance: number
|
||||||
peer: string,
|
peer: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PeerBalances {
|
interface PeerBalances {
|
||||||
balances: Array<PeerBalance>
|
balances: Array<PeerBalance>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
peerBalances: PeerBalances | null,
|
peerBalances: PeerBalances | null
|
||||||
loading?: boolean,
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function BalancesTable(props: IProps) {
|
function BalancesTable(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{props.loading ?
|
{props.loading ? (
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
:
|
) : (
|
||||||
<TableContainer component={Paper}>
|
<TableContainer component={Paper}>
|
||||||
<Table className={classes.table} size="small" aria-label="simple table">
|
<Table className={classes.table} size="small" aria-label="simple table">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Peer</TableCell>
|
<TableCell>Peer</TableCell>
|
||||||
<TableCell style={{textAlign:'right'}}>Balance (BZZ)</TableCell>
|
<TableCell style={{ textAlign: 'right' }}>Balance (BZZ)</TableCell>
|
||||||
<TableCell align="right"></TableCell>
|
<TableCell align="right"></TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{props.peerBalances?.balances.map((peerBalance: PeerBalance, idx: number) => (
|
{props.peerBalances?.balances.map((peerBalance: PeerBalance) => (
|
||||||
<TableRow key={peerBalance.peer}>
|
<TableRow key={peerBalance.peer}>
|
||||||
<TableCell>{peerBalance.peer}</TableCell>
|
<TableCell>{peerBalance.peer}</TableCell>
|
||||||
<TableCell style={{ color: ConvertBalanceToBZZ(peerBalance.balance) > 0 ? '#32c48d' : '#c9201f', textAlign:'right', fontFamily: 'monospace, monospace' }}>
|
<TableCell
|
||||||
{ConvertBalanceToBZZ(peerBalance.balance).toFixed(7).toLocaleString() }
|
style={{
|
||||||
</TableCell>
|
color: ConvertBalanceToBZZ(peerBalance.balance) > 0 ? '#32c48d' : '#c9201f',
|
||||||
<TableCell align="right">
|
textAlign: 'right',
|
||||||
</TableCell>
|
fontFamily: 'monospace, monospace',
|
||||||
</TableRow>
|
}}
|
||||||
))}
|
>
|
||||||
</TableBody>
|
{ConvertBalanceToBZZ(peerBalance.balance).toFixed(7).toLocaleString()}
|
||||||
</Table>
|
</TableCell>
|
||||||
</TableContainer>}
|
<TableCell align="right"></TableCell>
|
||||||
</div>
|
</TableRow>
|
||||||
)
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BalancesTable;
|
export default BalancesTable
|
||||||
|
|||||||
@@ -1,113 +1,125 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement } from 'react'
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableRow, TableHead, Paper, Container, CircularProgress } from '@material-ui/core';
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
Paper,
|
||||||
|
Container,
|
||||||
|
CircularProgress,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
import { ConvertBalanceToBZZ } from '../../utils/common';
|
import { ConvertBalanceToBZZ } from '../../utils/common'
|
||||||
import EthereumAddress from '../../components/EthereumAddress';
|
import EthereumAddress from '../../components/EthereumAddress'
|
||||||
import ClipboardCopy from '../../components/ClipboardCopy';
|
import ClipboardCopy from '../../components/ClipboardCopy'
|
||||||
import PeerDetailDrawer from './PeerDetailDrawer';
|
import PeerDetailDrawer from './PeerDetailDrawer'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
table: {
|
table: {
|
||||||
minWidth: 650,
|
minWidth: 650,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
interface ChequeEvent {
|
interface ChequeEvent {
|
||||||
beneficiary: string,
|
beneficiary: string
|
||||||
chequebook: string,
|
chequebook: string
|
||||||
payout: number,
|
payout: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PeerCheque {
|
interface PeerCheque {
|
||||||
lastreceived?: ChequeEvent,
|
lastreceived?: ChequeEvent
|
||||||
lastsent?: ChequeEvent,
|
lastsent?: ChequeEvent
|
||||||
peer: string,
|
peer: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PeerCheques {
|
interface PeerCheques {
|
||||||
lastcheques: Array<PeerCheque>
|
lastcheques: Array<PeerCheque>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface Props {
|
||||||
peerCheques: PeerCheques | null,
|
peerCheques: PeerCheques | null
|
||||||
loading?: boolean,
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChequebookTable(props: IProps) {
|
function ChequebookTable(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
|
{props.loading ? (
|
||||||
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Container>
|
||||||
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{props.loading ?
|
<TableContainer component={Paper}>
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Table className={classes.table} size="small" aria-label="simple table">
|
||||||
<CircularProgress />
|
<TableHead>
|
||||||
</Container>
|
<TableRow>
|
||||||
:
|
<TableCell>Peer</TableCell>
|
||||||
<div>
|
<TableCell>Last Received</TableCell>
|
||||||
<TableContainer component={Paper}>
|
<TableCell>Last Sent</TableCell>
|
||||||
<Table className={classes.table} size="small" aria-label="simple table">
|
<TableCell align="right"></TableCell>
|
||||||
<TableHead>
|
</TableRow>
|
||||||
<TableRow>
|
</TableHead>
|
||||||
<TableCell>Peer</TableCell>
|
<TableBody>
|
||||||
<TableCell>Last Received</TableCell>
|
{props.peerCheques?.lastcheques.map((peerCheque: PeerCheque) => (
|
||||||
<TableCell>Last Sent</TableCell>
|
<TableRow key={peerCheque.peer}>
|
||||||
<TableCell align="right"></TableCell>
|
<TableCell>
|
||||||
</TableRow>
|
<div style={{ display: 'flex' }}>
|
||||||
</TableHead>
|
<small>
|
||||||
<TableBody>
|
<PeerDetailDrawer peerId={peerCheque.peer} />
|
||||||
{props.peerCheques?.lastcheques.map((peerCheque: PeerCheque, idx: number) => (
|
</small>
|
||||||
<TableRow key={peerCheque.peer}>
|
<ClipboardCopy value={peerCheque.peer} />
|
||||||
<TableCell>
|
</div>
|
||||||
<div style={{display:'flex'}}>
|
</TableCell>
|
||||||
<small>
|
<TableCell style={{ maxWidth: '320px' }}>
|
||||||
<PeerDetailDrawer
|
<p style={{ marginBottom: '0px', fontFamily: 'monospace, monospace', display: 'flex' }}>
|
||||||
peerId={peerCheque.peer}
|
<span style={{ whiteSpace: 'nowrap', marginRight: '12px', paddingTop: '3px' }}>
|
||||||
/>
|
{peerCheque.lastreceived?.payout
|
||||||
</small>
|
? `${ConvertBalanceToBZZ(peerCheque.lastreceived?.payout).toFixed(7).toLocaleString()} from`
|
||||||
<ClipboardCopy value={peerCheque.peer} />
|
: '-'}
|
||||||
</div>
|
</span>
|
||||||
</TableCell>
|
{peerCheque.lastreceived ? (
|
||||||
<TableCell style={{maxWidth:'320px'}}>
|
<EthereumAddress
|
||||||
<p style={{marginBottom: '0px', fontFamily: 'monospace, monospace', display:'flex'}}>
|
hideBlockie
|
||||||
<span style={{whiteSpace: 'nowrap', marginRight:'12px', paddingTop:'3px'}}>
|
truncate
|
||||||
{peerCheque.lastreceived?.payout ?
|
network="goerli"
|
||||||
`${ConvertBalanceToBZZ(peerCheque.lastreceived?.payout).toFixed(7).toLocaleString()} from`
|
address={peerCheque.lastreceived.beneficiary}
|
||||||
: '-'}
|
/>
|
||||||
</span>
|
) : null}
|
||||||
{peerCheque.lastreceived ?
|
</p>
|
||||||
<EthereumAddress
|
</TableCell>
|
||||||
hideBlockie
|
<TableCell style={{ maxWidth: '320px' }}>
|
||||||
truncate
|
<p style={{ marginBottom: '0px', fontFamily: 'monospace, monospace', display: 'flex' }}>
|
||||||
network='goerli'
|
<span style={{ whiteSpace: 'nowrap', marginRight: '12px', paddingTop: '3px' }}>
|
||||||
address={peerCheque.lastreceived.beneficiary}
|
{peerCheque.lastsent?.payout
|
||||||
/> : null}
|
? `${ConvertBalanceToBZZ(peerCheque.lastsent?.payout).toFixed(7).toLocaleString()} to`
|
||||||
</p>
|
: '-'}
|
||||||
</TableCell>
|
</span>
|
||||||
<TableCell style={{maxWidth:'320px'}}>
|
{peerCheque.lastsent ? (
|
||||||
<p style={{marginBottom: '0px', fontFamily: 'monospace, monospace', display:'flex'}}>
|
<EthereumAddress
|
||||||
<span style={{whiteSpace: 'nowrap', marginRight:'12px', paddingTop:'3px'}}>
|
hideBlockie
|
||||||
{peerCheque.lastsent?.payout ? `${ConvertBalanceToBZZ(peerCheque.lastsent?.payout).toFixed(7).toLocaleString()} to` : '-'}
|
truncate
|
||||||
</span>
|
network="goerli"
|
||||||
{peerCheque.lastsent ?
|
address={peerCheque.lastsent.beneficiary}
|
||||||
<EthereumAddress
|
/>
|
||||||
hideBlockie
|
) : null}
|
||||||
truncate
|
</p>
|
||||||
network='goerli'
|
</TableCell>
|
||||||
address={peerCheque.lastsent.beneficiary}
|
<TableCell align="right"></TableCell>
|
||||||
/> : null}
|
</TableRow>
|
||||||
</p>
|
))}
|
||||||
</TableCell>
|
</TableBody>
|
||||||
<TableCell align="right">
|
</Table>
|
||||||
</TableCell>
|
</TableContainer>
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</div>}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChequebookTable;
|
export default ChequebookTable
|
||||||
|
|||||||
@@ -1,188 +1,170 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { Paper, Container, Drawer, Button, Typography, CircularProgress, Grid } from '@material-ui/core';
|
import { Paper, Container, Drawer, Button, Typography, CircularProgress, Grid } from '@material-ui/core'
|
||||||
import ClipboardCopy from '../../components/ClipboardCopy';
|
import ClipboardCopy from '../../components/ClipboardCopy'
|
||||||
import { beeDebugApi } from '../../services/bee';
|
import { beeDebugApi } from '../../services/bee'
|
||||||
import EthereumAddress from '../../components/EthereumAddress';
|
import EthereumAddress from '../../components/EthereumAddress'
|
||||||
import { ConvertBalanceToBZZ } from '../../utils/common';
|
import { ConvertBalanceToBZZ } from '../../utils/common'
|
||||||
|
import { LastCashoutActionResponse, LastChequesForPeerResponse } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
function truncStringPortion(str: string, firstCharCount=10, endCharCount=10) {
|
function truncStringPortion(str: string, firstCharCount = 10, endCharCount = 10) {
|
||||||
var convertedStr="";
|
let convertedStr = ''
|
||||||
convertedStr+=str.substring(0, firstCharCount);
|
convertedStr += str.substring(0, firstCharCount)
|
||||||
convertedStr += ".".repeat(3);
|
convertedStr += '.'.repeat(3)
|
||||||
convertedStr+=str.substring(str.length-endCharCount, str.length);
|
convertedStr += str.substring(str.length - endCharCount, str.length)
|
||||||
return convertedStr;
|
|
||||||
|
return convertedStr
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Index(props: any) {
|
interface Props {
|
||||||
const [open, setOpen] = useState(false);
|
peerId: string
|
||||||
const [peerCashout, setPeerCashout] = useState({ "peer": "",
|
}
|
||||||
"chequebook": "",
|
|
||||||
"cumulativePayout": 0,
|
|
||||||
"beneficiary": "",
|
|
||||||
"transactionHash": "",
|
|
||||||
"result": {
|
|
||||||
"recipient": "",
|
|
||||||
"lastPayout": 0,
|
|
||||||
"bounced": false
|
|
||||||
}});
|
|
||||||
|
|
||||||
const [peerCheque, setPeerCheque] = useState({
|
export default function Index(props: Props): ReactElement {
|
||||||
lastreceived: { beneficiary: "", payout: 0, chequebook: "" },
|
const [open, setOpen] = useState(false)
|
||||||
lastsent: { beneficiary: "", payout: 0, chequebook: "" },
|
const [peerCashout, setPeerCashout] = useState<LastCashoutActionResponse | null>(null)
|
||||||
peer: ""
|
const [peerCheque, setPeerCheque] = useState<LastChequesForPeerResponse | null>(null)
|
||||||
})
|
|
||||||
|
|
||||||
const [isLoadingPeerCheque, setIsLoadingPeerCheque] = useState<boolean>(false)
|
const [isLoadingPeerCheque, setIsLoadingPeerCheque] = useState<boolean>(false)
|
||||||
const [isLoadingPeerCashout, setIsLoadingPeerCashout] = useState<boolean>(false);
|
const [isLoadingPeerCashout, setIsLoadingPeerCashout] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const handleClickOpen = (peerId: string) => {
|
||||||
|
setIsLoadingPeerCashout(true)
|
||||||
|
beeDebugApi.chequebook
|
||||||
|
.getPeerLastCashout(peerId)
|
||||||
|
.then(res => {
|
||||||
|
setPeerCashout(res)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// FIXME: handle the error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoadingPeerCashout(false)
|
||||||
|
})
|
||||||
|
|
||||||
const handleClickOpen = (peerId: string) => {
|
setIsLoadingPeerCheque(true)
|
||||||
setIsLoadingPeerCashout(true)
|
beeDebugApi.chequebook
|
||||||
beeDebugApi.chequebook.getPeerLastCashout(peerId)
|
.getPeerLastCheques(peerId)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setPeerCashout(res)
|
setPeerCheque(res)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
})
|
// FIXME: handle the error
|
||||||
.finally(() => {
|
})
|
||||||
setIsLoadingPeerCashout(false)
|
.finally(() => {
|
||||||
})
|
setIsLoadingPeerCheque(false)
|
||||||
|
})
|
||||||
|
|
||||||
setIsLoadingPeerCheque(true)
|
setOpen(true)
|
||||||
beeDebugApi.chequebook.getPeerLastCheques(peerId)
|
}
|
||||||
.then(res => {
|
|
||||||
setPeerCheque(res)
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoadingPeerCheque(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
setOpen(true);
|
const handleClose = () => {
|
||||||
}
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
const handleClose = () => {
|
return (
|
||||||
setOpen(false);
|
<div>
|
||||||
};
|
<Button color="primary" onClick={() => handleClickOpen(props.peerId)}>
|
||||||
|
{truncStringPortion(props.peerId)}
|
||||||
|
</Button>
|
||||||
return (
|
<Drawer anchor={'right'} open={open} onClose={handleClose}>
|
||||||
<div>
|
<div style={{ padding: '20px' }}>
|
||||||
<Button color="primary" onClick={() => handleClickOpen(props.peerId)}>{truncStringPortion(props.peerId)}</Button>
|
<Typography variant="h5" gutterBottom style={{ display: 'flex' }}>
|
||||||
<Drawer anchor={'right'} open={open} onClose={handleClose}>
|
<span>Peer: {truncStringPortion(props.peerId)}</span>
|
||||||
<div style={{padding:'20px'}}>
|
<ClipboardCopy value={props.peerId} />
|
||||||
<Typography variant="h5" gutterBottom style={{display:'flex'}}>
|
</Typography>
|
||||||
<span>Peer: { truncStringPortion(props.peerId) }</span>
|
<Paper>
|
||||||
<ClipboardCopy value={props.peerId} />
|
{isLoadingPeerCashout || isLoadingPeerCheque ? (
|
||||||
</Typography>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<Paper>
|
<CircularProgress />
|
||||||
{ isLoadingPeerCashout || isLoadingPeerCheque ?
|
</Container>
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
) : (
|
||||||
<CircularProgress />
|
<div style={{ textAlign: 'left', padding: '10px' }}>
|
||||||
</Container>
|
<h3>Last Cheque</h3>
|
||||||
:
|
<Grid container spacing={1}>
|
||||||
<div style={{textAlign:'left', padding:'10px'}}>
|
<Grid key={1} item xs={12} sm={12} xl={6}>
|
||||||
<h3>Last Cheque</h3>
|
<h5>Last Sent</h5>
|
||||||
<Grid container spacing={1}>
|
<p>
|
||||||
<Grid key={1} item xs={12} sm={12} xl={6}>
|
Payout:
|
||||||
<h5>Last Sent</h5>
|
<span style={{ marginBottom: '0px', fontFamily: 'monospace, monospace' }}>
|
||||||
<p>
|
{' '}
|
||||||
Payout:
|
{peerCheque?.lastsent?.payout ? ConvertBalanceToBZZ(peerCheque?.lastsent?.payout) : '-'}
|
||||||
<span style={{marginBottom: '0px', fontFamily: 'monospace, monospace'}}> {
|
</span>
|
||||||
peerCheque.lastsent?.payout ? ConvertBalanceToBZZ(peerCheque.lastsent?.payout) : '-'
|
</p>
|
||||||
}</span>
|
<p>
|
||||||
</p>
|
Beneficiary:
|
||||||
<p>Beneficiary:
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCheque?.lastsent?.beneficiary} />
|
||||||
<EthereumAddress
|
</p>
|
||||||
network={'goerli'}
|
<p>
|
||||||
hideBlockie
|
Chequebook:
|
||||||
address={peerCheque.lastsent?.beneficiary}
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCheque?.lastsent?.chequebook} />
|
||||||
/>
|
</p>
|
||||||
</p>
|
</Grid>
|
||||||
<p>Chequebook:
|
<Grid key={1} item xs={12} sm={12} xl={6}>
|
||||||
<EthereumAddress
|
<h5>Last Received</h5>
|
||||||
network={'goerli'}
|
<p>
|
||||||
hideBlockie
|
Payout:
|
||||||
address={peerCheque.lastsent?.chequebook}
|
<span style={{ marginBottom: '0px', fontFamily: 'monospace, monospace' }}>
|
||||||
/>
|
{' '}
|
||||||
</p>
|
{peerCheque?.lastreceived?.payout ? ConvertBalanceToBZZ(peerCheque?.lastreceived?.payout) : '-'}
|
||||||
</Grid>
|
</span>
|
||||||
<Grid key={1} item xs={12} sm={12} xl={6}>
|
</p>
|
||||||
<h5>Last Received</h5>
|
<p>
|
||||||
<p>
|
Beneficiary:
|
||||||
Payout:
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCheque?.lastreceived?.beneficiary} />
|
||||||
<span style={{marginBottom: '0px', fontFamily: 'monospace, monospace'}}> {
|
</p>
|
||||||
peerCheque.lastreceived?.payout ? ConvertBalanceToBZZ(peerCheque.lastreceived?.payout) : '-'}</span>
|
<p>
|
||||||
</p>
|
Chequebook:
|
||||||
<p>Beneficiary:
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCheque?.lastreceived?.chequebook} />
|
||||||
<EthereumAddress
|
</p>
|
||||||
network={'goerli'}
|
</Grid>
|
||||||
hideBlockie
|
</Grid>
|
||||||
address={peerCheque.lastreceived?.beneficiary}
|
<h3>Last Cashout</h3>
|
||||||
/>
|
{peerCashout && peerCashout?.cumulativePayout > 0 ? (
|
||||||
</p>
|
<div>
|
||||||
<p>Chequebook:
|
<p>
|
||||||
<EthereumAddress
|
Cumulative Payout:
|
||||||
network={'goerli'}
|
<span style={{ marginBottom: '0px', fontFamily: 'monospace, monospace' }}>
|
||||||
hideBlockie
|
{peerCashout?.cumulativePayout ? ConvertBalanceToBZZ(peerCashout?.cumulativePayout) : '-'}
|
||||||
address={peerCheque.lastreceived?.chequebook}
|
</span>
|
||||||
/>
|
</p>
|
||||||
</p>
|
<p>
|
||||||
</Grid>
|
Last Payout:
|
||||||
</Grid>
|
<span style={{ marginBottom: '0px', fontFamily: 'monospace, monospace' }}>
|
||||||
<h3>Last Cashout</h3>
|
{' '}
|
||||||
{peerCashout.cumulativePayout > 0 ?
|
{peerCashout?.result.lastPayout ? ConvertBalanceToBZZ(peerCashout?.result.lastPayout) : '-'}
|
||||||
<div>
|
</span>
|
||||||
<p>
|
<span> {peerCashout?.result.bounced ? 'Bounced' : ''}</span>
|
||||||
Cumulative Payout:
|
</p>
|
||||||
<span style={{marginBottom: '0px', fontFamily: 'monospace, monospace'}}>
|
<p>
|
||||||
{peerCashout.cumulativePayout ? ConvertBalanceToBZZ(peerCashout.cumulativePayout) : '-'}
|
Beneficiary:
|
||||||
</span>
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCashout?.beneficiary} />
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Last Payout:
|
Chequebook:
|
||||||
<span style={{marginBottom: '0px', fontFamily: 'monospace, monospace'}}> {
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCashout?.chequebook} />
|
||||||
peerCashout.result.lastPayout ? ConvertBalanceToBZZ(peerCashout.result.lastPayout) : '-'
|
</p>
|
||||||
}</span>
|
<p>
|
||||||
<span> {peerCashout.result.bounced ? 'Bounced' : ''}</span>
|
Recipient:
|
||||||
</p>
|
<EthereumAddress network={'goerli'} hideBlockie address={peerCashout?.result.recipient} />
|
||||||
<p>Beneficiary:
|
</p>
|
||||||
<EthereumAddress
|
<p>
|
||||||
network={'goerli'}
|
Transaction:
|
||||||
hideBlockie
|
<EthereumAddress
|
||||||
address={peerCashout.beneficiary}
|
transaction
|
||||||
/>
|
network={'goerli'}
|
||||||
</p>
|
hideBlockie
|
||||||
<p>Chequebook:
|
address={peerCashout?.transactionHash}
|
||||||
<EthereumAddress
|
/>
|
||||||
network={'goerli'}
|
</p>
|
||||||
hideBlockie
|
</div>
|
||||||
address={peerCashout.chequebook}
|
) : (
|
||||||
/>
|
'None'
|
||||||
</p>
|
)}
|
||||||
<p>Recipient:
|
</div>
|
||||||
<EthereumAddress
|
)}
|
||||||
network={'goerli'}
|
</Paper>
|
||||||
hideBlockie
|
|
||||||
address={peerCashout.result.recipient}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
<p>Transaction:
|
|
||||||
<EthereumAddress
|
|
||||||
transaction
|
|
||||||
network={'goerli'}
|
|
||||||
hideBlockie
|
|
||||||
address={peerCashout.transactionHash}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
: 'None'}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
</Drawer>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,69 @@
|
|||||||
import React from 'react';
|
import { ReactElement } from 'react'
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableRow, TableHead, Paper, Container, CircularProgress } from '@material-ui/core';
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
Paper,
|
||||||
|
Container,
|
||||||
|
CircularProgress,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
import { ConvertBalanceToBZZ } from '../../utils/common';
|
import { ConvertBalanceToBZZ } from '../../utils/common'
|
||||||
|
|
||||||
import type { AllSettlements, Settlements } from '@ethersphere/bee-js'
|
import type { AllSettlements, Settlements } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
table: {
|
table: {
|
||||||
minWidth: 650,
|
minWidth: 650,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
|
interface Props {
|
||||||
interface IProps {
|
nodeSettlements: AllSettlements | null
|
||||||
nodeSettlements: AllSettlements | null,
|
loading?: boolean
|
||||||
loading?: boolean,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function SettlementsTable(props: IProps) {
|
function SettlementsTable(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{props.loading ?
|
{props.loading ? (
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
:
|
) : (
|
||||||
<TableContainer component={Paper}>
|
<TableContainer component={Paper}>
|
||||||
<Table className={classes.table} size="small" aria-label="simple table">
|
<Table className={classes.table} size="small" aria-label="simple table">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Peer</TableCell>
|
<TableCell>Peer</TableCell>
|
||||||
<TableCell>Received (BZZ)</TableCell>
|
<TableCell>Received (BZZ)</TableCell>
|
||||||
<TableCell>Sent (BZZ)</TableCell>
|
<TableCell>Sent (BZZ)</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{props.nodeSettlements?.settlements.map((item: Settlements, idx: number) => (
|
{props.nodeSettlements?.settlements.map((item: Settlements) => (
|
||||||
<TableRow key={item.peer}>
|
<TableRow key={item.peer}>
|
||||||
<TableCell>{item.peer}</TableCell>
|
<TableCell>{item.peer}</TableCell>
|
||||||
<TableCell style={{ fontFamily: 'monospace, monospace'}}>
|
<TableCell style={{ fontFamily: 'monospace, monospace' }}>
|
||||||
{item.received > 0 ? ConvertBalanceToBZZ(item.received).toFixed(7).toLocaleString() : item.received}
|
{item.received > 0 ? ConvertBalanceToBZZ(item.received).toFixed(7).toLocaleString() : item.received}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell style={{ fontFamily: 'monospace, monospace'}}>
|
<TableCell style={{ fontFamily: 'monospace, monospace' }}>
|
||||||
{item.sent > 0 ? ConvertBalanceToBZZ(item.sent).toFixed(7).toLocaleString() : item.sent}
|
{item.sent > 0 ? ConvertBalanceToBZZ(item.sent).toFixed(7).toLocaleString() : item.sent}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>}
|
</TableContainer>
|
||||||
</div>
|
)}
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SettlementsTable;
|
export default SettlementsTable
|
||||||
|
|||||||
+140
-138
@@ -1,166 +1,168 @@
|
|||||||
import React from 'react';
|
import { ReactElement, useState, ChangeEvent, ReactChild } from 'react'
|
||||||
import { withStyles, Theme, createStyles } from '@material-ui/core/styles';
|
import { withStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
import { Tabs, Tab, Box, Typography, Container, CircularProgress } from '@material-ui/core';
|
import { Tabs, Tab, Box, Typography, Container, CircularProgress } from '@material-ui/core'
|
||||||
|
|
||||||
import AccountCard from '../accounting/AccountCard';
|
import AccountCard from '../accounting/AccountCard'
|
||||||
import BalancesTable from './BalancesTable';
|
import BalancesTable from './BalancesTable'
|
||||||
import ChequebookTable from './ChequebookTable';
|
import ChequebookTable from './ChequebookTable'
|
||||||
import SettlementsTable from './SettlementsTable';
|
import SettlementsTable from './SettlementsTable'
|
||||||
import EthereumAddressCard from '../../components/EthereumAddressCard';
|
import EthereumAddressCard from '../../components/EthereumAddressCard'
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard';
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
|
|
||||||
import { useApiNodeAddresses, useApiChequebookAddress, useApiChequebookBalance, useApiPeerBalances, useApiPeerCheques, useApiSettlements } from '../../hooks/apiHooks';
|
import {
|
||||||
|
useApiNodeAddresses,
|
||||||
|
useApiChequebookAddress,
|
||||||
|
useApiChequebookBalance,
|
||||||
|
useApiPeerBalances,
|
||||||
|
useApiPeerCheques,
|
||||||
|
useApiSettlements,
|
||||||
|
useApiHealth,
|
||||||
|
useDebugApiHealth,
|
||||||
|
} from '../../hooks/apiHooks'
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: ReactChild
|
||||||
index: any;
|
index: number
|
||||||
value: any;
|
value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function a11yProps(index: number) {
|
||||||
function a11yProps(index: any) {
|
return {
|
||||||
return {
|
id: `simple-tab-${index}`,
|
||||||
id: `simple-tab-${index}`,
|
'aria-controls': `simple-tabpanel-${index}`,
|
||||||
'aria-controls': `simple-tabpanel-${index}`,
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Accounting(props: any) {
|
export default function Accounting(): ReactElement {
|
||||||
const [value, setValue] = React.useState(0);
|
const [value, setValue] = useState(0)
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
const handleChange = (event: ChangeEvent<unknown>, newValue: number) => {
|
||||||
setValue(newValue);
|
setValue(newValue)
|
||||||
};
|
}
|
||||||
|
|
||||||
const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress()
|
const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress()
|
||||||
const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance()
|
const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance()
|
||||||
const { peerBalances, isLoadingPeerBalances } = useApiPeerBalances()
|
const { peerBalances, isLoadingPeerBalances } = useApiPeerBalances()
|
||||||
const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses()
|
const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses()
|
||||||
|
const { health, isLoadingHealth } = useApiHealth()
|
||||||
|
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
|
||||||
|
|
||||||
const { peerCheques, isLoadingPeerCheques } = useApiPeerCheques()
|
const { peerCheques, isLoadingPeerCheques } = useApiPeerCheques()
|
||||||
const { settlements, isLoadingSettlements } = useApiSettlements()
|
const { settlements, isLoadingSettlements } = useApiSettlements()
|
||||||
|
|
||||||
|
|
||||||
function TabPanel(props: TabPanelProps) {
|
|
||||||
const { children, value, index, ...other } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="tabpanel"
|
|
||||||
hidden={value !== index}
|
|
||||||
id={`simple-tabpanel-${index}`}
|
|
||||||
aria-labelledby={`simple-tab-${index}`}
|
|
||||||
{...other}
|
|
||||||
>
|
|
||||||
{value === index && (
|
|
||||||
<Box style={{ marginTop: '20px' }}>
|
|
||||||
<Typography component="div">{children}</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AntTabs = withStyles({
|
|
||||||
root: {
|
|
||||||
borderBottom: '1px solid #e8e8e8',
|
|
||||||
},
|
|
||||||
indicator: {
|
|
||||||
backgroundColor: '#3f51b5',
|
|
||||||
},
|
|
||||||
})(Tabs);
|
|
||||||
|
|
||||||
interface StyledTabProps {
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AntTab = withStyles((theme: Theme) =>
|
|
||||||
createStyles({
|
|
||||||
root: {
|
|
||||||
textTransform: 'none',
|
|
||||||
minWidth: 72,
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
fontWeight: theme.typography.fontWeightRegular,
|
|
||||||
marginRight: theme.spacing(4),
|
|
||||||
fontFamily: [
|
|
||||||
'-apple-system',
|
|
||||||
'BlinkMacSystemFont',
|
|
||||||
'"Segoe UI"',
|
|
||||||
'Roboto',
|
|
||||||
'"Helvetica Neue"',
|
|
||||||
'Arial',
|
|
||||||
'sans-serif',
|
|
||||||
'"Apple Color Emoji"',
|
|
||||||
'"Segoe UI Emoji"',
|
|
||||||
'"Segoe UI Symbol"',
|
|
||||||
].join(','),
|
|
||||||
'&:hover': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
'&$selected': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
fontWeight: theme.typography.fontWeightMedium,
|
|
||||||
},
|
|
||||||
'&:focus': {
|
|
||||||
color: '#3f51b5',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
selected: {},
|
|
||||||
}),
|
|
||||||
)((props: StyledTabProps) => <Tab disableRipple {...props} />);
|
|
||||||
|
|
||||||
|
function TabPanel(props: TabPanelProps) {
|
||||||
|
const { children, value, index, ...other } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
{props.nodeHealth?.status === 'ok' && props.health ?
|
role="tabpanel"
|
||||||
|
hidden={value !== index}
|
||||||
|
id={`simple-tabpanel-${index}`}
|
||||||
|
aria-labelledby={`simple-tab-${index}`}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{value === index && (
|
||||||
|
<Box style={{ marginTop: '20px' }}>
|
||||||
|
<Typography component="div">{children}</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const AntTabs = withStyles({
|
||||||
|
root: {
|
||||||
|
borderBottom: '1px solid #e8e8e8',
|
||||||
|
},
|
||||||
|
indicator: {
|
||||||
|
backgroundColor: '#3f51b5',
|
||||||
|
},
|
||||||
|
})(Tabs)
|
||||||
|
|
||||||
|
interface StyledTabProps {
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AntTab = withStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
textTransform: 'none',
|
||||||
|
minWidth: 72,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
fontWeight: theme.typography.fontWeightRegular,
|
||||||
|
marginRight: theme.spacing(4),
|
||||||
|
fontFamily: [
|
||||||
|
'-apple-system',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'"Segoe UI"',
|
||||||
|
'Roboto',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
'Arial',
|
||||||
|
'sans-serif',
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"',
|
||||||
|
].join(','),
|
||||||
|
'&:hover': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
'&$selected': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
|
},
|
||||||
|
'&:focus': {
|
||||||
|
color: '#3f51b5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selected: {},
|
||||||
|
}),
|
||||||
|
)((props: StyledTabProps) => <Tab disableRipple {...props} />)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
// FIXME: this should be broken up
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
|
nodeHealth?.status === 'ok' && health ? (
|
||||||
<div>
|
<div>
|
||||||
<AccountCard
|
<AccountCard
|
||||||
chequebookAddress={chequebookAddress}
|
chequebookAddress={chequebookAddress}
|
||||||
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||||
chequebookBalance={chequebookBalance}
|
chequebookBalance={chequebookBalance}
|
||||||
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
||||||
settlements={settlements}
|
settlements={settlements}
|
||||||
isLoadingSettlements={isLoadingSettlements}
|
isLoadingSettlements={isLoadingSettlements}
|
||||||
/>
|
/>
|
||||||
<EthereumAddressCard
|
<EthereumAddressCard
|
||||||
nodeAddresses={nodeAddresses}
|
nodeAddresses={nodeAddresses}
|
||||||
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
||||||
chequebookAddress={chequebookAddress}
|
chequebookAddress={chequebookAddress}
|
||||||
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||||
/>
|
/>
|
||||||
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
|
<AntTabs style={{ marginTop: '12px' }} value={value} onChange={handleChange} aria-label="ant example">
|
||||||
<AntTab label="Balances" {...a11yProps(0)} />
|
<AntTab label="Balances" {...a11yProps(0)} />
|
||||||
<AntTab label="Chequebook" {...a11yProps(1)} />
|
<AntTab label="Chequebook" {...a11yProps(1)} />
|
||||||
<AntTab label="Settlements" {...a11yProps(2)} />
|
<AntTab label="Settlements" {...a11yProps(2)} />
|
||||||
</AntTabs>
|
</AntTabs>
|
||||||
<TabPanel value={value} index={0}>
|
<TabPanel value={value} index={0}>
|
||||||
<BalancesTable
|
<BalancesTable peerBalances={peerBalances} loading={isLoadingPeerBalances} />
|
||||||
peerBalances={peerBalances}
|
|
||||||
loading={isLoadingPeerBalances}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={value} index={1}>
|
<TabPanel value={value} index={1}>
|
||||||
<ChequebookTable
|
<ChequebookTable peerCheques={peerCheques} loading={isLoadingPeerCheques} />
|
||||||
peerCheques={peerCheques}
|
|
||||||
loading={isLoadingPeerCheques}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={value} index={2}>
|
<TabPanel value={value} index={2}>
|
||||||
<SettlementsTable
|
<SettlementsTable nodeSettlements={settlements} loading={isLoadingSettlements} />
|
||||||
nodeSettlements={settlements}
|
|
||||||
loading={isLoadingSettlements}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</div>
|
</div>
|
||||||
:
|
) : isLoadingHealth || isLoadingNodeHealth ? (
|
||||||
props.isLoadingHealth || props.isLoadingNodeHealth ?
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<CircularProgress />
|
||||||
<CircularProgress />
|
</Container>
|
||||||
</Container>
|
) : (
|
||||||
:
|
<TroubleshootConnectionCard />
|
||||||
<TroubleshootConnectionCard />
|
) /* eslint-enable no-nested-ternary */
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+126
-115
@@ -1,14 +1,15 @@
|
|||||||
import React, { useState } from 'react';
|
import { ReactElement, useState } from 'react'
|
||||||
import { beeApi } from '../../services/bee';
|
import { beeApi } from '../../services/bee'
|
||||||
|
|
||||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
|
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
import { Paper, InputBase, IconButton, Button, Container, CircularProgress } from '@material-ui/core';
|
import { Paper, InputBase, IconButton, Button, Container, CircularProgress } from '@material-ui/core'
|
||||||
import { Search } from '@material-ui/icons';
|
import { Search } from '@material-ui/icons'
|
||||||
import {DropzoneArea} from 'material-ui-dropzone'
|
import { DropzoneArea } from 'material-ui-dropzone'
|
||||||
import ClipboardCopy from '../../components/ClipboardCopy';
|
import ClipboardCopy from '../../components/ClipboardCopy'
|
||||||
|
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard';
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
import { Data, FileData } from '@ethersphere/bee-js';
|
import { Data, FileData } from '@ethersphere/bee-js'
|
||||||
|
import { useApiHealth, useDebugApiHealth } from '../../hooks/apiHooks'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -30,128 +31,138 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
margin: 4,
|
margin: 4,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
export default function Files(props: any) {
|
export default function Files(): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
const [inputMode, setInputMode] = useState<'browse' | 'upload'>('browse');
|
const [inputMode, setInputMode] = useState<'browse' | 'upload'>('browse')
|
||||||
const [searchInput, setSearchInput] = useState('');
|
const [searchInput, setSearchInput] = useState('')
|
||||||
const [searchResult, setSearchResult] = useState<FileData<Data> | null>(null);
|
const [searchResult, setSearchResult] = useState<FileData<Data> | null>(null)
|
||||||
const [loadingSearch, setLoadingSearch] = useState(false);
|
const [loadingSearch, setLoadingSearch] = useState(false)
|
||||||
|
const { health, isLoadingHealth } = useApiHealth()
|
||||||
|
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
|
||||||
|
|
||||||
const [files, setFiles] = useState<File[]>([]);
|
const [files, setFiles] = useState<File[]>([])
|
||||||
const [uploadReference, setUploadReference] = useState('');
|
const [uploadReference, setUploadReference] = useState('')
|
||||||
const [uploadingFile, setUploadingFile] = useState(false);
|
const [uploadingFile, setUploadingFile] = useState(false)
|
||||||
|
|
||||||
const getFile = () => {
|
const getFile = () => {
|
||||||
setLoadingSearch(true)
|
setLoadingSearch(true)
|
||||||
beeApi.files.downloadFile(searchInput)
|
beeApi.files
|
||||||
.then(res => {
|
.downloadFile(searchInput)
|
||||||
setSearchResult(res)
|
.then(res => {
|
||||||
|
setSearchResult(res)
|
||||||
|
|
||||||
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
|
const downloadUrl = window.URL.createObjectURL(new Blob([res.data]))
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a')
|
||||||
link.href = downloadUrl;
|
link.href = downloadUrl
|
||||||
link.setAttribute('download', 'file.zip'); //any other extension
|
link.setAttribute('download', 'file.zip') //any other extension
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link)
|
||||||
link.click();
|
link.click()
|
||||||
link.remove();
|
link.remove()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
})
|
// FIXME: handle the error
|
||||||
.finally(() => {
|
})
|
||||||
setLoadingSearch(false)
|
.finally(() => {
|
||||||
})
|
setLoadingSearch(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadFile = () => {
|
||||||
|
setUploadingFile(true)
|
||||||
|
beeApi.files
|
||||||
|
.uploadFile(files[0])
|
||||||
|
.then(hash => {
|
||||||
|
setUploadReference(hash)
|
||||||
|
setFiles([])
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// FIXME: handle the error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setUploadingFile(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (files?: File[]) => {
|
||||||
|
if (files) {
|
||||||
|
setFiles(files)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const uploadFile = () => {
|
return (
|
||||||
setUploadingFile(true)
|
<div>
|
||||||
beeApi.files.uploadFile(files[0])
|
{
|
||||||
.then(hash => {
|
// FIXME: this should be broken up
|
||||||
setUploadReference(hash)
|
/* eslint-disable no-nested-ternary */
|
||||||
setFiles([])
|
nodeHealth?.status === 'ok' && health ? (
|
||||||
})
|
<Container maxWidth="sm">
|
||||||
.catch(error => {
|
<div style={{ marginBottom: '7px' }}>
|
||||||
})
|
<Button color="primary" style={{ marginRight: '7px' }} onClick={() => setInputMode('browse')}>
|
||||||
.finally(() => {
|
Browse
|
||||||
setUploadingFile(false)
|
</Button>
|
||||||
})
|
<Button color="primary" onClick={() => setInputMode('upload')}>
|
||||||
}
|
Upload
|
||||||
|
</Button>
|
||||||
const handleChange = (files: any) => {
|
</div>
|
||||||
if (files) {
|
{inputMode === 'browse' ? (
|
||||||
setFiles(files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{props.nodeHealth?.status === 'ok' && props.health ?
|
|
||||||
<Container maxWidth="sm">
|
|
||||||
<div style={{marginBottom: '7px'}}>
|
|
||||||
<Button color="primary" style={{marginRight: '7px'}} onClick={() => setInputMode('browse')}>Browse</Button>
|
|
||||||
<Button color="primary" onClick={() => setInputMode('upload')}>Upload</Button>
|
|
||||||
</div>
|
|
||||||
{inputMode === 'browse' ?
|
|
||||||
<Paper component="form" className={classes.root}>
|
<Paper component="form" className={classes.root}>
|
||||||
<InputBase
|
<InputBase
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
placeholder="Enter hash e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
|
placeholder="Enter hash e.g. 0773a91efd6547c754fc1d95fb1c62c7d1b47f959c2caa685dfec8736da95c1c"
|
||||||
inputProps={{ 'aria-label': 'search swarm nodes' }}
|
inputProps={{ 'aria-label': 'search swarm nodes' }}
|
||||||
onChange={(e) => setSearchInput(e.target.value)}
|
onChange={e => setSearchInput(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<IconButton onClick={() => getFile()} className={classes.iconButton} aria-label="search">
|
<IconButton onClick={() => getFile()} className={classes.iconButton} aria-label="search">
|
||||||
<Search />
|
<Search />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Paper>
|
</Paper>
|
||||||
:
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{uploadingFile ?
|
{uploadingFile ? (
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
:
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{uploadReference ?
|
{uploadReference ? (
|
||||||
<Paper component="form" className={classes.root} style={{marginBottom:'15px', display: 'flex'}}>
|
<Paper
|
||||||
<span>{uploadReference}</span>
|
component="form"
|
||||||
<ClipboardCopy
|
className={classes.root}
|
||||||
value={uploadReference}
|
style={{ marginBottom: '15px', display: 'flex' }}
|
||||||
/>
|
>
|
||||||
</Paper>
|
<span>{uploadReference}</span>
|
||||||
:
|
<ClipboardCopy value={uploadReference} />
|
||||||
null
|
</Paper>
|
||||||
}
|
) : null}
|
||||||
<DropzoneArea
|
<DropzoneArea onChange={handleChange} />
|
||||||
onChange={handleChange}
|
<div style={{ marginTop: '15px' }}>
|
||||||
/>
|
<Button onClick={() => uploadFile()} className={classes.iconButton}>
|
||||||
<div style={{marginTop:'15px'}}>
|
|
||||||
<Button onClick={() => uploadFile()} className={classes.iconButton}>
|
|
||||||
Upload
|
Upload
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
{loadingSearch ?
|
{loadingSearch ? (
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<CircularProgress />
|
|
||||||
</Container>
|
|
||||||
:
|
|
||||||
<div style={{padding:'20px'}} >
|
|
||||||
{searchResult}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</Container>
|
|
||||||
:
|
|
||||||
props.isLoadingHealth || props.isLoadingNodeHealth ?
|
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
:
|
) : (
|
||||||
<TroubleshootConnectionCard
|
<div style={{ padding: '20px' }}>{searchResult}</div>
|
||||||
/>}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
)
|
) : isLoadingHealth || isLoadingNodeHealth ? (
|
||||||
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Container>
|
||||||
|
) : (
|
||||||
|
<TroubleshootConnectionCard />
|
||||||
|
) /* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,113 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableRow, TableHead, Button, Paper, Tooltip, Container, CircularProgress } from '@material-ui/core';
|
import {
|
||||||
import { Autorenew } from '@material-ui/icons';
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
Button,
|
||||||
|
Paper,
|
||||||
|
Tooltip,
|
||||||
|
Container,
|
||||||
|
CircularProgress,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
import { Autorenew } from '@material-ui/icons'
|
||||||
|
|
||||||
import { beeDebugApi } from '../../services/bee';
|
import { beeDebugApi } from '../../services/bee'
|
||||||
import type { Peer } from '@ethersphere/bee-js';
|
import type { Peer } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
table: {
|
table: {
|
||||||
minWidth: 650,
|
minWidth: 650,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
peers: Peer[] | null
|
peers: Peer[] | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
error: Error | null
|
error: Error | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PeerTable(props: Props): ReactElement {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
function PeerTable(props: Props) {
|
const [peerLatency, setPeerLatency] = useState([{ peerId: '', rtt: '', loading: false }])
|
||||||
const classes = useStyles();
|
|
||||||
|
|
||||||
const [peerLatency, setPeerLatency] = useState([{ peerId: '', rtt: '', loading: false }]);
|
const PingPeer = (peerId: string) => {
|
||||||
|
setPeerLatency([...peerLatency, { peerId: peerId, rtt: '', loading: true }])
|
||||||
const PingPeer = async (peerId: string) => {
|
beeDebugApi.connectivity
|
||||||
|
.ping(peerId)
|
||||||
setPeerLatency([...peerLatency, { peerId: peerId, rtt: '', loading: true }])
|
.then(res => {
|
||||||
beeDebugApi.connectivity.ping(peerId)
|
setPeerLatency([...peerLatency, { peerId: peerId, rtt: res.rtt, loading: false }])
|
||||||
.then(res => {
|
})
|
||||||
setPeerLatency([...peerLatency, { peerId: peerId, rtt: res.rtt, loading: false }])
|
.catch(() => {
|
||||||
})
|
setPeerLatency([...peerLatency, { peerId: peerId, rtt: 'error', loading: false }])
|
||||||
.catch(error => {
|
})
|
||||||
setPeerLatency([...peerLatency, { peerId: peerId, rtt: 'error', loading: false }])
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.isLoading) {
|
|
||||||
return (
|
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
|
||||||
<CircularProgress />
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.error || props.peers === null) {
|
|
||||||
return (
|
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
|
||||||
<p>Failed to load peers</p>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (props.isLoading) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<TableContainer component={Paper}>
|
<CircularProgress />
|
||||||
<Table className={classes.table} aria-label="simple table">
|
</Container>
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>Index</TableCell>
|
|
||||||
<TableCell>Peer Id</TableCell>
|
|
||||||
<TableCell align="right">Actions</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{props.peers.map((peer: any, idx: number) => (
|
|
||||||
<TableRow key={peer.address}>
|
|
||||||
<TableCell component="th" scope="row">
|
|
||||||
{idx + 1}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{peer.address}</TableCell>
|
|
||||||
<TableCell align="right">
|
|
||||||
<Tooltip title="Ping node">
|
|
||||||
<Button color="primary" onClick={() => PingPeer(peer.address)} >
|
|
||||||
{peerLatency.find(item => item.peerId === peer.address) ?
|
|
||||||
peerLatency.filter(item => item.peerId === peer.address)[0].loading ? <CircularProgress size={20} /> :
|
|
||||||
peerLatency.filter(item => item.peerId === peer.address)[0].rtt :
|
|
||||||
<Autorenew />}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.error || props.peers === null) {
|
||||||
|
return (
|
||||||
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
|
<p>Failed to load peers</p>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table className={classes.table} aria-label="simple table">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Index</TableCell>
|
||||||
|
<TableCell>Peer Id</TableCell>
|
||||||
|
<TableCell align="right">Actions</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{props.peers.map((peer: Peer, idx: number) => (
|
||||||
|
<TableRow key={peer.address}>
|
||||||
|
<TableCell component="th" scope="row">
|
||||||
|
{idx + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>{peer.address}</TableCell>
|
||||||
|
<TableCell align="right">
|
||||||
|
<Tooltip title="Ping node">
|
||||||
|
<Button color="primary" onClick={() => PingPeer(peer.address)}>
|
||||||
|
{
|
||||||
|
// FIXME: this should be broken up
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
|
peerLatency.find(item => item.peerId === peer.address) ? (
|
||||||
|
peerLatency.filter(item => item.peerId === peer.address)[0].loading ? (
|
||||||
|
<CircularProgress size={20} />
|
||||||
|
) : (
|
||||||
|
peerLatency.filter(item => item.peerId === peer.address)[0].rtt
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Autorenew />
|
||||||
|
)
|
||||||
|
/* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PeerTable
|
export default PeerTable
|
||||||
|
|||||||
+26
-25
@@ -1,31 +1,32 @@
|
|||||||
import { Container, CircularProgress } from '@material-ui/core/';
|
import { Container, CircularProgress } from '@material-ui/core/'
|
||||||
import PeerTable from './PeerTable';
|
import PeerTable from './PeerTable'
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard';
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
|
|
||||||
import { useApiNodeTopology, useApiNodePeers, useDebugApiHealth } from '../../hooks/apiHooks';
|
import { useApiNodeTopology, useApiNodePeers, useDebugApiHealth } from '../../hooks/apiHooks'
|
||||||
import TopologyStats from '../../components/TopologyStats';
|
import TopologyStats from '../../components/TopologyStats'
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
|
||||||
export default function Peers() {
|
export default function Peers(): ReactElement {
|
||||||
const topology = useApiNodeTopology()
|
const topology = useApiNodeTopology()
|
||||||
const debugHealth = useDebugApiHealth()
|
const debugHealth = useDebugApiHealth()
|
||||||
const peers = useApiNodePeers()
|
const peers = useApiNodePeers()
|
||||||
|
|
||||||
if (debugHealth.isLoadingNodeHealth) {
|
|
||||||
return (
|
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
|
||||||
<CircularProgress />
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debugHealth.error) {
|
|
||||||
return <TroubleshootConnectionCard />
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (debugHealth.isLoadingNodeHealth) {
|
||||||
return (
|
return (
|
||||||
<>
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
<TopologyStats {...topology} />
|
<CircularProgress />
|
||||||
<PeerTable {...peers} />
|
</Container>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debugHealth.error) {
|
||||||
|
return <TroubleshootConnectionCard />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TopologyStats {...topology} />
|
||||||
|
<PeerTable {...peers} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,86 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { ReactElement, useState } from 'react'
|
||||||
import { Paper, Container, TextField, Typography, Button } from '@material-ui/core';
|
import { Paper, Container, TextField, Typography, Button } from '@material-ui/core'
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings(): ReactElement {
|
||||||
|
const [refreshVisibility, toggleRefreshVisibility] = useState(false)
|
||||||
|
const [host, setHost] = useState('')
|
||||||
|
const [debugHost, setDebugHost] = useState('')
|
||||||
|
|
||||||
const [refreshVisibility, toggleRefreshVisibility] = useState(false)
|
const handleNewHostConnection = () => {
|
||||||
const [host, setHost] = useState('')
|
if (host) {
|
||||||
const [debugHost, setDebugHost] = useState('')
|
sessionStorage.setItem('api_host', host)
|
||||||
|
|
||||||
const handleNewHostConnection = () => {
|
|
||||||
if (host) {
|
|
||||||
sessionStorage.setItem('api_host', host)
|
|
||||||
}
|
|
||||||
if (debugHost) {
|
|
||||||
sessionStorage.setItem('debug_api_host', debugHost)
|
|
||||||
}
|
|
||||||
if (host || debugHost) {
|
|
||||||
toggleRefreshVisibility(!refreshVisibility)
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (debugHost) {
|
||||||
<div>
|
sessionStorage.setItem('debug_api_host', debugHost)
|
||||||
<Container>
|
}
|
||||||
<Typography variant="h4" gutterBottom>
|
|
||||||
Settings
|
if (host || debugHost) {
|
||||||
</Typography>
|
toggleRefreshVisibility(!refreshVisibility)
|
||||||
<Paper>
|
window.location.reload()
|
||||||
<TextField
|
}
|
||||||
id="filled-full-width"
|
}
|
||||||
label="API Endpoint"
|
|
||||||
style={{ margin: 0 }}
|
return (
|
||||||
placeholder="ex: 127.0.0.0.1:1633"
|
<div>
|
||||||
helperText="Enter node host override / port"
|
<Container>
|
||||||
fullWidth
|
<Typography variant="h4" gutterBottom>
|
||||||
defaultValue={sessionStorage.getItem('api_host') ? sessionStorage.getItem('api_host') : process.env.REACT_APP_BEE_HOST}
|
Settings
|
||||||
margin="normal"
|
</Typography>
|
||||||
InputLabelProps={{
|
<Paper>
|
||||||
shrink: true,
|
<TextField
|
||||||
}}
|
id="filled-full-width"
|
||||||
onChange={(e) => {
|
label="API Endpoint"
|
||||||
setHost(e.target.value)
|
style={{ margin: 0 }}
|
||||||
toggleRefreshVisibility(true)
|
placeholder="ex: 127.0.0.0.1:1633"
|
||||||
}}
|
helperText="Enter node host override / port"
|
||||||
variant="filled"
|
fullWidth
|
||||||
/>
|
defaultValue={
|
||||||
</Paper>
|
sessionStorage.getItem('api_host') ? sessionStorage.getItem('api_host') : process.env.REACT_APP_BEE_HOST
|
||||||
<Paper style={{marginTop:'20px'}}>
|
}
|
||||||
<TextField
|
margin="normal"
|
||||||
id="filled-full-width"
|
InputLabelProps={{
|
||||||
label="Debug API Endpoint"
|
shrink: true,
|
||||||
style={{ margin: 0 }}
|
}}
|
||||||
placeholder="ex: 127.0.0.0.1:1635"
|
onChange={e => {
|
||||||
helperText="Enter node debug host override / port"
|
setHost(e.target.value)
|
||||||
fullWidth
|
toggleRefreshVisibility(true)
|
||||||
defaultValue={sessionStorage.getItem('debug_api_host') ? sessionStorage.getItem('debug_api_host') : process.env.REACT_APP_BEE_DEBUG_HOST}
|
}}
|
||||||
onChange={(e) => {
|
variant="filled"
|
||||||
setDebugHost(e.target.value)
|
/>
|
||||||
toggleRefreshVisibility(true)
|
</Paper>
|
||||||
}}
|
<Paper style={{ marginTop: '20px' }}>
|
||||||
margin="normal"
|
<TextField
|
||||||
InputLabelProps={{
|
id="filled-full-width"
|
||||||
shrink: true,
|
label="Debug API Endpoint"
|
||||||
}}
|
style={{ margin: 0 }}
|
||||||
variant="filled"
|
placeholder="ex: 127.0.0.0.1:1635"
|
||||||
/>
|
helperText="Enter node debug host override / port"
|
||||||
</Paper>
|
fullWidth
|
||||||
{refreshVisibility ?
|
defaultValue={
|
||||||
<div style={{marginTop:'20px'}}>
|
sessionStorage.getItem('debug_api_host')
|
||||||
<Button variant='outlined' color='primary' onClick={() => handleNewHostConnection()}>Save</Button>
|
? sessionStorage.getItem('debug_api_host')
|
||||||
</div>
|
: process.env.REACT_APP_BEE_DEBUG_HOST
|
||||||
: null}
|
}
|
||||||
</Container>
|
onChange={e => {
|
||||||
</div>
|
setDebugHost(e.target.value)
|
||||||
)
|
toggleRefreshVisibility(true)
|
||||||
|
}}
|
||||||
|
margin="normal"
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true,
|
||||||
|
}}
|
||||||
|
variant="filled"
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
{refreshVisibility ? (
|
||||||
|
<div style={{ marginTop: '20px' }}>
|
||||||
|
<Button variant="outlined" color="primary" onClick={() => handleNewHostConnection()}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import { ReactElement, useEffect, useState } from 'react'
|
||||||
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
|
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
|
||||||
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/';
|
import { Typography, Paper, Button, Step, StepLabel, StepContent, Stepper, StepButton } from '@material-ui/core/'
|
||||||
import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp } from '@material-ui/icons/';
|
import { CheckCircle, Error, Sync, ExpandLessSharp, ExpandMoreSharp } from '@material-ui/icons/'
|
||||||
|
|
||||||
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck';
|
|
||||||
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck';
|
|
||||||
import VersionCheck from './SetupSteps/VersionCheck';
|
|
||||||
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck';
|
|
||||||
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund';
|
|
||||||
import PeerConnection from './SetupSteps/PeerConnection';
|
|
||||||
|
|
||||||
|
import DebugConnectionCheck from './SetupSteps/DebugConnectionCheck'
|
||||||
|
import NodeConnectionCheck from './SetupSteps/NodeConnectionCheck'
|
||||||
|
import VersionCheck from './SetupSteps/VersionCheck'
|
||||||
|
import EthereumConnectionCheck from './SetupSteps/EthereumConnectionCheck'
|
||||||
|
import ChequebookDeployFund from './SetupSteps/ChequebookDeployFund'
|
||||||
|
import PeerConnection from './SetupSteps/PeerConnection'
|
||||||
|
import type {
|
||||||
|
ChequebookAddressResponse,
|
||||||
|
ChequebookBalanceResponse,
|
||||||
|
Health,
|
||||||
|
NodeAddresses,
|
||||||
|
Topology,
|
||||||
|
} from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -27,7 +33,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
padding: theme.spacing(5),
|
padding: theme.spacing(5),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
function getSteps() {
|
function getSteps() {
|
||||||
return [
|
return [
|
||||||
@@ -37,40 +43,90 @@ function getSteps() {
|
|||||||
'Deploy and Fund Chequebook',
|
'Deploy and Fund Chequebook',
|
||||||
'Node Connection Check',
|
'Node Connection Check',
|
||||||
'Connect to Peers',
|
'Connect to Peers',
|
||||||
];
|
]
|
||||||
|
}
|
||||||
|
interface Props {
|
||||||
|
nodeHealth: Health | null
|
||||||
|
nodeApiHealth: boolean
|
||||||
|
nodeAddresses: NodeAddresses | null
|
||||||
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
|
chequebookBalance: ChequebookBalanceResponse | null
|
||||||
|
beeRelease: LatestBeeRelease | null
|
||||||
|
nodeTopology: Topology | null
|
||||||
|
isLoadingBeeRelease: boolean
|
||||||
|
isLoadingNodeHealth: boolean
|
||||||
|
isLoadingNodeAddresses: boolean
|
||||||
|
isLoadingNodeTopology: boolean
|
||||||
|
isLoadingHealth: boolean
|
||||||
|
isLoadingChequebookAddress: boolean
|
||||||
|
isLoadingChequebookBalance: boolean
|
||||||
|
setStatusChecksVisible: (value: boolean) => void
|
||||||
|
apiHost: string
|
||||||
|
debugApiHost: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStepContent(step: number, props: any) {
|
function getStepContent(step: number, props: Props) {
|
||||||
|
const {
|
||||||
|
nodeHealth,
|
||||||
|
debugApiHost,
|
||||||
|
beeRelease,
|
||||||
|
isLoadingBeeRelease,
|
||||||
|
nodeAddresses,
|
||||||
|
isLoadingNodeAddresses,
|
||||||
|
isLoadingChequebookBalance,
|
||||||
|
chequebookAddress,
|
||||||
|
chequebookBalance,
|
||||||
|
isLoadingChequebookAddress,
|
||||||
|
nodeApiHealth,
|
||||||
|
apiHost,
|
||||||
|
isLoadingNodeTopology,
|
||||||
|
nodeTopology,
|
||||||
|
} = props
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 0:
|
case 0:
|
||||||
return <DebugConnectionCheck {...props} />;
|
return <DebugConnectionCheck nodeHealth={nodeHealth} debugApiHost={debugApiHost} />
|
||||||
case 1:
|
case 1:
|
||||||
return <VersionCheck {...props} />;
|
return <VersionCheck nodeHealth={nodeHealth} beeRelease={beeRelease} isLoadingBeeRelease={isLoadingBeeRelease} />
|
||||||
case 2:
|
case 2:
|
||||||
return <EthereumConnectionCheck {...props} />;
|
return <EthereumConnectionCheck nodeAddresses={nodeAddresses} isLoadingNodeAddresses={isLoadingNodeAddresses} />
|
||||||
case 3:
|
case 3:
|
||||||
return <ChequebookDeployFund {...props} />;
|
return (
|
||||||
|
<ChequebookDeployFund
|
||||||
|
chequebookAddress={chequebookAddress}
|
||||||
|
chequebookBalance={chequebookBalance}
|
||||||
|
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||||
|
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
||||||
|
/>
|
||||||
|
)
|
||||||
case 4:
|
case 4:
|
||||||
return <NodeConnectionCheck {...props} />;
|
return <NodeConnectionCheck nodeApiHealth={nodeApiHealth} apiHost={apiHost} />
|
||||||
default:
|
default:
|
||||||
return <PeerConnection {...props} />;
|
return <PeerConnection nodeTopology={nodeTopology} isLoadingNodeTopology={isLoadingNodeTopology} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NodeSetupWorkflow(props: any) {
|
export default function NodeSetupWorkflow(props: Props): ReactElement {
|
||||||
const {nodeHealth, nodeApiHealth, nodeAddresses, chequebookAddress, chequebookBalance, beeRelease, nodeTopology, setStatusChecksVisible} = props
|
const {
|
||||||
const classes = useStyles();
|
nodeHealth,
|
||||||
const [activeStep, setActiveStep] = useState(0);
|
nodeApiHealth,
|
||||||
const [completed, setCompleted] = useState<{ [k: number]: boolean }>({});
|
nodeAddresses,
|
||||||
const steps = getSteps();
|
chequebookAddress,
|
||||||
|
chequebookBalance,
|
||||||
|
beeRelease,
|
||||||
|
nodeTopology,
|
||||||
|
setStatusChecksVisible,
|
||||||
|
} = props
|
||||||
|
const classes = useStyles()
|
||||||
|
const [activeStep, setActiveStep] = useState(0)
|
||||||
|
const [completed, setCompleted] = useState<{ [k: number]: boolean }>({})
|
||||||
|
const steps = getSteps()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleComplete = (index: number) => {
|
const handleComplete = (index: number) => {
|
||||||
const newCompleted = completed;
|
const newCompleted = completed
|
||||||
newCompleted[index] = true;
|
newCompleted[index] = true
|
||||||
setCompleted(newCompleted);
|
setCompleted(newCompleted)
|
||||||
};
|
}
|
||||||
|
|
||||||
const evaluateNodeStatus = () => {
|
const evaluateNodeStatus = () => {
|
||||||
if (nodeHealth?.status === 'ok') {
|
if (nodeHealth?.status === 'ok') {
|
||||||
@@ -88,7 +144,7 @@ export default function NodeSetupWorkflow(props: any) {
|
|||||||
setActiveStep(3)
|
setActiveStep(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chequebookAddress?.chequebookaddress && chequebookBalance.totalBalance > 0) {
|
if (chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance.totalBalance > 0) {
|
||||||
handleComplete(3)
|
handleComplete(3)
|
||||||
setActiveStep(4)
|
setActiveStep(4)
|
||||||
}
|
}
|
||||||
@@ -104,45 +160,62 @@ export default function NodeSetupWorkflow(props: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
evaluateNodeStatus()
|
evaluateNodeStatus()
|
||||||
}, [nodeHealth, nodeApiHealth, nodeAddresses, chequebookAddress, beeRelease, chequebookBalance, nodeTopology, completed])
|
}, [
|
||||||
|
nodeHealth,
|
||||||
|
nodeApiHealth,
|
||||||
|
nodeAddresses,
|
||||||
|
chequebookAddress,
|
||||||
|
beeRelease,
|
||||||
|
chequebookBalance,
|
||||||
|
nodeTopology,
|
||||||
|
completed,
|
||||||
|
])
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSetupComplete = () => {
|
const handleSetupComplete = () => {
|
||||||
setStatusChecksVisible(false)
|
setStatusChecksVisible(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<Typography variant="h4" gutterBottom>
|
<Typography variant="h4" gutterBottom>
|
||||||
Node Setup
|
Node Setup
|
||||||
<span style={{marginLeft:'25px'}}>
|
<span style={{ marginLeft: '25px' }}>
|
||||||
<Button variant='outlined' size='small' onClick={() => window.location.reload()}><Sync/><span style={{marginLeft:'7px'}}>Refresh Checks</span></Button>
|
<Button variant="outlined" size="small" onClick={() => window.location.reload()}>
|
||||||
|
<Sync />
|
||||||
|
<span style={{ marginLeft: '7px' }}>Refresh Checks</span>
|
||||||
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
|
<Stepper nonLinear activeStep={activeStep} orientation="vertical">
|
||||||
{steps.map((label, index) => (
|
{steps.map((label, index) => (
|
||||||
<Step key={label}>
|
<Step key={label}>
|
||||||
<StepLabel
|
<StepLabel
|
||||||
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
|
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
|
||||||
StepIconComponent={() => {
|
StepIconComponent={() => {
|
||||||
if(completed[index])
|
if (completed[index]) {
|
||||||
return <CheckCircle style={{color:'#32c48d', height: '25px', cursor:'pointer'}} />
|
return <CheckCircle style={{ color: '#32c48d', height: '25px', cursor: 'pointer' }} />
|
||||||
else {
|
} else {
|
||||||
return <Error style={{color:'#c9201f', height: '25px', cursor:'pointer'}} />
|
return <Error style={{ color: '#c9201f', height: '25px', cursor: 'pointer' }} />
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StepButton onClick={() => setActiveStep(index === activeStep ? 6 : index)} style={{justifyContent:'space-between'}}>
|
<StepButton
|
||||||
<div style={{display:'flex'}}>
|
onClick={() => setActiveStep(index === activeStep ? 6 : index)}
|
||||||
<div style={{marginTop:'5px'}}>{label}</div>
|
style={{ justifyContent: 'space-between' }}
|
||||||
<div style={{marginLeft:'12px'}}>{index === activeStep ? <ExpandLessSharp /> : <ExpandMoreSharp />}</div>
|
>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ marginTop: '5px' }}>{label}</div>
|
||||||
|
<div style={{ marginLeft: '12px' }}>
|
||||||
|
{index === activeStep ? <ExpandLessSharp /> : <ExpandMoreSharp />}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</StepButton>
|
</StepButton>
|
||||||
</StepLabel>
|
</StepLabel>
|
||||||
@@ -150,20 +223,11 @@ export default function NodeSetupWorkflow(props: any) {
|
|||||||
<Typography component="div">{getStepContent(index, props)}</Typography>
|
<Typography component="div">{getStepContent(index, props)}</Typography>
|
||||||
<div className={classes.actionsContainer}>
|
<div className={classes.actionsContainer}>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
|
||||||
disabled={activeStep === 0}
|
|
||||||
onClick={handleBack}
|
|
||||||
className={classes.button}
|
|
||||||
>
|
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button variant="contained" color="primary" onClick={handleNext} className={classes.button}>
|
||||||
variant="contained"
|
Next
|
||||||
color="primary"
|
|
||||||
onClick={handleNext}
|
|
||||||
className={classes.button}
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -174,10 +238,7 @@ export default function NodeSetupWorkflow(props: any) {
|
|||||||
{Object.values(completed).filter(value => value).length === 6 ? (
|
{Object.values(completed).filter(value => value).length === 6 ? (
|
||||||
<Paper square elevation={0} className={classes.resetContainer}>
|
<Paper square elevation={0} className={classes.resetContainer}>
|
||||||
<Typography>Bee setup complete! Welcome to the swarm and the internet of decentralized storage</Typography>
|
<Typography>Bee setup complete! Welcome to the swarm and the internet of decentralized storage</Typography>
|
||||||
<Button
|
<Button onClick={handleBack} className={classes.button}>
|
||||||
onClick={handleBack}
|
|
||||||
className={classes.button}
|
|
||||||
>
|
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleSetupComplete} variant="contained" color="primary" className={classes.button}>
|
<Button onClick={handleSetupComplete} variant="contained" color="primary" className={classes.button}>
|
||||||
@@ -186,5 +247,5 @@ export default function NodeSetupWorkflow(props: any) {
|
|||||||
</Paper>
|
</Paper>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,57 @@
|
|||||||
import { Typography } from '@material-ui/core/';
|
import { Typography } from '@material-ui/core/'
|
||||||
import { CheckCircle, Warning } from '@material-ui/icons/';
|
import { CheckCircle, Warning } from '@material-ui/icons/'
|
||||||
import EthereumAddress from '../../../components/EthereumAddress';
|
import EthereumAddress from '../../../components/EthereumAddress'
|
||||||
import DepositModal from '../../../components/DepositModal';
|
import DepositModal from '../../../components/DepositModal'
|
||||||
import CodeBlockTabs from '../../../components/CodeBlockTabs';
|
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
||||||
|
import type { ChequebookAddressResponse, ChequebookBalanceResponse } from '@ethersphere/bee-js'
|
||||||
|
import type { ReactElement } from 'react'
|
||||||
|
|
||||||
export default function ChequebookDeployFund(props: any) {
|
interface Props {
|
||||||
return (
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
<div>
|
chequebookBalance: ChequebookBalanceResponse | null
|
||||||
<p style={{marginBottom:'20px', display:'flex'}}>
|
isLoadingChequebookAddress: boolean
|
||||||
<span style={{ marginRight:'40px'}} >Deploy chequebook and fund with BZZ</span>
|
isLoadingChequebookBalance: boolean
|
||||||
{props.chequebookAddress?.chequebookaddress ?
|
|
||||||
<DepositModal />
|
|
||||||
: null }
|
|
||||||
</p>
|
|
||||||
<div style={{ marginBottom:'10px' }}>
|
|
||||||
{props.chequebookAddress?.chequebookaddress && props.chequebookBalance && props.chequebookBalance?.totalBalance > 0 ?
|
|
||||||
<div>
|
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
|
||||||
<span>Your chequebook is deployed and funded!</span>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
props.loadingChequebookAddress || props.isLoadingChequebookBalance ?
|
|
||||||
null
|
|
||||||
:
|
|
||||||
<div>
|
|
||||||
<Warning style={{color:'#ff9800', marginRight: '7px', height: '18px'}} />
|
|
||||||
<span>Your chequebook is either not deployed or funded. Run the below commands to get your address and deposit ETH. Then visit the BZZaar here <Typography variant='button'>https://bzz.ethswarm.org/?transaction=buy&amount=10&slippage=30&receiver=[ENTER_ADDRESS_HERE]</Typography> to get BZZ</span>
|
|
||||||
<CodeBlockTabs
|
|
||||||
showLineNumbers
|
|
||||||
linux={`bee-get-addr`}
|
|
||||||
mac={`bee-get-addr`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<Typography variant="subtitle1" gutterBottom>Chequebook Address</Typography>
|
|
||||||
<EthereumAddress
|
|
||||||
address={props.chequebookAddress?.chequebookaddress}
|
|
||||||
network={'goerli'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ChequebookDeployFund = (props: Props): ReactElement => (
|
||||||
|
<div>
|
||||||
|
<p style={{ marginBottom: '20px', display: 'flex' }}>
|
||||||
|
<span style={{ marginRight: '40px' }}>Deploy chequebook and fund with BZZ</span>
|
||||||
|
{props.chequebookAddress?.chequebookaddress ? <DepositModal /> : null}
|
||||||
|
</p>
|
||||||
|
<div style={{ marginBottom: '10px' }}>
|
||||||
|
{
|
||||||
|
// FIXME: this should be broken up
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
|
props.chequebookAddress?.chequebookaddress &&
|
||||||
|
props.chequebookBalance &&
|
||||||
|
props.chequebookBalance?.totalBalance > 0 ? (
|
||||||
|
<div>
|
||||||
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
|
<span>Your chequebook is deployed and funded!</span>
|
||||||
|
</div>
|
||||||
|
) : props.isLoadingChequebookAddress || props.isLoadingChequebookBalance ? null : (
|
||||||
|
<div>
|
||||||
|
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
|
||||||
|
<span>
|
||||||
|
Your chequebook is either not deployed or funded. Run the below commands to get your address and deposit
|
||||||
|
ETH. Then visit the BZZaar here{' '}
|
||||||
|
<Typography variant="button">
|
||||||
|
https://bzz.ethswarm.org/?transaction=buy&amount=10&slippage=30&receiver=[ENTER_ADDRESS_HERE]
|
||||||
|
</Typography>{' '}
|
||||||
|
to get BZZ
|
||||||
|
</span>
|
||||||
|
<CodeBlockTabs showLineNumbers linux={`bee-get-addr`} mac={`bee-get-addr`} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
/* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Chequebook Address
|
||||||
|
</Typography>
|
||||||
|
<EthereumAddress address={props.chequebookAddress?.chequebookaddress} network={'goerli'} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default ChequebookDeployFund
|
||||||
|
|||||||
@@ -1,76 +1,101 @@
|
|||||||
import React from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/';
|
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/'
|
||||||
import MuiAlert from '@material-ui/lab/Alert';
|
import MuiAlert from '@material-ui/lab/Alert'
|
||||||
import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/';
|
import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/'
|
||||||
|
|
||||||
import ConnectToHost from '../../../components/ConnectToHost';
|
import ConnectToHost from '../../../components/ConnectToHost'
|
||||||
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
||||||
|
import type { Health } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
export default function NodeConnectionCheck(props: any) {
|
interface Props {
|
||||||
return (
|
nodeHealth: Health | null
|
||||||
|
debugApiHost: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NodeConnectionCheck(props: Props): ReactElement {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Connect to Bee Node Debug API</p>
|
||||||
|
<div>
|
||||||
|
<div style={{ display: 'flex', marginBottom: '25px' }}>
|
||||||
|
{props.nodeHealth?.status === 'ok' ? (
|
||||||
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
|
) : (
|
||||||
|
<Error style={{ color: '#c9201f', marginRight: '7px', height: '18px' }} />
|
||||||
|
)}
|
||||||
|
<span style={{ marginRight: '15px' }}>
|
||||||
|
Debug API (<Typography variant="button">{props.debugApiHost}</Typography>)
|
||||||
|
</span>
|
||||||
|
<ConnectToHost hostName={'debug_api_host'} defaultHost={props.debugApiHost} />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>Connect to Bee Node Debug API</p>
|
{props.nodeHealth?.status !== 'ok' ? (
|
||||||
<div>
|
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
|
||||||
<div style={{display:'flex', marginBottom: '25px'}}>
|
We cannot connect to your nodes debug API at{' '}
|
||||||
{ props.nodeHealth?.status === 'ok' ?
|
<Typography variant="button">{props.debugApiHost}</Typography>. Please check the following to troubleshoot
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
your issue.
|
||||||
:
|
<Accordion style={{ marginTop: '20px' }}>
|
||||||
<Error style={{color:'#c9201f', marginRight: '7px', height: '18px'}} />
|
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
|
||||||
}
|
<Typography>Troubleshoot</Typography>
|
||||||
<span style={{marginRight:'15px'}}>Debug API (<Typography variant="button">{props.debugApiHost}</Typography>)</span>
|
</AccordionSummary>
|
||||||
<ConnectToHost hostName={'debug_api_host'} defaultHost={props.debugApiHost} />
|
<AccordionDetails>
|
||||||
</div>
|
<Typography component="div">
|
||||||
<div>
|
|
||||||
{ props.nodeHealth?.status !== 'ok' ?
|
|
||||||
<Typography component="div" variant="body2" gutterBottom style={{margin: '15px'}}>
|
|
||||||
We cannot connect to your nodes debug API at <Typography variant="button">{props.debugApiHost}</Typography>. Please check the following to troubleshoot your issue.
|
|
||||||
<Accordion style={{marginTop:'20px'}}>
|
|
||||||
<AccordionSummary
|
|
||||||
expandIcon={<ExpandMoreSharp />}
|
|
||||||
aria-controls="panel1a-content"
|
|
||||||
id="panel1a-header"
|
|
||||||
>
|
|
||||||
<Typography>Troubleshoot</Typography>
|
|
||||||
</AccordionSummary>
|
|
||||||
<AccordionDetails>
|
|
||||||
<Typography component="div">
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Check the status of your node by running the below command to see if your node is running.</li>
|
<li>
|
||||||
|
Check the status of your node by running the below command to see if your node is running.
|
||||||
|
</li>
|
||||||
<CodeBlockTabs
|
<CodeBlockTabs
|
||||||
showLineNumbers
|
showLineNumbers
|
||||||
linux={`sudo systemctl status bee`}
|
linux={`sudo systemctl status bee`}
|
||||||
mac={`brew services status swarm-bee`}
|
mac={`brew services status swarm-bee`}
|
||||||
/>
|
/>
|
||||||
<li>If your node is running, check your firewall settings to make sure that port 1635 (or your custom specified port) is bound to localhost. If your node is not running try executing the below command to start your bee node</li>
|
<li>
|
||||||
<MuiAlert style={{marginTop:'10px', marginBottom:'10px'}} elevation={6} variant="filled" severity="error">
|
If your node is running, check your firewall settings to make sure that port 1635 (or your
|
||||||
Your debug node API should never be completely open to the internet. If you want to connect remotely, make sure your firewall settings are set to only allow specific trusted IP addresses and block all other ports. A simple google search for "what is my ip" will show you your computers public IP address to allow.
|
custom specified port) is bound to localhost. If your node is not running try executing the
|
||||||
|
below command to start your bee node
|
||||||
|
</li>
|
||||||
|
<MuiAlert
|
||||||
|
style={{ marginTop: '10px', marginBottom: '10px' }}
|
||||||
|
elevation={6}
|
||||||
|
variant="filled"
|
||||||
|
severity="error"
|
||||||
|
>
|
||||||
|
Your debug node API should never be completely open to the internet. If you want to connect
|
||||||
|
remotely, make sure your firewall settings are set to only allow specific trusted IP addresses
|
||||||
|
and block all other ports. A simple google search for "what is my ip" will show you
|
||||||
|
your computers public IP address to allow.
|
||||||
</MuiAlert>
|
</MuiAlert>
|
||||||
<CodeBlockTabs
|
<CodeBlockTabs
|
||||||
showLineNumbers
|
showLineNumbers
|
||||||
linux={`sudo systemctl start bee`}
|
linux={`sudo systemctl start bee`}
|
||||||
mac={`brew services start swarm-bee`}
|
mac={`brew services start swarm-bee`}
|
||||||
/>
|
/>
|
||||||
<li>Run the commands to validate your node is running and see the log output.</li>
|
<li>Run the commands to validate your node is running and see the log output.</li>
|
||||||
<CodeBlockTabs
|
<CodeBlockTabs
|
||||||
showLineNumbers
|
showLineNumbers
|
||||||
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
|
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
|
||||||
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
|
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
|
||||||
/>
|
/>
|
||||||
<li>Lastly, check your nodes configuration settings to validate the debug API is enabled and the Cross Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter <strong>debug-api-enable</strong> must be set to <strong>true</strong> and <strong>cors-allowed-origins</strong> must be set to your host domain or IP. If edits are made to the configuration run the restart command below for changes to take effect.</li>
|
<li>
|
||||||
|
Lastly, check your nodes configuration settings to validate the debug API is enabled and the
|
||||||
|
Cross Origin Resource Sharing (CORS) setting is configured to allow your host. Config parameter{' '}
|
||||||
|
<strong>debug-api-enable</strong> must be set to <strong>true</strong> and{' '}
|
||||||
|
<strong>cors-allowed-origins</strong> must be set to your host domain or IP. If edits are made
|
||||||
|
to the configuration run the restart command below for changes to take effect.
|
||||||
|
</li>
|
||||||
<CodeBlockTabs
|
<CodeBlockTabs
|
||||||
showLineNumbers
|
showLineNumbers
|
||||||
linux={`sudo vi /etc/bee/bee.yaml\nsudo systemctl restart bee`}
|
linux={`sudo vi /etc/bee/bee.yaml\nsudo systemctl restart bee`}
|
||||||
mac={`sudo vi /etc/bee/bee.yaml \nbrew services restart swarm-bee`}
|
mac={`sudo vi /etc/bee/bee.yaml \nbrew services restart swarm-bee`}
|
||||||
/>
|
/>
|
||||||
</ol>
|
</ol>
|
||||||
</Typography>
|
</Typography>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Typography>
|
</Typography>
|
||||||
:
|
) : null}
|
||||||
null}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,56 @@
|
|||||||
import React from 'react';
|
import type { ReactElement } from 'react'
|
||||||
import { Typography } from '@material-ui/core/';
|
import { Typography } from '@material-ui/core/'
|
||||||
import { CheckCircle, Warning } from '@material-ui/icons/';
|
import { CheckCircle, Warning } from '@material-ui/icons/'
|
||||||
import EthereumAddress from '../../../components/EthereumAddress';
|
import EthereumAddress from '../../../components/EthereumAddress'
|
||||||
|
import type { NodeAddresses } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
export default function EthereumConnectionCheck(props: any) {
|
interface Props {
|
||||||
return (
|
nodeAddresses: NodeAddresses | null
|
||||||
<div>
|
isLoadingNodeAddresses: boolean
|
||||||
<p>Connect to the ethereum blockchain.</p>
|
}
|
||||||
<div style={{ marginBottom:'10px' }}>
|
|
||||||
{props.nodeAddresses?.ethereum ?
|
export default function EthereumConnectionCheck(props: Props): ReactElement {
|
||||||
<div>
|
return (
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
<div>
|
||||||
<span>Your connected to the Ethereum network</span>
|
<p>Connect to the ethereum blockchain.</p>
|
||||||
</div>
|
<div style={{ marginBottom: '10px' }}>
|
||||||
:
|
{
|
||||||
props.loadingNodeAddresses ?
|
// FIXME: this should be broken up
|
||||||
null
|
/* eslint-disable no-nested-ternary */
|
||||||
:
|
props.nodeAddresses?.ethereum ? (
|
||||||
<div>
|
<div>
|
||||||
<Warning style={{color:'#ff9800', marginRight: '7px', height: '18px'}} />
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
<span>Your not connected to the Ethereum network. </span>
|
<span>Your connected to the Ethereum network</span>
|
||||||
<p>Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your chequebook contract. You can run <a href='https://github.com/goerli/testnet' rel='noreferrer' target='_blank'>your own Goerli node</a>, or use a provider such as <a href='https://rpc.slock.it/goerli' rel='noreferrer' target='_blank'>rpc.slock.it/goerli</a> or <a href='https://infura.io/' rel='noreferrer' target='_blank'>Infura</a>.
|
</div>
|
||||||
|
) : props.isLoadingNodeAddresses ? null : (
|
||||||
By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead, simply change your <strong>--swap-endpoint</strong> in your configuration file.
|
<div>
|
||||||
</p>
|
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
|
||||||
</div>
|
<span>Your not connected to the Ethereum network. </span>
|
||||||
}
|
<p>
|
||||||
</div>
|
Your Bee node must have access to the Ethereum blockchain, so that it can interact and deploy your
|
||||||
<Typography variant="subtitle1" gutterBottom>Node Address</Typography>
|
chequebook contract. You can run{' '}
|
||||||
<EthereumAddress
|
<a href="https://github.com/goerli/testnet" rel="noreferrer" target="_blank">
|
||||||
address={props.nodeAddresses?.ethereum}
|
your own Goerli node
|
||||||
network={'goerli'}
|
</a>
|
||||||
/>
|
, or use a provider such as{' '}
|
||||||
</div>
|
<a href="https://rpc.slock.it/goerli" rel="noreferrer" target="_blank">
|
||||||
)
|
rpc.slock.it/goerli
|
||||||
|
</a>{' '}
|
||||||
|
or{' '}
|
||||||
|
<a href="https://infura.io/" rel="noreferrer" target="_blank">
|
||||||
|
Infura
|
||||||
|
</a>
|
||||||
|
. By default, Bee expects a local Goerli node at http://localhost:8545. To use a provider instead,
|
||||||
|
simply change your <strong>--swap-endpoint</strong> in your configuration file.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) /* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<Typography variant="subtitle1" gutterBottom>
|
||||||
|
Node Address
|
||||||
|
</Typography>
|
||||||
|
<EthereumAddress address={props.nodeAddresses?.ethereum} network={'goerli'} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +1,71 @@
|
|||||||
import React from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/';
|
import { Typography, Accordion, AccordionSummary, AccordionDetails } from '@material-ui/core/'
|
||||||
import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/';
|
import { CheckCircle, Error, ExpandMoreSharp } from '@material-ui/icons/'
|
||||||
|
|
||||||
import ConnectToHost from '../../../components/ConnectToHost';
|
import ConnectToHost from '../../../components/ConnectToHost'
|
||||||
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
||||||
|
|
||||||
export default function NodeConnectionCheck(props: any) {
|
interface Props {
|
||||||
return (
|
nodeApiHealth: boolean
|
||||||
<div>
|
apiHost: string
|
||||||
<p>Connect to Bee Node API</p>
|
}
|
||||||
<div style={{display:'flex', marginBottom: '25px'}}>
|
|
||||||
{ props.nodeApiHealth ?
|
export default function NodeConnectionCheck(props: Props): ReactElement {
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
return (
|
||||||
:
|
<div>
|
||||||
<Error style={{color:'#c9201f', marginRight: '7px', height: '18px'}} />
|
<p>Connect to Bee Node API</p>
|
||||||
}
|
<div style={{ display: 'flex', marginBottom: '25px' }}>
|
||||||
<span style={{marginRight:'15px'}}>Node API (<Typography variant="button">{props.apiHost}</Typography>)</span>
|
{props.nodeApiHealth ? (
|
||||||
<ConnectToHost hostName='api_host' defaultHost={props.apiHost} />
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
</div>
|
) : (
|
||||||
<div>
|
<Error style={{ color: '#c9201f', marginRight: '7px', height: '18px' }} />
|
||||||
{ !props.nodeApiHealth ?
|
)}
|
||||||
<Typography component="div" variant="body2" gutterBottom style={{margin: '15px'}}>
|
<span style={{ marginRight: '15px' }}>
|
||||||
We cannot connect to your nodes API at <Typography variant="button">{props.apiHost}</Typography>. Please check the following to troubleshoot your issue.
|
Node API (<Typography variant="button">{props.apiHost}</Typography>)
|
||||||
<Accordion style={{marginTop:'20px'}}>
|
</span>
|
||||||
<AccordionSummary
|
<ConnectToHost hostName="api_host" defaultHost={props.apiHost} />
|
||||||
expandIcon={<ExpandMoreSharp />}
|
</div>
|
||||||
aria-controls="panel1a-content"
|
<div>
|
||||||
id="panel1a-header"
|
{!props.nodeApiHealth ? (
|
||||||
>
|
<Typography component="div" variant="body2" gutterBottom style={{ margin: '15px' }}>
|
||||||
<Typography>Troubleshoot</Typography>
|
We cannot connect to your nodes API at <Typography variant="button">{props.apiHost}</Typography>. Please
|
||||||
</AccordionSummary>
|
check the following to troubleshoot your issue.
|
||||||
<AccordionDetails>
|
<Accordion style={{ marginTop: '20px' }}>
|
||||||
<Typography component="div">
|
<AccordionSummary expandIcon={<ExpandMoreSharp />} aria-controls="panel1a-content" id="panel1a-header">
|
||||||
<ol>
|
<Typography>Troubleshoot</Typography>
|
||||||
<li>Check the status of your node by running the below command to see if your node is running.</li>
|
</AccordionSummary>
|
||||||
<CodeBlockTabs
|
<AccordionDetails>
|
||||||
showLineNumbers
|
<Typography component="div">
|
||||||
linux={`sudo systemctl status bee`}
|
<ol>
|
||||||
mac={`brew services status swarm-bee`}
|
<li>Check the status of your node by running the below command to see if your node is running.</li>
|
||||||
/>
|
<CodeBlockTabs
|
||||||
<li>If your node is running, check your firewall settings to make sure that port 1633 (or your custom specified port) is exposed to the internet. If your node is not running try executing the below command to start your bee node</li>
|
showLineNumbers
|
||||||
<CodeBlockTabs
|
linux={`sudo systemctl status bee`}
|
||||||
showLineNumbers
|
mac={`brew services status swarm-bee`}
|
||||||
linux={`sudo systemctl start bee`}
|
/>
|
||||||
mac={`brew services start swarm-bee`}
|
<li>
|
||||||
/>
|
If your node is running, check your firewall settings to make sure that port 1633 (or your custom
|
||||||
<li>Run the commands to validate your node is running and see the log output.</li>
|
specified port) is exposed to the internet. If your node is not running try executing the below
|
||||||
<CodeBlockTabs
|
command to start your bee node
|
||||||
showLineNumbers
|
</li>
|
||||||
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
|
<CodeBlockTabs
|
||||||
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
|
showLineNumbers
|
||||||
/>
|
linux={`sudo systemctl start bee`}
|
||||||
</ol>
|
mac={`brew services start swarm-bee`}
|
||||||
</Typography>
|
/>
|
||||||
</AccordionDetails>
|
<li>Run the commands to validate your node is running and see the log output.</li>
|
||||||
</Accordion>
|
<CodeBlockTabs
|
||||||
</Typography>
|
showLineNumbers
|
||||||
:
|
linux={`sudo systemctl status bee \njournalctl --lines=100 --follow --unit bee`}
|
||||||
null}
|
mac={`brew services status swarm-bee \ntail -f /usr/local/var/log/swarm-bee/bee.log`}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</ol>
|
||||||
)
|
</Typography>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,53 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement } from 'react'
|
||||||
import { Typography } from '@material-ui/core/';
|
import { Typography } from '@material-ui/core/'
|
||||||
import { CheckCircle, Warning } from '@material-ui/icons/';
|
import { CheckCircle, Warning } from '@material-ui/icons/'
|
||||||
|
import { Topology } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
export default function PeerConnection(props: any) {
|
interface Props {
|
||||||
return (
|
nodeTopology: Topology | null
|
||||||
<div>
|
isLoadingNodeTopology: boolean
|
||||||
<p>Connect to Peers</p>
|
}
|
||||||
<div style={{ marginBottom:'10px' }}>
|
|
||||||
{props.nodeTopology.connected && props.nodeTopology.connected > 0 ?
|
export default function PeerConnection(props: Props): ReactElement {
|
||||||
<div>
|
return (
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
<div>
|
||||||
<span>Your connected to {props.nodeTopology.connected} peers!</span>
|
<p>Connect to Peers</p>
|
||||||
</div>
|
<div style={{ marginBottom: '10px' }}>
|
||||||
:
|
html_url
|
||||||
props.loadingNodeTopology ?
|
{
|
||||||
null
|
// FIXME: this should be broken up
|
||||||
:
|
/* eslint-disable no-nested-ternary */
|
||||||
<div>
|
props.nodeTopology?.connected && props.nodeTopology?.connected > 0 ? (
|
||||||
<Warning style={{color:'#ff9800', marginRight: '7px', height: '18px'}} />
|
<div>
|
||||||
<span>Your node is not connected to any peers</span>
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
</div>
|
<span>Your connected to {props.nodeTopology.connected} peers!</span>
|
||||||
}
|
</div>
|
||||||
</div>
|
) : props.isLoadingNodeTopology ? null : (
|
||||||
<div style={{display:'flex'}}>
|
<div>
|
||||||
<div style={{marginRight:'30px'}}>
|
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
|
||||||
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
|
<span>Your node is not connected to any peers</span>
|
||||||
<span>Connected Peers</span>
|
</div>
|
||||||
</Typography>
|
) /* eslint-enable no-nested-ternary */
|
||||||
<Typography component="h2" variant="h5">
|
}
|
||||||
{ props.nodeTopology.connected }
|
</div>
|
||||||
</Typography>
|
<div style={{ display: 'flex' }}>
|
||||||
</div>
|
<div style={{ marginRight: '30px' }}>
|
||||||
<div>
|
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
|
||||||
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
|
<span>Connected Peers</span>
|
||||||
<span>Discovered Nodes</span>
|
</Typography>
|
||||||
</Typography>
|
<Typography component="h2" variant="h5">
|
||||||
<Typography component="h2" variant="h5">
|
{props.nodeTopology?.connected}
|
||||||
{ props.nodeTopology.population }
|
</Typography>
|
||||||
</Typography>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
</div>
|
<Typography component="div" variant="subtitle1" gutterBottom color="textSecondary">
|
||||||
</div>
|
<span>Discovered Nodes</span>
|
||||||
)
|
</Typography>
|
||||||
|
<Typography component="h2" variant="h5">
|
||||||
|
{props.nodeTopology?.population}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,81 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement } from 'react'
|
||||||
import { Typography } from '@material-ui/core/';
|
import { Typography } from '@material-ui/core/'
|
||||||
import { CheckCircle, Warning } from '@material-ui/icons/';
|
import { CheckCircle, Warning } from '@material-ui/icons/'
|
||||||
import CodeBlockTabs from '../../../components/CodeBlockTabs';
|
import CodeBlockTabs from '../../../components/CodeBlockTabs'
|
||||||
|
import { Health } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
export default function VersionCheck(props: any) {
|
interface Props {
|
||||||
return (
|
beeRelease: LatestBeeRelease | null
|
||||||
<div>
|
isLoadingBeeRelease: boolean
|
||||||
<p>Check to make sure the latest version of <a href='https://github.com/ethersphere/bee' rel='noreferrer' target='_blank'>Bee</a> is running</p>
|
nodeHealth: Health | null
|
||||||
{props.beeRelease && props.beeRelease.name === `v${props.nodeReadiness?.version?.split('-')[0]}` ?
|
}
|
||||||
<div>
|
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px', height: '18px'}} />
|
export default function VersionCheck(props: Props): ReactElement {
|
||||||
<span>Your running the latest version of Bee</span>
|
return (
|
||||||
</div>
|
<div>
|
||||||
:
|
<p>
|
||||||
props.loadingBeeRelease ?
|
Check to make sure the latest version of{' '}
|
||||||
null
|
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
|
||||||
:
|
Bee
|
||||||
<div>
|
</a>{' '}
|
||||||
<Warning style={{color:'#ff9800', marginRight: '7px', height: '18px'}} />
|
is running
|
||||||
<span>Your Bee version is out of date. Please update to the <a href={props.beeRelease.html_url} rel='noreferrer' target='_blank'>latest</a> before continuing. Rerun the installation script below to upgrade. Reference the docs for help with updating. <a href='https://docs.ethswarm.org/docs/installation/manual#upgrading-bee' rel='noreferrer' target='_blank'>Docs</a></span>
|
</p>
|
||||||
<CodeBlockTabs
|
{
|
||||||
showLineNumbers
|
// FIXME: this should be broken up
|
||||||
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${props.beeRelease.name}/bee_${props.nodeReadiness?.version?.split('-')[0]}_amd64.deb\nsudo dpkg -i bee_${props.nodeReadiness?.version?.split('-')[0]}_amd64.deb`}
|
/* eslint-disable no-nested-ternary */
|
||||||
mac={`bee version\nbrew tap ethersphere/tap\nbrew install swarm-bee\nbrew services start swarm-bee`}
|
props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? (
|
||||||
/>
|
<div>
|
||||||
</div>
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px', height: '18px' }} />
|
||||||
}
|
<span>Your running the latest version of Bee</span>
|
||||||
<div style={{display:'flex'}}>
|
</div>
|
||||||
<div style={{marginRight:'30px'}}>
|
) : props.isLoadingBeeRelease ? null : (
|
||||||
<p><span>Current Version</span></p>
|
<div>
|
||||||
<Typography component="h5" variant="h5">
|
<Warning style={{ color: '#ff9800', marginRight: '7px', height: '18px' }} />
|
||||||
<span>{props.nodeReadiness?.version ? ` v${props.nodeReadiness?.version?.split('-')[0]}` : '-'}</span>
|
<span>
|
||||||
</Typography>
|
Your Bee version is out of date. Please update to the{' '}
|
||||||
</div>
|
<a href={props.beeRelease?.html_url} rel="noreferrer" target="_blank">
|
||||||
<div>
|
latest
|
||||||
<p><span>Latest Version</span></p>
|
</a>{' '}
|
||||||
<Typography component="h5" variant="h5">
|
before continuing. Rerun the installation script below to upgrade. Reference the docs for help with
|
||||||
<span>{props.beeRelease && props.beeRelease.name ? props.beeRelease.name : '-'}</span>
|
updating.{' '}
|
||||||
</Typography>
|
<a
|
||||||
</div>
|
href="https://docs.ethswarm.org/docs/installation/manual#upgrading-bee"
|
||||||
</div>
|
rel="noreferrer"
|
||||||
</div>
|
target="_blank"
|
||||||
)
|
>
|
||||||
|
Docs
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<CodeBlockTabs
|
||||||
|
showLineNumbers
|
||||||
|
linux={`bee version\nwget https://github.com/ethersphere/bee/releases/download/${
|
||||||
|
props.beeRelease?.name
|
||||||
|
}/bee_${props.nodeHealth?.version?.split('-')[0]}_amd64.deb\nsudo dpkg -i bee_${
|
||||||
|
props.nodeHealth?.version?.split('-')[0]
|
||||||
|
}_amd64.deb`}
|
||||||
|
mac={`bee version\nbrew tap ethersphere/tap\nbrew install swarm-bee\nbrew services start swarm-bee`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) /* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ marginRight: '30px' }}>
|
||||||
|
<p>
|
||||||
|
<span>Current Version</span>
|
||||||
|
</p>
|
||||||
|
<Typography component="h5" variant="h5">
|
||||||
|
<span>{props.nodeHealth?.version ? ` v${props.nodeHealth?.version?.split('-')[0]}` : '-'}</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span>Latest Version</span>
|
||||||
|
</p>
|
||||||
|
<Typography component="h5" variant="h5">
|
||||||
|
<span>{props.beeRelease && props.beeRelease.name ? props.beeRelease.name : '-'}</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+126
-114
@@ -1,13 +1,13 @@
|
|||||||
import React, { useState } from 'react'
|
import { ReactElement, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles';
|
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/';
|
import { Card, CardContent, Typography, Chip, Button } from '@material-ui/core/'
|
||||||
import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/';
|
import { CheckCircle, Error, ArrowRight, ArrowDropUp } from '@material-ui/icons/'
|
||||||
import { Skeleton } from '@material-ui/lab';
|
import { Skeleton } from '@material-ui/lab'
|
||||||
import type { Health, NodeAddresses, Topology } from '@ethersphere/bee-js';
|
import type { Health, NodeAddresses, Topology } from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -22,120 +22,132 @@ const useStyles = makeStyles((theme: Theme) =>
|
|||||||
flex: '1 0 auto',
|
flex: '1 0 auto',
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
color: '#2145a0',
|
color: '#2145a0',
|
||||||
backgroundColor: '#e1effe',
|
backgroundColor: '#e1effe',
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
|
|
||||||
interface IProps{
|
interface Props {
|
||||||
nodeHealth: Health,
|
nodeHealth: Health | null
|
||||||
loadingNodeHealth: boolean,
|
loadingNodeHealth: boolean
|
||||||
beeRelease: any,
|
beeRelease: LatestBeeRelease | null
|
||||||
loadingBeeRelease: boolean,
|
loadingBeeRelease: boolean
|
||||||
nodeAddresses: NodeAddresses,
|
nodeAddresses: NodeAddresses
|
||||||
nodeTopology: Topology,
|
nodeTopology: Topology
|
||||||
loadingNodeTopology: boolean,
|
loadingNodeTopology: boolean
|
||||||
setStatusChecksVisible: any,
|
setStatusChecksVisible: (value: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusCard(props: IProps) {
|
function StatusCard(props: Props): ReactElement {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
|
|
||||||
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<Boolean>(false)
|
const [underlayAddressesVisible, setUnderlayAddresessVisible] = useState<boolean>(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Card className={classes.root}>
|
<Card className={classes.root}>
|
||||||
{ !props.loadingNodeHealth && props.nodeHealth ?
|
{!props.loadingNodeHealth && props.nodeHealth ? (
|
||||||
<div className={classes.details}>
|
<div className={classes.details}>
|
||||||
<CardContent className={classes.content}>
|
<CardContent className={classes.content}>
|
||||||
<Typography component="h5" variant="h5" style={{display:'flex', justifyContent:'space-between'}}>
|
<Typography component="h5" variant="h5" style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
{ props.nodeHealth.status === 'ok' ?
|
{props.nodeHealth.status === 'ok' ? (
|
||||||
<div>
|
<div>
|
||||||
<CheckCircle style={{color:'#32c48d', marginRight: '7px'}} />
|
<CheckCircle style={{ color: '#32c48d', marginRight: '7px' }} />
|
||||||
<span>Connected to Bee Node</span>
|
<span>Connected to Bee Node</span>
|
||||||
</div>
|
</div>
|
||||||
:
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<Error style={{color:'#c9201f', marginRight: '7px'}} />
|
<Error style={{ color: '#c9201f', marginRight: '7px' }} />
|
||||||
<span>Could not connect to Bee Node</span>
|
<span>Could not connect to Bee Node</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
<Button variant='outlined' color='primary' size='small' style={{marginLeft:'12px'}} onClick={() => props.setStatusChecksVisible(true)}>View Status Checks</Button>
|
<Button
|
||||||
</Typography>
|
variant="outlined"
|
||||||
<div style={{marginBottom: '20px' }}>
|
color="primary"
|
||||||
<span style={{marginRight:'20px'}}>Discovered Nodes: { props.nodeTopology.population }</span>
|
size="small"
|
||||||
<span style={{marginRight:'20px'}}>
|
style={{ marginLeft: '12px' }}
|
||||||
<span>Connected Peers: </span>
|
onClick={() => props.setStatusChecksVisible(true)}
|
||||||
<Link to='/peers/'>
|
>
|
||||||
{ props.nodeTopology.connected }
|
View Status Checks
|
||||||
</Link>
|
</Button>
|
||||||
</span>
|
</Typography>
|
||||||
</div>
|
<div style={{ marginBottom: '20px' }}>
|
||||||
|
<span style={{ marginRight: '20px' }}>Discovered Nodes: {props.nodeTopology.population}</span>
|
||||||
|
<span style={{ marginRight: '20px' }}>
|
||||||
|
<span>Connected Peers: </span>
|
||||||
|
<Link to="/peers/">{props.nodeTopology.connected}</Link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Typography component="div" variant="subtitle2" gutterBottom>
|
||||||
|
<span>AGENT: </span>
|
||||||
|
<a href="https://github.com/ethersphere/bee" rel="noreferrer" target="_blank">
|
||||||
|
Bee
|
||||||
|
</a>
|
||||||
|
<span>{props.nodeHealth?.version ? ` v${props.nodeHealth.version}` : '-'}</span>
|
||||||
|
{
|
||||||
|
// FIXME: this should be broken up
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
|
props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ? (
|
||||||
|
<Chip
|
||||||
|
style={{ marginLeft: '7px', color: '#2145a0' }}
|
||||||
|
size="small"
|
||||||
|
label="latest"
|
||||||
|
className={classes.status}
|
||||||
|
/>
|
||||||
|
) : props.loadingBeeRelease ? (
|
||||||
|
''
|
||||||
|
) : (
|
||||||
|
<Typography variant="button">update</Typography>
|
||||||
|
)
|
||||||
|
/* eslint-enable no-nested-ternary */
|
||||||
|
}
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div" variant="subtitle2" gutterBottom>
|
||||||
|
<span>PUBLIC KEY: </span>
|
||||||
|
<span>{props.nodeAddresses.public_key ? props.nodeAddresses.public_key : '-'}</span>
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div" variant="subtitle2" gutterBottom>
|
||||||
|
<span>PSS PUBLIC KEY: </span>
|
||||||
|
<span>{props.nodeAddresses.pss_public_key ? props.nodeAddresses.pss_public_key : '-'}</span>
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div" variant="subtitle2" gutterBottom>
|
||||||
|
<Typography component="div" style={{ marginTop: '20px' }}>
|
||||||
|
<span>OVERLAY ADDRESS (PEER ID): </span>
|
||||||
|
<span>{props.nodeAddresses.overlay ? props.nodeAddresses.overlay : '-'}</span>
|
||||||
|
</Typography>
|
||||||
|
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
|
||||||
|
<Button color="primary" style={{ padding: 0, marginTop: '6px' }}>
|
||||||
|
{underlayAddressesVisible ? (
|
||||||
|
<ArrowDropUp style={{ fontSize: '12px' }} />
|
||||||
|
) : (
|
||||||
|
<ArrowRight style={{ fontSize: '12px' }} />
|
||||||
|
)}
|
||||||
|
<span>Underlay Addresses</span>
|
||||||
|
</Button>
|
||||||
|
</Typography>
|
||||||
|
{underlayAddressesVisible ? (
|
||||||
<div>
|
<div>
|
||||||
<Typography component="div" variant="subtitle2" gutterBottom>
|
{props.nodeAddresses.underlay
|
||||||
<span>AGENT: </span>
|
? props.nodeAddresses.underlay.map(item => <li key={item}>{item}</li>)
|
||||||
<a href='https://github.com/ethersphere/bee' rel='noreferrer' target='_blank'>Bee</a>
|
: '-'}
|
||||||
<span>{props.nodeHealth?.version ? ` v${props.nodeHealth.version}` : '-'}</span>
|
|
||||||
{props.beeRelease && props.beeRelease.name === `v${props.nodeHealth?.version?.split('-')[0]}` ?
|
|
||||||
<Chip
|
|
||||||
style={{ marginLeft: '7px', color: '#2145a0' }}
|
|
||||||
size="small"
|
|
||||||
label='latest'
|
|
||||||
className={classes.status}
|
|
||||||
/>
|
|
||||||
:
|
|
||||||
props.loadingBeeRelease ?
|
|
||||||
''
|
|
||||||
:
|
|
||||||
<Typography variant="button">update</Typography>
|
|
||||||
}
|
|
||||||
</Typography>
|
|
||||||
<Typography component="div" variant="subtitle2" gutterBottom>
|
|
||||||
<span>PUBLIC KEY: </span>
|
|
||||||
<span>{ props.nodeAddresses.public_key ? props.nodeAddresses.public_key : '-' }</span>
|
|
||||||
</Typography>
|
|
||||||
<Typography component="div" variant="subtitle2" gutterBottom>
|
|
||||||
<span>PSS PUBLIC KEY: </span>
|
|
||||||
<span>{ props.nodeAddresses.pss_public_key ? props.nodeAddresses.pss_public_key : '-' }</span>
|
|
||||||
</Typography>
|
|
||||||
<Typography component="div" variant="subtitle2" gutterBottom>
|
|
||||||
<Typography component="div" style={{marginTop:'20px'}}>
|
|
||||||
<span>OVERLAY ADDRESS (PEER ID): </span>
|
|
||||||
<span>{ props.nodeAddresses.overlay ? props.nodeAddresses.overlay : '-' }</span>
|
|
||||||
</Typography>
|
|
||||||
<Typography component="div" onClick={() => setUnderlayAddresessVisible(!underlayAddressesVisible)}>
|
|
||||||
<Button color="primary" style={{padding: 0, marginTop:'6px'}}>
|
|
||||||
{ underlayAddressesVisible ?
|
|
||||||
<ArrowDropUp style={{fontSize:'12px'}} /> :
|
|
||||||
<ArrowRight style={{fontSize:'12px'}} />
|
|
||||||
}
|
|
||||||
<span>Underlay Addresses</span>
|
|
||||||
</Button>
|
|
||||||
</Typography>
|
|
||||||
{underlayAddressesVisible ?
|
|
||||||
<div>
|
|
||||||
|
|
||||||
{ props.nodeAddresses.underlay ?
|
|
||||||
props.nodeAddresses.underlay.map(item => (<li>{item}</li>))
|
|
||||||
: '-' }
|
|
||||||
</div>
|
|
||||||
: null}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
) : null}
|
||||||
</div>
|
</Typography>
|
||||||
:
|
</div>
|
||||||
<div style={{padding: '16px'}}>
|
</CardContent>
|
||||||
<Skeleton width={650} height={32} animation="wave" />
|
</div>
|
||||||
<Skeleton width={650} height={24} animation="wave" />
|
) : (
|
||||||
<Skeleton width={650} height={24} animation="wave" />
|
<div style={{ padding: '16px' }}>
|
||||||
</div>
|
<Skeleton width={650} height={32} animation="wave" />
|
||||||
}
|
<Skeleton width={650} height={24} animation="wave" />
|
||||||
</Card>
|
<Skeleton width={650} height={24} animation="wave" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StatusCard
|
export default StatusCard
|
||||||
|
|||||||
+127
-119
@@ -1,135 +1,143 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import { useState, useEffect, ReactElement } from 'react'
|
||||||
import axios from 'axios';
|
import axios from 'axios'
|
||||||
import { Container, CircularProgress } from '@material-ui/core';
|
import { Container, CircularProgress } from '@material-ui/core'
|
||||||
|
|
||||||
import NodeSetupWorkflow from './NodeSetupWorkflow';
|
import NodeSetupWorkflow from './NodeSetupWorkflow'
|
||||||
import StatusCard from './StatusCard';
|
import StatusCard from './StatusCard'
|
||||||
import EthereumAddressCard from '../../components/EthereumAddressCard';
|
import EthereumAddressCard from '../../components/EthereumAddressCard'
|
||||||
import { useApiHealth, useDebugApiHealth, useApiNodeAddresses, useApiChequebookAddress, useApiNodeTopology, useApiChequebookBalance } from '../../hooks/apiHooks';
|
import {
|
||||||
|
useApiHealth,
|
||||||
|
useDebugApiHealth,
|
||||||
|
useApiNodeAddresses,
|
||||||
|
useApiChequebookAddress,
|
||||||
|
useApiNodeTopology,
|
||||||
|
useApiChequebookBalance,
|
||||||
|
} from '../../hooks/apiHooks'
|
||||||
|
|
||||||
export default function Status() {
|
export default function Status(): ReactElement {
|
||||||
const [beeRelease, setBeeRelease] = useState({ name: ''});
|
const [beeRelease, setBeeRelease] = useState<LatestBeeRelease | null>(null)
|
||||||
const [isLoadingBeeRelease, setIsLoadingBeeRelease] = useState<boolean>(false);
|
const [isLoadingBeeRelease, setIsLoadingBeeRelease] = useState<boolean>(false)
|
||||||
|
|
||||||
const [apiHost, setApiHost] = useState('');
|
const [apiHost, setApiHost] = useState('')
|
||||||
const [debugApiHost, setDebugApiHost] = useState('');
|
const [debugApiHost, setDebugApiHost] = useState('')
|
||||||
|
|
||||||
const [statusChecksVisible, setStatusChecksVisible] = useState<boolean>(false);
|
const [statusChecksVisible, setStatusChecksVisible] = useState<boolean>(false)
|
||||||
|
|
||||||
const { health, isLoadingHealth } = useApiHealth()
|
const { health, isLoadingHealth } = useApiHealth()
|
||||||
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
|
const { nodeHealth, isLoadingNodeHealth } = useDebugApiHealth()
|
||||||
const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses()
|
const { nodeAddresses, isLoadingNodeAddresses } = useApiNodeAddresses()
|
||||||
const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress()
|
const { chequebookAddress, isLoadingChequebookAddress } = useApiChequebookAddress()
|
||||||
const { topology: nodeTopology, isLoading: isLoadingNodeTopology } = useApiNodeTopology()
|
const { topology: nodeTopology, isLoading: isLoadingNodeTopology } = useApiNodeTopology()
|
||||||
const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance()
|
const { chequebookBalance, isLoadingChequebookBalance } = useApiChequebookBalance()
|
||||||
|
|
||||||
|
const fetchLatestBeeRelease = () => {
|
||||||
|
setIsLoadingBeeRelease(true)
|
||||||
|
axios
|
||||||
|
.get(`${process.env.REACT_APP_BEE_GITHUB_REPO_URL}/releases/latest`)
|
||||||
|
.then(res => {
|
||||||
|
setBeeRelease(res.data)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// FIXME: should do something about the error
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoadingBeeRelease(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const fetchLatestBeeRelease = async () => {
|
const fetchApiHost = () => {
|
||||||
setIsLoadingBeeRelease(true)
|
let apiHost
|
||||||
axios.get(`${process.env.REACT_APP_BEE_GITHUB_REPO_URL}/releases/latest`)
|
|
||||||
.then(res => {
|
if (sessionStorage.getItem('api_host')) {
|
||||||
setBeeRelease(res.data)
|
apiHost = String(sessionStorage.getItem('api_host') || '')
|
||||||
})
|
} else {
|
||||||
.catch(error => {
|
apiHost = String(process.env.REACT_APP_BEE_HOST)
|
||||||
console.log(error)
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoadingBeeRelease(false)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
setApiHost(apiHost)
|
||||||
|
}
|
||||||
|
|
||||||
const fetchApiHost = () => {
|
const fetchDebugApiHost = () => {
|
||||||
let apiHost
|
let debugApiHost
|
||||||
|
|
||||||
if (sessionStorage.getItem('api_host')) {
|
if (sessionStorage.getItem('debug_api_host')) {
|
||||||
apiHost = String(sessionStorage.getItem('api_host') || '')
|
debugApiHost = String(sessionStorage.getItem('debug_api_host') || '')
|
||||||
} else {
|
} else {
|
||||||
apiHost = String(process.env.REACT_APP_BEE_HOST)
|
debugApiHost = String(process.env.REACT_APP_BEE_DEBUG_HOST)
|
||||||
}
|
|
||||||
setApiHost(apiHost)
|
|
||||||
}
|
}
|
||||||
|
setDebugApiHost(debugApiHost)
|
||||||
|
}
|
||||||
|
|
||||||
const fetchDebugApiHost = () => {
|
useEffect(() => {
|
||||||
let debugApiHost
|
fetchApiHost()
|
||||||
|
fetchDebugApiHost()
|
||||||
|
fetchLatestBeeRelease()
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (sessionStorage.getItem('debug_api_host')) {
|
// FIXME: this should be broken up
|
||||||
debugApiHost = String(sessionStorage.getItem('debug_api_host') || '')
|
/* eslint-disable no-nested-ternary */
|
||||||
} else {
|
return (
|
||||||
debugApiHost = String(process.env.REACT_APP_BEE_DEBUG_HOST)
|
<div>
|
||||||
}
|
{nodeHealth?.status === 'ok' &&
|
||||||
setDebugApiHost(debugApiHost)
|
health &&
|
||||||
}
|
beeRelease &&
|
||||||
|
beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}` &&
|
||||||
useEffect(() => {
|
nodeAddresses?.ethereum &&
|
||||||
fetchApiHost()
|
chequebookAddress?.chequebookaddress &&
|
||||||
fetchDebugApiHost()
|
chequebookBalance &&
|
||||||
fetchLatestBeeRelease()
|
chequebookBalance?.totalBalance > 0 &&
|
||||||
}, []);
|
nodeTopology?.connected &&
|
||||||
|
nodeTopology?.connected > 0 &&
|
||||||
return (
|
!statusChecksVisible ? (
|
||||||
<div>
|
<div>
|
||||||
{nodeHealth?.status === 'ok' &&
|
<StatusCard
|
||||||
health &&
|
nodeHealth={nodeHealth}
|
||||||
beeRelease &&
|
loadingNodeHealth={isLoadingNodeHealth}
|
||||||
beeRelease.name === `v${nodeHealth?.version?.split('-')[0]}` &&
|
beeRelease={beeRelease}
|
||||||
nodeAddresses?.ethereum &&
|
loadingBeeRelease={isLoadingBeeRelease}
|
||||||
chequebookAddress?.chequebookaddress && chequebookBalance && chequebookBalance?.totalBalance > 0 &&
|
nodeAddresses={nodeAddresses}
|
||||||
nodeTopology?.connected && nodeTopology?.connected > 0 &&
|
loadingNodeTopology={isLoadingNodeTopology}
|
||||||
!statusChecksVisible ?
|
nodeTopology={nodeTopology}
|
||||||
<div>
|
setStatusChecksVisible={setStatusChecksVisible}
|
||||||
<StatusCard
|
/>
|
||||||
nodeHealth={nodeHealth}
|
<EthereumAddressCard
|
||||||
loadingNodeHealth={isLoadingNodeHealth}
|
nodeAddresses={nodeAddresses}
|
||||||
beeRelease={beeRelease}
|
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
||||||
loadingBeeRelease={isLoadingBeeRelease}
|
chequebookAddress={chequebookAddress}
|
||||||
nodeAddresses={nodeAddresses}
|
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||||
loadingNodeTopology={isLoadingNodeTopology}
|
/>
|
||||||
nodeTopology={nodeTopology}
|
|
||||||
setStatusChecksVisible={setStatusChecksVisible}
|
|
||||||
/>
|
|
||||||
<EthereumAddressCard
|
|
||||||
nodeAddresses={nodeAddresses}
|
|
||||||
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
|
||||||
chequebookAddress={chequebookAddress}
|
|
||||||
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
( isLoadingNodeHealth || isLoadingHealth || isLoadingChequebookAddress ||
|
|
||||||
isLoadingNodeTopology || isLoadingBeeRelease || isLoadingNodeAddresses || isLoadingBeeRelease || isLoadingChequebookBalance
|
|
||||||
)
|
|
||||||
?
|
|
||||||
<Container style={{textAlign:'center', padding:'50px'}}>
|
|
||||||
<CircularProgress />
|
|
||||||
</Container>
|
|
||||||
:
|
|
||||||
<NodeSetupWorkflow
|
|
||||||
beeRelease={beeRelease}
|
|
||||||
isLoadingBeeRelease={isLoadingBeeRelease}
|
|
||||||
|
|
||||||
nodeHealth={nodeHealth}
|
|
||||||
isLoadingNodeHealth={isLoadingNodeHealth}
|
|
||||||
|
|
||||||
nodeAddresses={nodeAddresses}
|
|
||||||
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
|
||||||
|
|
||||||
nodeTopology={nodeTopology}
|
|
||||||
isLoadingNodeTopology={isLoadingNodeTopology}
|
|
||||||
|
|
||||||
nodeApiHealth={health}
|
|
||||||
isLoadingHealth={isLoadingHealth}
|
|
||||||
|
|
||||||
chequebookAddress={chequebookAddress}
|
|
||||||
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
|
||||||
|
|
||||||
chequebookBalance={chequebookBalance}
|
|
||||||
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
|
||||||
|
|
||||||
apiHost={apiHost}
|
|
||||||
debugApiHost={debugApiHost}
|
|
||||||
setStatusChecksVisible={setStatusChecksVisible}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
) : isLoadingNodeHealth ||
|
||||||
|
isLoadingHealth ||
|
||||||
|
isLoadingChequebookAddress ||
|
||||||
|
isLoadingNodeTopology ||
|
||||||
|
isLoadingBeeRelease ||
|
||||||
|
isLoadingNodeAddresses ||
|
||||||
|
isLoadingBeeRelease ||
|
||||||
|
isLoadingChequebookBalance ? (
|
||||||
|
<Container style={{ textAlign: 'center', padding: '50px' }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Container>
|
||||||
|
) : (
|
||||||
|
<NodeSetupWorkflow
|
||||||
|
beeRelease={beeRelease}
|
||||||
|
isLoadingBeeRelease={isLoadingBeeRelease}
|
||||||
|
nodeHealth={nodeHealth}
|
||||||
|
isLoadingNodeHealth={isLoadingNodeHealth}
|
||||||
|
nodeAddresses={nodeAddresses}
|
||||||
|
isLoadingNodeAddresses={isLoadingNodeAddresses}
|
||||||
|
nodeTopology={nodeTopology}
|
||||||
|
isLoadingNodeTopology={isLoadingNodeTopology}
|
||||||
|
nodeApiHealth={health}
|
||||||
|
isLoadingHealth={isLoadingHealth}
|
||||||
|
chequebookAddress={chequebookAddress}
|
||||||
|
isLoadingChequebookAddress={isLoadingChequebookAddress}
|
||||||
|
chequebookBalance={chequebookBalance}
|
||||||
|
isLoadingChequebookBalance={isLoadingChequebookBalance}
|
||||||
|
apiHost={apiHost}
|
||||||
|
debugApiHost={debugApiHost}
|
||||||
|
setStatusChecksVisible={setStatusChecksVisible}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+5
@@ -1 +1,6 @@
|
|||||||
/// <reference types="react-scripts" />
|
/// <reference types="react-scripts" />
|
||||||
|
|
||||||
|
interface LatestBeeRelease {
|
||||||
|
name: string
|
||||||
|
html_url: string
|
||||||
|
}
|
||||||
|
|||||||
+10
-10
@@ -1,15 +1,15 @@
|
|||||||
import { ReportHandler } from 'web-vitals';
|
import { ReportHandler } from 'web-vitals'
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
const reportWebVitals = (onPerfEntry?: ReportHandler): void => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry)
|
||||||
getFID(onPerfEntry);
|
getFID(onPerfEntry)
|
||||||
getFCP(onPerfEntry);
|
getFCP(onPerfEntry)
|
||||||
getLCP(onPerfEntry);
|
getLCP(onPerfEntry)
|
||||||
getTTFB(onPerfEntry);
|
getTTFB(onPerfEntry)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default reportWebVitals;
|
export default reportWebVitals
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Route } from 'react-router-dom';
|
|
||||||
|
|
||||||
|
|
||||||
const AppRoute = ( {component: Component, layout: Layout, ...rest }) => (
|
|
||||||
<Route {...rest} render={props => (
|
|
||||||
<Layout {...props}>
|
|
||||||
<Component {...props} />
|
|
||||||
</Layout>
|
|
||||||
)} />
|
|
||||||
)
|
|
||||||
|
|
||||||
export default AppRoute;
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import type { JSXElementConstructor, ReactElement } from 'react'
|
||||||
|
import { Route, RouteComponentProps } from 'react-router-dom'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
component: JSXElementConstructor<RouteComponentProps>
|
||||||
|
layout: JSXElementConstructor<RouteComponentProps>
|
||||||
|
exact?: boolean
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppRoute = ({ component: Component, layout: Layout, exact, path }: Props): ReactElement => (
|
||||||
|
<Route
|
||||||
|
exact={exact}
|
||||||
|
path={path}
|
||||||
|
render={props => (
|
||||||
|
<Layout {...props}>
|
||||||
|
<Component {...props} />
|
||||||
|
</Layout>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default AppRoute
|
||||||
+19
-20
@@ -1,27 +1,26 @@
|
|||||||
import React from 'react';
|
import type { ReactElement } from 'react'
|
||||||
import { Switch } from 'react-router-dom';
|
import { Switch } from 'react-router-dom'
|
||||||
|
|
||||||
import AppRoute from './AppRoute';
|
import AppRoute from './AppRoute'
|
||||||
|
|
||||||
// layouts
|
// layouts
|
||||||
import Dashboard from '../layout/Dashboard';
|
import Dashboard from '../layout/Dashboard'
|
||||||
|
|
||||||
// pages
|
// pages
|
||||||
import Status from '../pages/status/index';
|
import Status from '../pages/status/index'
|
||||||
import Files from '../pages/files/index';
|
import Files from '../pages/files/index'
|
||||||
import Peers from '../pages/peers/index';
|
import Peers from '../pages/peers/index'
|
||||||
import Accounting from '../pages/accounting/index';
|
import Accounting from '../pages/accounting/index'
|
||||||
import Settings from '../pages/settings/index';
|
import Settings from '../pages/settings/index'
|
||||||
|
|
||||||
|
const BaseRouter = (): ReactElement => (
|
||||||
|
<Switch>
|
||||||
|
<AppRoute exact path="/" layout={Dashboard} component={Status} />
|
||||||
|
<AppRoute exact path="/files/" layout={Dashboard} component={Files} />
|
||||||
|
<AppRoute exact path="/peers/" layout={Dashboard} component={Peers} />
|
||||||
|
<AppRoute exact path="/accounting/" layout={Dashboard} component={Accounting} />
|
||||||
|
<AppRoute exact path="/settings/" layout={Dashboard} component={Settings} />
|
||||||
|
</Switch>
|
||||||
|
)
|
||||||
|
|
||||||
const BaseRouter = (props: any) => (
|
export default BaseRouter
|
||||||
<Switch>
|
|
||||||
<AppRoute exact path='/' layout={ Dashboard } component={Status}/>
|
|
||||||
<AppRoute exact path='/files/' layout={ Dashboard } component={Files}/>
|
|
||||||
<AppRoute exact path='/peers/' layout={ Dashboard } component={Peers}/>
|
|
||||||
<AppRoute exact path='/accounting/' layout={ Dashboard } component={Accounting}/>
|
|
||||||
<AppRoute exact path='/settings/' layout={ Dashboard } component={Settings}/>
|
|
||||||
</Switch>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default BaseRouter;
|
|
||||||
|
|||||||
+94
-73
@@ -1,95 +1,116 @@
|
|||||||
import { Bee, BeeDebug, Reference } from "@ethersphere/bee-js";
|
import {
|
||||||
|
AllSettlements,
|
||||||
|
BalanceResponse,
|
||||||
|
Bee,
|
||||||
|
BeeDebug,
|
||||||
|
CashoutResponse,
|
||||||
|
ChequebookAddressResponse,
|
||||||
|
ChequebookBalanceResponse,
|
||||||
|
Data,
|
||||||
|
DepositTokensResponse,
|
||||||
|
FileData,
|
||||||
|
Health,
|
||||||
|
LastCashoutActionResponse,
|
||||||
|
LastChequesForPeerResponse,
|
||||||
|
LastChequesResponse,
|
||||||
|
NodeAddresses,
|
||||||
|
Peer,
|
||||||
|
PingResponse,
|
||||||
|
Reference,
|
||||||
|
Topology,
|
||||||
|
WithdrawTokensResponse,
|
||||||
|
} from '@ethersphere/bee-js'
|
||||||
|
|
||||||
const beeJSClient = () => {
|
const beeJSClient = () => {
|
||||||
let apiHost = process.env.REACT_APP_BEE_HOST || 'http://localhost:1633'
|
let apiHost = process.env.REACT_APP_BEE_HOST || 'http://localhost:1633'
|
||||||
|
|
||||||
if (sessionStorage.getItem('api_host')) {
|
if (sessionStorage.getItem('api_host')) {
|
||||||
apiHost = String(sessionStorage.getItem('api_host'))
|
apiHost = String(sessionStorage.getItem('api_host'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Bee(apiHost)
|
return new Bee(apiHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
const beeJSDebugClient = () => {
|
const beeJSDebugClient = () => {
|
||||||
let debugApiHost = process.env.REACT_APP_BEE_DEBUG_HOST || 'http://localhost:1635'
|
let debugApiHost = process.env.REACT_APP_BEE_DEBUG_HOST || 'http://localhost:1635'
|
||||||
|
|
||||||
if (sessionStorage.getItem('debug_api_host')) {
|
if (sessionStorage.getItem('debug_api_host')) {
|
||||||
debugApiHost = String(sessionStorage.getItem('debug_api_host'))
|
debugApiHost = String(sessionStorage.getItem('debug_api_host'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BeeDebug(debugApiHost)
|
return new BeeDebug(debugApiHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const beeApi = {
|
export const beeApi = {
|
||||||
status: {
|
status: {
|
||||||
health() {
|
health(): Promise<boolean> {
|
||||||
return beeJSClient().isConnected()
|
return beeJSClient().isConnected()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
files: {
|
},
|
||||||
uploadFile(file: File) {
|
files: {
|
||||||
return beeJSClient().uploadFile(file)
|
uploadFile(file: File): Promise<Reference> {
|
||||||
},
|
return beeJSClient().uploadFile(file)
|
||||||
downloadFile(hash: string | Reference) {
|
|
||||||
return beeJSClient().downloadFile(hash)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
downloadFile(hash: string | Reference): Promise<FileData<Data>> {
|
||||||
|
return beeJSClient().downloadFile(hash)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const beeDebugApi = {
|
export const beeDebugApi = {
|
||||||
status: {
|
status: {
|
||||||
nodeHealth() {
|
nodeHealth(): Promise<Health> {
|
||||||
return beeJSDebugClient().getHealth()
|
return beeJSDebugClient().getHealth()
|
||||||
},
|
|
||||||
},
|
},
|
||||||
connectivity: {
|
},
|
||||||
addresses() {
|
connectivity: {
|
||||||
return beeJSDebugClient().getNodeAddresses()
|
addresses(): Promise<NodeAddresses> {
|
||||||
},
|
return beeJSDebugClient().getNodeAddresses()
|
||||||
listPeers() {
|
|
||||||
return beeJSDebugClient().getPeers()
|
|
||||||
},
|
|
||||||
topology() {
|
|
||||||
return beeJSDebugClient().getTopology()
|
|
||||||
},
|
|
||||||
ping(peerId: string) {
|
|
||||||
return beeJSDebugClient().pingPeer(peerId)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
balance: {
|
listPeers(): Promise<Peer[]> {
|
||||||
balances() {
|
return beeJSDebugClient().getPeers()
|
||||||
return beeJSDebugClient().getAllBalances()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
chequebook: {
|
topology(): Promise<Topology> {
|
||||||
address() {
|
return beeJSDebugClient().getTopology()
|
||||||
return beeJSDebugClient().getChequebookAddress()
|
|
||||||
},
|
|
||||||
balance() {
|
|
||||||
return beeJSDebugClient().getChequebookBalance()
|
|
||||||
},
|
|
||||||
getLastCheques() {
|
|
||||||
return beeJSDebugClient().getLastCheques()
|
|
||||||
},
|
|
||||||
peerCashout(peerId: string) {
|
|
||||||
return beeJSDebugClient().cashoutLastCheque(peerId)
|
|
||||||
},
|
|
||||||
getPeerLastCashout(peerId: string) {
|
|
||||||
return beeJSDebugClient().getLastCashoutAction(peerId)
|
|
||||||
},
|
|
||||||
getPeerLastCheques(peerId: string) {
|
|
||||||
return beeJSDebugClient().getLastChequesForPeer(peerId)
|
|
||||||
},
|
|
||||||
withdraw(amount: bigint) {
|
|
||||||
return beeJSDebugClient().withdrawTokens(amount)
|
|
||||||
},
|
|
||||||
deposit(amount: bigint) {
|
|
||||||
return beeJSDebugClient().depositTokens(amount)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
settlements: {
|
ping(peerId: string): Promise<PingResponse> {
|
||||||
getSettlements() {
|
return beeJSDebugClient().pingPeer(peerId)
|
||||||
return beeJSDebugClient().getAllSettlements()
|
},
|
||||||
}
|
},
|
||||||
}
|
balance: {
|
||||||
|
balances(): Promise<BalanceResponse> {
|
||||||
|
return beeJSDebugClient().getAllBalances()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chequebook: {
|
||||||
|
address(): Promise<ChequebookAddressResponse> {
|
||||||
|
return beeJSDebugClient().getChequebookAddress()
|
||||||
|
},
|
||||||
|
balance(): Promise<ChequebookBalanceResponse> {
|
||||||
|
return beeJSDebugClient().getChequebookBalance()
|
||||||
|
},
|
||||||
|
getLastCheques(): Promise<LastChequesResponse> {
|
||||||
|
return beeJSDebugClient().getLastCheques()
|
||||||
|
},
|
||||||
|
peerCashout(peerId: string): Promise<CashoutResponse> {
|
||||||
|
return beeJSDebugClient().cashoutLastCheque(peerId)
|
||||||
|
},
|
||||||
|
getPeerLastCashout(peerId: string): Promise<LastCashoutActionResponse> {
|
||||||
|
return beeJSDebugClient().getLastCashoutAction(peerId)
|
||||||
|
},
|
||||||
|
getPeerLastCheques(peerId: string): Promise<LastChequesForPeerResponse> {
|
||||||
|
return beeJSDebugClient().getLastChequesForPeer(peerId)
|
||||||
|
},
|
||||||
|
withdraw(amount: bigint): Promise<WithdrawTokensResponse> {
|
||||||
|
return beeJSDebugClient().withdrawTokens(amount)
|
||||||
|
},
|
||||||
|
deposit(amount: bigint): Promise<DepositTokensResponse> {
|
||||||
|
return beeJSDebugClient().depositTokens(amount)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settlements: {
|
||||||
|
getSettlements(): Promise<AllSettlements> {
|
||||||
|
return beeJSDebugClient().getAllSettlements()
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
+1
-1
@@ -2,4 +2,4 @@
|
|||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom'
|
||||||
|
|||||||
+32
-48
@@ -1,4 +1,4 @@
|
|||||||
import { createMuiTheme } from '@material-ui/core/styles';
|
import { createMuiTheme } from '@material-ui/core/styles'
|
||||||
|
|
||||||
declare module '@material-ui/core/styles/createPalette' {
|
declare module '@material-ui/core/styles/createPalette' {
|
||||||
interface TypeBackground {
|
interface TypeBackground {
|
||||||
@@ -7,54 +7,38 @@ declare module '@material-ui/core/styles/createPalette' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const lightTheme = createMuiTheme({
|
export const lightTheme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
type: "light",
|
type: 'light',
|
||||||
background: {
|
background: {
|
||||||
default: '#fafafa',
|
default: '#fafafa',
|
||||||
},
|
|
||||||
primary: {
|
|
||||||
main: '#6a6a6a',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: '#333333',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
typography: {
|
primary: {
|
||||||
fontFamily: [
|
main: '#6a6a6a',
|
||||||
'Work Sans',
|
},
|
||||||
'Montserrat',
|
secondary: {
|
||||||
'Nunito',
|
main: '#333333',
|
||||||
'Roboto',
|
},
|
||||||
'"Helvetica Neue"',
|
},
|
||||||
'Arial',
|
typography: {
|
||||||
'sans-serif'
|
fontFamily: ['Work Sans', 'Montserrat', 'Nunito', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif'].join(','),
|
||||||
].join(','),
|
},
|
||||||
}
|
})
|
||||||
});
|
|
||||||
|
|
||||||
export const darkTheme = createMuiTheme({
|
export const darkTheme = createMuiTheme({
|
||||||
palette: {
|
palette: {
|
||||||
type: "dark",
|
type: 'dark',
|
||||||
background: {
|
background: {
|
||||||
default: '#0d1117',
|
default: '#0d1117',
|
||||||
paper: '#161b22',
|
paper: '#161b22',
|
||||||
},
|
|
||||||
primary: {
|
|
||||||
main: '#dd7700',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: '#1f2937',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
typography: {
|
primary: {
|
||||||
fontFamily: [
|
main: '#dd7700',
|
||||||
'Work Sans',
|
},
|
||||||
'Montserrat',
|
secondary: {
|
||||||
'Nunito',
|
main: '#1f2937',
|
||||||
'Roboto',
|
},
|
||||||
'"Helvetica Neue"',
|
},
|
||||||
'Arial',
|
typography: {
|
||||||
'sans-serif'
|
fontFamily: ['Work Sans', 'Montserrat', 'Nunito', 'Roboto', '"Helvetica Neue"', 'Arial', 'sans-serif'].join(','),
|
||||||
].join(','),
|
},
|
||||||
}
|
})
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
export const ConvertBalanceToBZZ = (amount: number): number => {
|
||||||
export const ConvertBalanceToBZZ = (amount: number) => {
|
return amount / 10 ** 16
|
||||||
return amount / (10 ** 16)
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user