Echarts implementation (#536)

* feat: implement the payments graph with echarts

* fix: improve echarts import in next.config.js

* fix: formatting

* fix: no-implicity-any

* fix: tidy up inconsistencies in echarts implementation

* feat: improve a number of aspects about echarts implementation as per suggestions

* feat: move forwards chart to echarts

* chore: move invoice, payment, forward graphs to new BarChart. move liquidityGraph to new HorizontalBarChart

* chore: remove V2 from new graphs

* feat: implement Sankey chart for forwards

* chore: change justification on Sankey Graph

* feat: add Sankey graph

* chore: remove chord graph

* chore: remove chord graph and update package.json

* chore: remove stray console log

* chore: format numbers on y axis to be integer values

* feat: tidy echarts implementation

* feat: revamp tooltip

* feat: bound the size of the sankey chart

* feat: provide function that creates a mock sankey dataset of arbitrary size

* fix: missing memo dependencies and css

* chore: small changes

---------

Co-authored-by: Anthony Potdevin <potdevin.anthony@gmail.com>
Co-authored-by: Anthony Potdevin <31413433+apotdevin@users.noreply.github.com>
Co-authored-by: secondl1ght <secondl1ght@protonmail.com>
This commit is contained in:
AmbossKeegan 2023-08-18 13:15:29 -03:00 committed by GitHub
parent e62f5bf6ea
commit 0f893bddad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 716 additions and 1402 deletions

768
package-lock.json generated
View File

@ -24,14 +24,6 @@
"@nestjs/throttler": "^4.0.0",
"@nestjs/websockets": "^9.4.2",
"@tanstack/react-table": "^8.9.1",
"@visx/axis": "^2.12.2",
"@visx/chord": "^2.10.0",
"@visx/event": "^2.6.0",
"@visx/group": "^2.10.0",
"@visx/responsive": "^2.10.0",
"@visx/scale": "^2.2.2",
"@visx/shape": "^2.12.2",
"@visx/tooltip": "^2.16.0",
"apollo-server-express": "^3.12.0",
"balanceofsatoshis": "^15.8.2",
"bcryptjs": "^2.4.3",
@ -48,6 +40,8 @@
"d3-time-format": "^4.1.0",
"date-fns": "^2.30.0",
"dotenv": "^16.0.3",
"echarts": "^5.4.2",
"echarts-for-react": "^3.0.2",
"ecpair": "^2.0.1",
"graphql": "^16.6.0",
"jest-fetch-mock": "^3.0.3",
@ -5224,11 +5218,6 @@
"lodash.get": "^4.4.2"
}
},
"node_modules/@juggle/resize-observer": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
},
"node_modules/@lukeed/csprng": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
@ -6552,50 +6541,6 @@
"integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==",
"dev": true
},
"node_modules/@types/d3-chord": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.11.tgz",
"integrity": "sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg=="
},
"node_modules/@types/d3-color": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
"integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA=="
},
"node_modules/@types/d3-interpolate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz",
"integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==",
"dependencies": {
"@types/d3-color": "^1"
}
},
"node_modules/@types/d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
},
"node_modules/@types/d3-scale": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
"integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
"dependencies": {
"@types/d3-time": "^2"
}
},
"node_modules/@types/d3-shape": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
"integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
"dependencies": {
"@types/d3-path": "^1"
}
},
"node_modules/@types/d3-time": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
"integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
},
"node_modules/@types/d3-time-format": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
@ -6755,7 +6700,8 @@
"node_modules/@types/lodash": {
"version": "4.14.195",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg=="
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==",
"dev": true
},
"node_modules/@types/long": {
"version": "4.0.2",
@ -6874,14 +6820,6 @@
"@types/react": "*"
}
},
"node_modules/@types/react-dom": {
"version": "18.2.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz",
"integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-grid-layout": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.3.2.tgz",
@ -7244,171 +7182,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@visx/axis": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@visx/axis/-/axis-2.14.0.tgz",
"integrity": "sha512-/mJuPiAUN/YdatSlXkkXD/8Pb4gVclbucNZkpUk6JVhnrmow0a1x73QiMXKuH2qDO5GDaqQS0C3Ly9lI3qTQ5Q==",
"dependencies": {
"@types/react": "*",
"@visx/group": "2.10.0",
"@visx/point": "2.6.0",
"@visx/scale": "2.2.2",
"@visx/shape": "2.12.2",
"@visx/text": "2.12.2",
"classnames": "^2.3.1",
"prop-types": "^15.6.0"
},
"peerDependencies": {
"react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/bounds": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.16.0.tgz",
"integrity": "sha512-KJoSSJt5SOLzprEbmsmdxR46PLudau0kWOgxavrah9D/49RsHwGdUdxHLKdXz4OdCruEfgGo4yxiuYLYmUWl4A==",
"dependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"prop-types": "^15.5.10"
},
"peerDependencies": {
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0",
"react-dom": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/chord": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/chord/-/chord-2.10.0.tgz",
"integrity": "sha512-p2ZQ9uiyiXV4Yu9DL+cqKbZbh1umMgCUWTP2CpgwSN5p822QHQjzpe1nafWOmNSLpANIBDDYIbtIgOZXUHkelA==",
"dependencies": {
"@types/d3-chord": "^1.0.9",
"@types/react": "*",
"classnames": "^2.3.1",
"d3-chord": "^1.0.4",
"prop-types": "^15.6.1"
},
"peerDependencies": {
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/curve": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.1.0.tgz",
"integrity": "sha512-9b6JOnx91gmOQiSPhUOxdsvcnW88fgqfTPKoVgQxidMsD/I3wksixtwo8TR/vtEz2aHzzsEEhlv1qK7Y3yaSDw==",
"dependencies": {
"@types/d3-shape": "^1.3.1",
"d3-shape": "^1.0.6"
}
},
"node_modules/@visx/event": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz",
"integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==",
"dependencies": {
"@types/react": "*",
"@visx/point": "2.6.0"
}
},
"node_modules/@visx/group": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/group/-/group-2.10.0.tgz",
"integrity": "sha512-DNJDX71f65Et1+UgQvYlZbE66owYUAfcxTkC96Db6TnxV221VKI3T5l23UWbnMzwFBP9dR3PWUjjqhhF12N5pA==",
"dependencies": {
"@types/react": "*",
"classnames": "^2.3.1",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/point": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@visx/point/-/point-2.6.0.tgz",
"integrity": "sha512-amBi7yMz4S2VSchlPdliznN41TuES64506ySI22DeKQ+mc1s1+BudlpnY90sM1EIw4xnqbKmrghTTGfy6SVqvQ=="
},
"node_modules/@visx/responsive": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.10.0.tgz",
"integrity": "sha512-NssDPpuUYp7hqVISuYkKZ5zk6ob0++RdTIaUjRcUdyFEbvzb9+zIb8QToOkvI90L2EC/MY4Jx0NpDbEe79GpAw==",
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
"@types/lodash": "^4.14.172",
"@types/react": "*",
"lodash": "^4.17.21",
"prop-types": "^15.6.1"
},
"peerDependencies": {
"react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/scale": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.2.2.tgz",
"integrity": "sha512-3aDySGUTpe6VykDQmF+g2nz5paFu9iSPTcCOEgkcru0/v5tmGzUdvivy8CkYbr87HN73V/Jc53lGm+kJUQcLBw==",
"dependencies": {
"@types/d3-interpolate": "^1.3.1",
"@types/d3-scale": "^3.3.0",
"@types/d3-time": "^2.0.0",
"d3-interpolate": "^1.4.0",
"d3-scale": "^3.3.0",
"d3-time": "^2.1.1"
}
},
"node_modules/@visx/shape": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.12.2.tgz",
"integrity": "sha512-4gN0fyHWYXiJ+Ck8VAazXX0i8TOnLJvOc5jZBnaJDVxgnSIfCjJn0+Nsy96l9Dy/bCMTh4DBYUBv9k+YICBUOA==",
"dependencies": {
"@types/d3-path": "^1.0.8",
"@types/d3-shape": "^1.3.1",
"@types/lodash": "^4.14.172",
"@types/react": "*",
"@visx/curve": "2.1.0",
"@visx/group": "2.10.0",
"@visx/scale": "2.2.2",
"classnames": "^2.3.1",
"d3-path": "^1.0.5",
"d3-shape": "^1.2.0",
"lodash": "^4.17.21",
"prop-types": "^15.5.10"
},
"peerDependencies": {
"react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/text": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/@visx/text/-/text-2.12.2.tgz",
"integrity": "sha512-Sv9YEolggfv2Nf6+l28ESG3VXVR1+s4u/Cz17QpgOxygcbOM8LfLtriWtBsBMKdMbYKeUpoUro0clx55TUwzew==",
"dependencies": {
"@types/lodash": "^4.14.172",
"@types/react": "*",
"classnames": "^2.3.1",
"lodash": "^4.17.21",
"prop-types": "^15.7.2",
"reduce-css-calc": "^1.3.0"
},
"peerDependencies": {
"react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@visx/tooltip": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.16.0.tgz",
"integrity": "sha512-kCjVtNGYpYm6AuMc1fT9cIwqpBivfQ8A5Qd7fQMC2xm4YVmehFdbyRr0nVft8zNXyUW3lDxbcq5bPIMRYhuhlA==",
"dependencies": {
"@types/react": "*",
"@visx/bounds": "2.16.0",
"classnames": "^2.3.1",
"prop-types": "^15.5.10",
"react-use-measure": "^2.0.4"
},
"peerDependencies": {
"react": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0",
"react-dom": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0"
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -10255,84 +10028,6 @@
"node": ">=12"
}
},
"node_modules/d3-chord": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
"integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
"dependencies": {
"d3-array": "1",
"d3-path": "1"
}
},
"node_modules/d3-chord/node_modules/d3-array": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
},
"node_modules/d3-color": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
},
"node_modules/d3-format": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
"integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
},
"node_modules/d3-interpolate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
"dependencies": {
"d3-color": "1"
}
},
"node_modules/d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
},
"node_modules/d3-scale": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
"integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
"dependencies": {
"d3-array": "^2.3.0",
"d3-format": "1 - 2",
"d3-interpolate": "1.2.0 - 2",
"d3-time": "^2.1.1",
"d3-time-format": "2 - 3"
}
},
"node_modules/d3-scale/node_modules/d3-array": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
"integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
"dependencies": {
"internmap": "^1.0.0"
}
},
"node_modules/d3-scale/node_modules/d3-time-format": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
"integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
"dependencies": {
"d3-time": "1 - 2"
}
},
"node_modules/d3-scale/node_modules/internmap": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
},
"node_modules/d3-shape": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
"dependencies": {
"d3-path": "1"
}
},
"node_modules/d3-time": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
@ -10421,7 +10116,8 @@
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
"dev": true
},
"node_modules/debug": {
"version": "4.3.4",
@ -10790,6 +10486,33 @@
"safe-buffer": "^5.0.1"
}
},
"node_modules/echarts": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.2.tgz",
"integrity": "sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==",
"dependencies": {
"tslib": "2.3.0",
"zrender": "5.4.3"
}
},
"node_modules/echarts-for-react": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz",
"integrity": "sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"size-sensor": "^1.0.1"
},
"peerDependencies": {
"echarts": "^3.0.0 || ^4.0.0 || ^5.0.0",
"react": "^15.0.0 || >=16.0.0"
}
},
"node_modules/echarts/node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
},
"node_modules/ecpair": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ecpair/-/ecpair-2.1.0.tgz",
@ -16852,11 +16575,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/math-expression-evaluator": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz",
"integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw=="
},
"node_modules/md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
@ -19215,18 +18933,6 @@
"react-dom": ">=16.6.0"
}
},
"node_modules/react-use-measure": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
"dependencies": {
"debounce": "^1.2.1"
},
"peerDependencies": {
"react": ">=16.13",
"react-dom": ">=16.13"
}
},
"node_modules/read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@ -19360,29 +19066,6 @@
"node": ">=8"
}
},
"node_modules/reduce-css-calc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
"integrity": "sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==",
"dependencies": {
"balanced-match": "^0.4.2",
"math-expression-evaluator": "^1.2.14",
"reduce-function-call": "^1.0.1"
}
},
"node_modules/reduce-css-calc/node_modules/balanced-match": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
"integrity": "sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg=="
},
"node_modules/reduce-function-call": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz",
"integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
@ -20001,6 +19684,11 @@
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
"node_modules/size-sensor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.1.tgz",
"integrity": "sha512-QTy7MnuugCFXIedXRpUSk9gUnyNiaxIdxGfUjr8xxXOqIB3QvBUYP9+b51oCg2C4dnhaeNk/h57TxjbvoJrJUA=="
},
"node_modules/sjcl": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz",
@ -22612,6 +22300,19 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zrender": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.3.tgz",
"integrity": "sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==",
"dependencies": {
"tslib": "2.3.0"
}
},
"node_modules/zrender/node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
},
"dependencies": {
@ -26464,11 +26165,6 @@
"lodash.get": "^4.4.2"
}
},
"@juggle/resize-observer": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.3.1.tgz",
"integrity": "sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw=="
},
"@lukeed/csprng": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
@ -27417,50 +27113,6 @@
"integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==",
"dev": true
},
"@types/d3-chord": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.11.tgz",
"integrity": "sha512-0DdfJ//bxyW3G9Nefwq/LDgazSKNN8NU0lBT3Cza6uVuInC2awMNsAcv1oKyRFLn9z7kXClH5XjwpveZjuz2eg=="
},
"@types/d3-color": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.2.tgz",
"integrity": "sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA=="
},
"@types/d3-interpolate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz",
"integrity": "sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg==",
"requires": {
"@types/d3-color": "^1"
}
},
"@types/d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
},
"@types/d3-scale": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
"integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
"requires": {
"@types/d3-time": "^2"
}
},
"@types/d3-shape": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.8.tgz",
"integrity": "sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==",
"requires": {
"@types/d3-path": "^1"
}
},
"@types/d3-time": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
"integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
},
"@types/d3-time-format": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
@ -27622,7 +27274,8 @@
"@types/lodash": {
"version": "4.14.195",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg=="
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==",
"dev": true
},
"@types/long": {
"version": "4.0.2",
@ -27740,14 +27393,6 @@
"@types/react": "*"
}
},
"@types/react-dom": {
"version": "18.2.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz",
"integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==",
"requires": {
"@types/react": "*"
}
},
"@types/react-grid-layout": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.3.2.tgz",
@ -28021,145 +27666,6 @@
"eslint-visitor-keys": "^3.3.0"
}
},
"@visx/axis": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@visx/axis/-/axis-2.14.0.tgz",
"integrity": "sha512-/mJuPiAUN/YdatSlXkkXD/8Pb4gVclbucNZkpUk6JVhnrmow0a1x73QiMXKuH2qDO5GDaqQS0C3Ly9lI3qTQ5Q==",
"requires": {
"@types/react": "*",
"@visx/group": "2.10.0",
"@visx/point": "2.6.0",
"@visx/scale": "2.2.2",
"@visx/shape": "2.12.2",
"@visx/text": "2.12.2",
"classnames": "^2.3.1",
"prop-types": "^15.6.0"
}
},
"@visx/bounds": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@visx/bounds/-/bounds-2.16.0.tgz",
"integrity": "sha512-KJoSSJt5SOLzprEbmsmdxR46PLudau0kWOgxavrah9D/49RsHwGdUdxHLKdXz4OdCruEfgGo4yxiuYLYmUWl4A==",
"requires": {
"@types/react": "*",
"@types/react-dom": "*",
"prop-types": "^15.5.10"
}
},
"@visx/chord": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/chord/-/chord-2.10.0.tgz",
"integrity": "sha512-p2ZQ9uiyiXV4Yu9DL+cqKbZbh1umMgCUWTP2CpgwSN5p822QHQjzpe1nafWOmNSLpANIBDDYIbtIgOZXUHkelA==",
"requires": {
"@types/d3-chord": "^1.0.9",
"@types/react": "*",
"classnames": "^2.3.1",
"d3-chord": "^1.0.4",
"prop-types": "^15.6.1"
}
},
"@visx/curve": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@visx/curve/-/curve-2.1.0.tgz",
"integrity": "sha512-9b6JOnx91gmOQiSPhUOxdsvcnW88fgqfTPKoVgQxidMsD/I3wksixtwo8TR/vtEz2aHzzsEEhlv1qK7Y3yaSDw==",
"requires": {
"@types/d3-shape": "^1.3.1",
"d3-shape": "^1.0.6"
}
},
"@visx/event": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz",
"integrity": "sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw==",
"requires": {
"@types/react": "*",
"@visx/point": "2.6.0"
}
},
"@visx/group": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/group/-/group-2.10.0.tgz",
"integrity": "sha512-DNJDX71f65Et1+UgQvYlZbE66owYUAfcxTkC96Db6TnxV221VKI3T5l23UWbnMzwFBP9dR3PWUjjqhhF12N5pA==",
"requires": {
"@types/react": "*",
"classnames": "^2.3.1",
"prop-types": "^15.6.2"
}
},
"@visx/point": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@visx/point/-/point-2.6.0.tgz",
"integrity": "sha512-amBi7yMz4S2VSchlPdliznN41TuES64506ySI22DeKQ+mc1s1+BudlpnY90sM1EIw4xnqbKmrghTTGfy6SVqvQ=="
},
"@visx/responsive": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-2.10.0.tgz",
"integrity": "sha512-NssDPpuUYp7hqVISuYkKZ5zk6ob0++RdTIaUjRcUdyFEbvzb9+zIb8QToOkvI90L2EC/MY4Jx0NpDbEe79GpAw==",
"requires": {
"@juggle/resize-observer": "^3.3.1",
"@types/lodash": "^4.14.172",
"@types/react": "*",
"lodash": "^4.17.21",
"prop-types": "^15.6.1"
}
},
"@visx/scale": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@visx/scale/-/scale-2.2.2.tgz",
"integrity": "sha512-3aDySGUTpe6VykDQmF+g2nz5paFu9iSPTcCOEgkcru0/v5tmGzUdvivy8CkYbr87HN73V/Jc53lGm+kJUQcLBw==",
"requires": {
"@types/d3-interpolate": "^1.3.1",
"@types/d3-scale": "^3.3.0",
"@types/d3-time": "^2.0.0",
"d3-interpolate": "^1.4.0",
"d3-scale": "^3.3.0",
"d3-time": "^2.1.1"
}
},
"@visx/shape": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.12.2.tgz",
"integrity": "sha512-4gN0fyHWYXiJ+Ck8VAazXX0i8TOnLJvOc5jZBnaJDVxgnSIfCjJn0+Nsy96l9Dy/bCMTh4DBYUBv9k+YICBUOA==",
"requires": {
"@types/d3-path": "^1.0.8",
"@types/d3-shape": "^1.3.1",
"@types/lodash": "^4.14.172",
"@types/react": "*",
"@visx/curve": "2.1.0",
"@visx/group": "2.10.0",
"@visx/scale": "2.2.2",
"classnames": "^2.3.1",
"d3-path": "^1.0.5",
"d3-shape": "^1.2.0",
"lodash": "^4.17.21",
"prop-types": "^15.5.10"
}
},
"@visx/text": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/@visx/text/-/text-2.12.2.tgz",
"integrity": "sha512-Sv9YEolggfv2Nf6+l28ESG3VXVR1+s4u/Cz17QpgOxygcbOM8LfLtriWtBsBMKdMbYKeUpoUro0clx55TUwzew==",
"requires": {
"@types/lodash": "^4.14.172",
"@types/react": "*",
"classnames": "^2.3.1",
"lodash": "^4.17.21",
"prop-types": "^15.7.2",
"reduce-css-calc": "^1.3.0"
}
},
"@visx/tooltip": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/@visx/tooltip/-/tooltip-2.16.0.tgz",
"integrity": "sha512-kCjVtNGYpYm6AuMc1fT9cIwqpBivfQ8A5Qd7fQMC2xm4YVmehFdbyRr0nVft8zNXyUW3lDxbcq5bPIMRYhuhlA==",
"requires": {
"@types/react": "*",
"@visx/bounds": "2.16.0",
"classnames": "^2.3.1",
"prop-types": "^15.5.10",
"react-use-measure": "^2.0.4"
}
},
"@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
@ -30438,88 +29944,6 @@
"internmap": "1 - 2"
}
},
"d3-chord": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
"integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
"requires": {
"d3-array": "1",
"d3-path": "1"
},
"dependencies": {
"d3-array": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
"integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
}
}
},
"d3-color": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
},
"d3-format": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
"integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
},
"d3-interpolate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
"requires": {
"d3-color": "1"
}
},
"d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
},
"d3-scale": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
"integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
"requires": {
"d3-array": "^2.3.0",
"d3-format": "1 - 2",
"d3-interpolate": "1.2.0 - 2",
"d3-time": "^2.1.1",
"d3-time-format": "2 - 3"
},
"dependencies": {
"d3-array": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
"integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
"requires": {
"internmap": "^1.0.0"
}
},
"d3-time-format": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
"integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
"requires": {
"d3-time": "1 - 2"
}
},
"internmap": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
}
}
},
"d3-shape": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
"requires": {
"d3-path": "1"
}
},
"d3-time": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
@ -30591,7 +30015,8 @@
"debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
"dev": true
},
"debug": {
"version": "4.3.4",
@ -30868,6 +30293,31 @@
"safe-buffer": "^5.0.1"
}
},
"echarts": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.4.2.tgz",
"integrity": "sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==",
"requires": {
"tslib": "2.3.0",
"zrender": "5.4.3"
},
"dependencies": {
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
}
},
"echarts-for-react": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz",
"integrity": "sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==",
"requires": {
"fast-deep-equal": "^3.1.3",
"size-sensor": "^1.0.1"
}
},
"ecpair": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ecpair/-/ecpair-2.1.0.tgz",
@ -35509,11 +34959,6 @@
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true
},
"math-expression-evaluator": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz",
"integrity": "sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw=="
},
"md5": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
@ -37260,14 +36705,6 @@
"prop-types": "^15.6.2"
}
},
"react-use-measure": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
"requires": {
"debounce": "^1.2.1"
}
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@ -37375,31 +36812,6 @@
}
}
},
"reduce-css-calc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
"integrity": "sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==",
"requires": {
"balanced-match": "^0.4.2",
"math-expression-evaluator": "^1.2.14",
"reduce-function-call": "^1.0.1"
},
"dependencies": {
"balanced-match": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
"integrity": "sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg=="
}
}
},
"reduce-function-call": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz",
"integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==",
"requires": {
"balanced-match": "^1.0.0"
}
},
"reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
@ -37874,6 +37286,11 @@
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"dev": true
},
"size-sensor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.1.tgz",
"integrity": "sha512-QTy7MnuugCFXIedXRpUSk9gUnyNiaxIdxGfUjr8xxXOqIB3QvBUYP9+b51oCg2C4dnhaeNk/h57TxjbvoJrJUA=="
},
"sjcl": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz",
@ -39812,6 +39229,21 @@
"version": "3.21.4",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw=="
},
"zrender": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.4.3.tgz",
"integrity": "sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==",
"requires": {
"tslib": "2.3.0"
},
"dependencies": {
"tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
}
}
}
}
}

View File

@ -49,14 +49,6 @@
"@nestjs/throttler": "^4.0.0",
"@nestjs/websockets": "^9.4.2",
"@tanstack/react-table": "^8.9.1",
"@visx/axis": "^2.12.2",
"@visx/chord": "^2.10.0",
"@visx/event": "^2.6.0",
"@visx/group": "^2.10.0",
"@visx/responsive": "^2.10.0",
"@visx/scale": "^2.2.2",
"@visx/shape": "^2.12.2",
"@visx/tooltip": "^2.16.0",
"apollo-server-express": "^3.12.0",
"balanceofsatoshis": "^15.8.2",
"bcryptjs": "^2.4.3",
@ -73,6 +65,8 @@
"d3-time-format": "^4.1.0",
"date-fns": "^2.30.0",
"dotenv": "^16.0.3",
"echarts": "^5.4.2",
"echarts-for-react": "^3.0.2",
"ecpair": "^2.0.1",
"graphql": "^16.6.0",
"jest-fetch-mock": "^3.0.3",

View File

@ -10,6 +10,7 @@ module.exports = {
reactStrictMode: true,
poweredByHeader: false,
basePath: process.env.BASE_PATH || '',
transpilePackages: ['echarts', 'zrender'],
compiler: {
styledComponents: true,
},

View File

@ -4,7 +4,6 @@ import { getProps } from '../src/utils/ssr';
import { ForwardsList } from '../src/views/forwards/index';
import { ForwardChannelsReport } from '../src/views/home/reports/forwardReport/ForwardChannelReport';
import { useState } from 'react';
import { ForwardChord } from '../src/views/forwards/forwardChord';
import { ForwardTable } from '../src/views/forwards/ForwardTable';
import { options, typeOptions } from '../src/views/home/reports/forwardReport';
import { ForwardsGraph } from '../src/views/home/reports/forwardReport/ForwardsGraph';
@ -18,6 +17,7 @@ import {
CardTitle,
Separation,
} from '../src/components/generic/Styled';
import { ForwardSankey } from '../src/views/forwards/forwardSankey';
const S = {
options: styled.div`
@ -85,9 +85,12 @@ const ForwardsView = () => {
<Card>
<ForwardTable days={days.value} order={type.value} />
</Card>
<SubTitle>Chord Graph</SubTitle>
<SubTitle>Sankey</SubTitle>
<Card>
<ForwardChord days={days.value} order={type.value} />
<ForwardSankey
days={days.value}
type={type.value as 'amount' | 'fee' | 'tokens'}
/>
</Card>
</>
)}

View File

@ -110,8 +110,6 @@ const TransactionsView = () => {
return '';
}
}
console.log(lastInvoice, lastPayment);
}, [invoiceQuery.data, paymentQuery.data, show]);
const renderInvoices = useCallback(() => {

View File

@ -1,235 +1,132 @@
import { Group } from '@visx/group';
import { BarGroup } from '@visx/shape';
import { AxisBottom, AxisLeft } from '@visx/axis';
import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale';
import { timeParse, timeFormat } from 'd3-time-format';
import { ParentSize } from '@visx/responsive';
import { chartColors } from '../../../src/styles/Themes';
import { useContext, useMemo } from 'react';
import { BarChart as EBarChart } from 'echarts/charts';
import {
GraphicComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
} from 'echarts/components';
import * as echarts from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import ReactEChartsCore from 'echarts-for-react/lib/core';
import { ThemeContext } from 'styled-components';
import { useContext } from 'react';
import { TooltipWithBounds, defaultStyles, useTooltip } from '@visx/tooltip';
import { localPoint } from '@visx/event';
import { Price } from '../price/Price';
import numeral from 'numeral';
import { timeFormat, timeParse } from 'd3-time-format';
import { formatSats } from '../../utils/helpers';
import { COMMON_CHART_STYLES } from './common';
type BarGroupProps = {
width: number;
height: number;
} & BarChartProps;
echarts.use([
EBarChart,
CanvasRenderer,
GridComponent,
TooltipComponent,
GraphicComponent,
TitleComponent,
LegendComponent,
ToolboxComponent,
]);
type BarChartProps = {
data: any[];
margin?: { top: number; right: number; bottom: number; left: number };
events?: boolean;
colorRange?: string[];
priceLabel?: boolean;
};
const defaultMargin = { top: 40, right: 0, bottom: 40, left: 0 };
const defaultColorRange = [
chartColors.green,
chartColors.orange,
chartColors.lightblue,
];
const amountOfYTicks = (height: number) => {
switch (true) {
case height < 300:
return 3;
case height < 400:
return 6;
default:
return 10;
}
};
const amountOfXTicks = (width: number) => {
switch (true) {
case width < 300:
return 2;
case width < 400:
return 6;
default:
return 10;
}
};
const parseDate = timeParse('%Y-%m-%dT%H:%M:%S.%L%Z');
const formatDate = timeFormat('%b %d');
const formatTime = timeFormat('%H:%M');
const tickFormatDate = (date: string) => formatDate(parseDate(date) as Date);
const tickFormatTime = (date: string) => formatTime(parseDate(date) as Date);
const tooltipStyles = {
...defaultStyles,
minWidth: 60,
backgroundColor: 'rgba(0,0,0,0.9)',
color: 'white',
};
const Chart = ({
width,
height,
margin = defaultMargin,
data = [],
colorRange = defaultColorRange,
priceLabel,
}: BarGroupProps) => {
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
showTooltip,
hideTooltip,
} = useTooltip<any>();
interface BarChartProps {
colorRange: string[];
data: any;
title: string;
dataKey: string;
}
export const BarChart = ({
data,
colorRange,
title,
dataKey,
}: BarChartProps) => {
const themeContext = useContext(ThemeContext);
const axisColor = themeContext.mode === 'light' ? 'black' : 'white';
const seriesData = useMemo(() => {
if (data.length === 0) return { dates: [], series: [] };
const keys = Object.keys(data[0] || {}).filter(d => d !== 'date');
const series = [
{
name: title,
type: 'bar',
emphasis: { focus: 'series' },
data: data.map((d: any) => d[dataKey]),
},
];
if (!keys.length) return null;
const dates = data.map((d: any) => d.date);
let tooltipTimeout: number;
return { dates, series };
}, [data, title]);
const getDate = (d: any) => d.date;
const option = useMemo(() => {
const fontColor = themeContext.mode === 'light' ? 'black' : 'white';
const xScale = scaleBand<string>({
domain: data.map(getDate),
padding: 0.2,
});
const barScale = scaleBand<string>({
domain: keys,
padding: 0.1,
});
const yScale = scaleLinear<number>({
domain: [
0,
Math.max(...data.map(d => Math.max(...keys.map(key => Number(d[key]))))),
],
});
const colorScale = scaleOrdinal<string, string>({
domain: keys,
range: colorRange,
});
// bounds
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
// update scale output dimensions
xScale.rangeRound([0, xMax]);
yScale.rangeRound([yMax, 0]);
barScale.rangeRound([0, xScale.bandwidth()]);
return {
color: colorRange,
grid: {
containLabel: true,
top: '50px',
left: '25px',
bottom: '25px',
right: '25px',
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false,
},
formatter: (params: any) => {
return `<span style='color: ${
colorRange[0]
}; font-weight: bold;'>${title}</span><br />
${formatSats(params[0].value)}<br />`;
},
...COMMON_CHART_STYLES.tooltip,
},
xAxis: {
name: 'Dates',
nameLocation: 'center',
nameGap: 32,
type: 'category',
axisLine: { show: true, lineStyle: { color: fontColor } },
data: seriesData.dates,
axisLabel: {
formatter: function (value: string) {
const parseDate = timeParse('%Y-%m-%dT%H:%M:%S.%L%Z');
const formatDate = timeFormat('%b %d');
return formatDate(parseDate(value) as Date);
},
},
},
yAxis: {
nameLocation: 'center',
nameGap: 48,
type: 'value',
minInterval: 1,
splitLine: { show: false },
axisLine: { show: true, lineStyle: { color: fontColor } },
axisTick: { show: true },
axisLabel: {
formatter: function (value: number) {
const format = value < 1000 ? '0a' : '0.0a';
return numeral(value).format(format);
},
},
},
series: seriesData.series,
};
}, [colorRange, themeContext, seriesData, title]);
return (
<div style={{ position: 'relative' }}>
<svg width={width} height={height - 10}>
<Group top={margin.top} left={margin.left}>
<BarGroup
data={data}
keys={keys}
height={yMax}
x0={getDate}
x0Scale={xScale}
x1Scale={barScale}
yScale={yScale}
color={colorScale}
>
{barGroups =>
barGroups.map(barGroup => (
<Group
key={`bar-group-${barGroup.index}-${barGroup.x0}`}
left={barGroup.x0}
>
{barGroup.bars.map(bar => (
<rect
key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
x={bar.x}
y={bar.y}
width={bar.width}
height={Math.abs(bar.height)}
fill={bar.color}
onMouseOver={(e: any) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const coords = localPoint(e.target.ownerSVGElement, e);
if (!coords) return;
showTooltip({
tooltipLeft: coords.x,
tooltipTop: coords.y,
tooltipData: bar,
});
}}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
/>
))}
</Group>
))
}
</BarGroup>
</Group>
<AxisLeft
numTicks={amountOfYTicks(height)}
left={width - margin.left}
top={margin.bottom}
hideZero={true}
scale={yScale}
stroke={axisColor}
tickStroke={axisColor}
tickLabelProps={() => ({
fill: axisColor,
fontSize: 11,
textAnchor: 'end',
dy: '0.33em',
dx: '-0.33em',
})}
/>
<AxisBottom
numTicks={amountOfXTicks(width)}
top={yMax + margin.top}
tickFormat={data.length == 24 ? tickFormatTime : tickFormatDate}
scale={xScale}
stroke={axisColor}
tickStroke={axisColor}
tickLabelProps={() => ({
fill: axisColor,
fontSize: 11,
textAnchor: 'middle',
})}
/>
</svg>
{tooltipOpen && tooltipData ? (
<TooltipWithBounds
top={tooltipTop}
left={tooltipLeft}
style={tooltipStyles}
>
<div style={{ color: colorScale(tooltipData.key) }}>
<strong>{tooltipData.key}</strong>
</div>
{priceLabel ? (
<Price amount={tooltipData.value} />
) : (
tooltipData.value
)}
</TooltipWithBounds>
) : null}
</div>
<ReactEChartsCore
echarts={echarts}
option={option}
notMerge={true}
lazyUpdate={true}
showLoading={false}
style={{ height: '100%' }}
/>
);
};
export const BarChart = (props: BarChartProps) => (
<ParentSize>
{parent => <Chart width={parent.width} height={parent.height} {...props} />}
</ParentSize>
);

View File

@ -1,191 +1,155 @@
import { Group } from '@visx/group';
import { BarGroupHorizontal, Bar } from '@visx/shape';
import { AxisLeft } from '@visx/axis';
import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale';
import { ParentSize } from '@visx/responsive';
import { chartColors } from '../../../src/styles/Themes';
import { chartColors } from '../../styles/Themes';
import { ThemeContext } from 'styled-components';
import { useContext } from 'react';
import { TooltipWithBounds, defaultStyles, useTooltip } from '@visx/tooltip';
import { localPoint } from '@visx/event';
import { Price } from '../price/Price';
import { useContext, useMemo } from 'react';
import { BarChart } from 'echarts/charts';
import {
GraphicComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
} from 'echarts/components';
import * as echarts from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import ReactEChartsCore from 'echarts-for-react/lib/core';
import { formatSats } from '../../utils/helpers';
import { COMMON_CHART_STYLES } from './common';
type BarGroupProps = {
width: number;
height: number;
} & BarChartProps;
echarts.use([
BarChart,
CanvasRenderer,
GridComponent,
TooltipComponent,
GraphicComponent,
TitleComponent,
LegendComponent,
ToolboxComponent,
]);
type BarChartProps = {
type HorizontalBarChartProps = {
data: any[];
margin?: { top: number; right: number; bottom: number; left: number };
events?: boolean;
dataKey: string;
colorRange?: string[];
priceLabel?: boolean;
};
const defaultMargin = { top: 40, right: 0, bottom: 40, left: 0 };
const defaultColorRange = [
chartColors.green,
chartColors.orange,
chartColors.lightblue,
];
const tooltipStyles = {
...defaultStyles,
minWidth: 60,
backgroundColor: 'rgba(0,0,0,0.9)',
color: 'white',
};
const Chart = ({
width,
height,
margin = defaultMargin,
export const HorizontalBarChart = ({
data = [],
colorRange = defaultColorRange,
priceLabel,
}: BarGroupProps) => {
const {
tooltipData,
tooltipLeft,
tooltipTop,
tooltipOpen,
showTooltip,
hideTooltip,
} = useTooltip<any>();
dataKey,
}: HorizontalBarChartProps) => {
const themeContext = useContext(ThemeContext);
const axisColor = themeContext.mode === 'light' ? 'black' : 'white';
const keys = Object.keys(data[0] || {}).filter(d => d !== 'label');
if (!keys.length) return null;
const maxValue = Math.max(
...data.map(d => Math.max(...keys.map(key => Number(d[key]))))
);
let tooltipTimeout: number;
const seriesData = useMemo(() => {
if (data.length === 0) return [{ type: 'bar', data: [], barWidth: 25 }];
const getLabel = (d: any) => d.label;
return [
{
type: 'bar',
data: data.map((d: any) => d[dataKey]),
barWidth: '25',
},
];
}, [data, dataKey]);
const yScale = scaleBand<string>({
domain: data.map(getLabel),
});
const yLabels = useMemo(() => {
if (!data.length) return [];
return data.map(d => d.label);
}, [data]);
const barScale = scaleBand<string>({
domain: keys,
padding: 0.1,
});
const option = useMemo(() => {
const themeColor = themeContext.mode === 'light' ? 'black' : 'white';
const xScale = scaleLinear<number>({
domain: [0, maxValue + 0.1 * maxValue],
});
return {
color: colorRange,
grid: {
left: '25px',
bottom: '25px',
top: '25px',
right: '25px',
},
tooltip: {
trigger: 'axis',
axisPointer: {
animation: false,
},
formatter: (params: any) => {
return `<span style='color: ${
colorRange[0]
}; font-weight: bold;'>Value</span><br />
${formatSats(params[0].value)}`;
},
...COMMON_CHART_STYLES.tooltip,
},
xAxis: {
max: maxValue * 1.4,
show: false,
alignTicks: 'value',
zIndex: 10,
z: 10,
},
yAxis: {
axisPointer: {
show: true,
type: 'none',
triggerTooltip: true,
},
lineStyle: {
color: themeColor,
type: 'solid',
},
axisLine: {
onZero: false,
lineStyle: {
color: themeColor,
type: 'solid',
},
},
axisLabel: {
inside: true,
interval: 'auto',
},
axisTick: {
show: true,
alignWithLabel: true,
interval: 'auto',
inside: true,
},
data: yLabels,
type: 'category',
inverse: true,
max: 4,
position: 'right',
},
legend: { show: true },
series: seriesData,
};
}, [colorRange, themeContext, seriesData, yLabels]);
const colorScale = scaleOrdinal<string, string>({
domain: keys,
range: colorRange,
});
// bounds
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
// update scale output dimensions
xScale.rangeRound([0, xMax]);
yScale.rangeRound([yMax, 0]);
barScale.rangeRound([0, yScale.bandwidth()]);
if (!keys.length) return null;
return (
<div style={{ position: 'relative' }}>
<svg width={width} height={height - 10}>
<Group top={margin.top} left={margin.left}>
<BarGroupHorizontal
data={data}
keys={keys}
width={xMax}
y0={getLabel}
y0Scale={yScale}
y1Scale={barScale}
xScale={xScale}
color={colorScale}
>
{barGroups =>
barGroups.map(barGroup => (
<Group
key={`bar-group-${barGroup.index}-${barGroup.y0}`}
top={barGroup.y0}
>
{barGroup.bars.map(bar => (
<Bar
key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
x={bar.x}
y={bar.y}
width={bar.width < 10 ? 10 : bar.width}
height={Math.abs(bar.height)}
fill={bar.color}
onMouseOver={(e: any) => {
if (tooltipTimeout) clearTimeout(tooltipTimeout);
const coords = localPoint(e.target.ownerSVGElement, e);
if (!coords) return;
showTooltip({
tooltipLeft: coords.x,
tooltipTop: coords.y,
tooltipData: bar,
});
}}
onMouseLeave={() => {
tooltipTimeout = window.setTimeout(() => {
hideTooltip();
}, 300);
}}
/>
))}
</Group>
))
}
</BarGroupHorizontal>
</Group>
<AxisLeft
left={width - margin.left}
top={margin.bottom}
hideZero={true}
scale={yScale}
stroke={axisColor}
tickStroke={axisColor}
tickLabelProps={() => ({
fill: axisColor,
fontSize: 11,
textAnchor: 'end',
dy: '0.33em',
dx: '-0.33em',
})}
/>
</svg>
{tooltipOpen && tooltipData ? (
<TooltipWithBounds
top={tooltipTop}
left={tooltipLeft}
style={tooltipStyles}
>
<div style={{ color: colorScale(tooltipData.key) }}>
<strong>{tooltipData.key}</strong>
</div>
{priceLabel ? (
<Price amount={tooltipData.value} />
) : (
tooltipData.value
)}
</TooltipWithBounds>
) : null}
<div style={{ position: 'relative', height: '100%' }}>
<ReactEChartsCore
echarts={echarts}
option={option}
notMerge={true}
lazyUpdate={true}
showLoading={false}
style={{ height: '100%' }}
/>
</div>
);
};
export const HorizontalBarChart = (props: BarChartProps) => (
<ParentSize>
{parent => <Chart width={parent.width} height={parent.height} {...props} />}
</ParentSize>
);

View File

@ -0,0 +1,9 @@
export const COMMON_CHART_STYLES = {
tooltip: {
backgroundColor: 'black',
borderColor: 'black',
textStyle: {
color: 'white',
},
},
};

View File

@ -1,108 +0,0 @@
import React from 'react';
import { Arc } from '@visx/shape';
import { Group } from '@visx/group';
import { Chord, Ribbon } from '@visx/chord';
import { scaleOrdinal } from '@visx/scale';
const pink = '#ff2fab';
const orange = '#ffc62e';
const purple = '#dc04ff';
const purple2 = '#7324ff';
const red = '#d04376';
const green = '#52f091';
const blue = '#04a6ff';
const lime = '#00ddc6';
function descending(a: number, b: number): number {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
export type SingleGroupProps = {
endAngle: number;
index: number;
startAngle: number;
value: number;
};
export type SingleChordProps = {
source: SingleGroupProps;
target: SingleGroupProps;
};
const groupColor = scaleOrdinal<number, string>({
domain: [0, 1, 2, 3, 4, 5, 6, 7],
range: [pink, orange, purple, purple2, red, green, blue, lime],
});
export type ChordProps = {
matrix: number[][];
width: number;
height: number;
centerSize?: number;
groupCallback?: (group: SingleGroupProps) => void;
chordCallback?: (chord: SingleChordProps) => void;
};
export const ChordGraph = ({
matrix,
width,
height,
centerSize = 20,
groupCallback,
chordCallback,
}: ChordProps) => {
const outerRadius = Math.min(width, height) * 0.5 - (centerSize + 10);
const innerRadius = outerRadius - centerSize;
return width < 10 ? null : (
<div className="chords">
<svg width={width} height={height}>
<Group top={height / 2} left={width / 2}>
<Chord matrix={matrix} padAngle={0.05} sortSubgroups={descending}>
{({ chords }) => (
<g>
{chords.groups.map((group, i) => (
<Arc
key={`key-${i}`}
data={group}
innerRadius={innerRadius}
outerRadius={outerRadius}
fill={groupColor(i)}
onClick={() => {
groupCallback && groupCallback(group);
}}
/>
))}
{chords.map((chord, i) => {
return (
<Ribbon
key={`ribbon-${i}`}
chord={chord}
radius={innerRadius}
fill={groupColor(chord.source.index)}
fillOpacity={0.75}
onClick={() => {
chordCallback && chordCallback(chord);
}}
/>
);
})}
</g>
)}
</Chord>
</Group>
</svg>
<style jsx>{`
.chords {
display: flex;
flex-direction: column;
user-select: none;
}
svg {
margin: 1rem 0;
cursor: pointer;
}
`}</style>
</div>
);
};

View File

@ -160,10 +160,11 @@ export const getPastFutureStr = (
};
export const getFormatDate = (
date: string | null | undefined
date: string | null | undefined,
style?: string
): string | null => {
if (!date) return null;
return format(new Date(date), 'dd/MM/yyyy - HH:mm:ss');
return format(new Date(date), style || 'yyyy.MM.dd - H:mm:ss');
};
export const getMessageDate = (

View File

@ -0,0 +1,101 @@
import { useContext, useMemo } from 'react';
import {
GraphicComponent,
GridComponent,
LegendComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
} from 'echarts/components';
import * as echarts from 'echarts/core';
import { SankeyChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import ReactEChartsCore from 'echarts-for-react/lib/core';
import { ThemeContext } from 'styled-components';
echarts.use([
SankeyChart,
CanvasRenderer,
GridComponent,
TooltipComponent,
GraphicComponent,
TitleComponent,
LegendComponent,
ToolboxComponent,
]);
export type SankeyProps = {
width: string;
height: string;
data: SankeyData;
};
interface SankeyNode {
name: string;
}
interface SankeyLink {
source: string;
target: string;
value: number;
}
export interface SankeyData {
links: SankeyLink[];
nodes: SankeyNode[];
}
export const Sankey = ({ data, width, height }: SankeyProps) => {
const themeContext = useContext(ThemeContext);
const option = useMemo(() => {
const fontColor = themeContext.mode === 'light' ? 'black' : 'white';
return {
resize: true,
tooltip: {
trigger: 'item',
triggerOn: 'mousemove',
},
series: [
{
height,
type: 'sankey',
nodeAlign: 'justify',
nodeGap: 3,
layoutIterations: 32,
data: data.nodes,
links: data.links,
dragable: false,
left: 'left',
bottom: '5',
top: 'top',
label: {
show: true,
color: fontColor,
},
emphasis: {
focus: 'adjacency',
},
lineStyle: {
color: 'gradient',
curveness: 0.25,
},
itemStyle: {
borderWidth: 50,
},
},
],
};
}, [data, themeContext, height]);
return (
<ReactEChartsCore
echarts={echarts}
option={option}
notMerge={true}
lazyUpdate={true}
showLoading={false}
style={{ height, width }}
/>
);
};

View File

@ -12,6 +12,8 @@ import { widgetList, WidgetProps } from './widgetList';
import { GetInvoicesQuery } from '../../../graphql/queries/__generated__/getInvoices.generated';
import { GetPaymentsQuery } from '../../../graphql/queries/__generated__/getPayments.generated';
const ONE_WEEK = 7;
const getColumns = (width: number): number => {
const { lg, md, sm, xs } = defaultGrid.breakpoints;
@ -82,12 +84,14 @@ export const getByTime = (array: ArrayType, time: number): any[] => {
fee?: number;
}[] = [];
// check to see if we're working in days (isDay = false) or hours (isDay = true)
const isDay = time <= 1;
const today = isDay
? new Date().setMinutes(0, 0, 0)
: new Date().setHours(0, 0, 0, 0);
// Look through each transaction in the array and build an array of transactions with the difference between today and the day (or hour) that the tx occurred
array.forEach((transaction: ArrayType[0]) => {
if (!transaction?.__typename) return;
@ -149,10 +153,16 @@ export const getByTime = (array: ArrayType, time: number): any[] => {
if (!transactions?.length) return [];
// Group the transactionw in the array according to the 'difference' property of the object
const grouped = groupBy(transactions, 'difference');
const final: any[] = [];
const differences = Array.from({ length: isDay ? 24 : time }, (_, i) => i);
// If were working with a single day, divide the array in 24 pieces, one for each hour, otherwise, go back 7 days
const differences = Array.from(
{ length: isDay ? 24 : time ? time : ONE_WEEK },
(_, i) => i
);
differences.forEach(key => {
const group = grouped[key];

View File

@ -1,11 +1,11 @@
import { useState } from 'react';
import { BarChart } from '../../../../components/chart/BarChart';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { SmallSelectWithValue } from '../../../../components/select';
import { useGetForwardsQuery } from '../../../../graphql/queries/__generated__/getForwards.generated';
import { chartColors } from '../../../../styles/Themes';
import styled from 'styled-components';
import { getByTime } from '../helpers';
import { BarChart } from '../../../../components/chart/BarChart';
const S = {
row: styled.div`
@ -110,11 +110,12 @@ export const ForwardsGraph = () => {
<Header />
<S.content>
<BarChart
priceLabel={type.value !== 'amount'}
data={forwards.map(f => ({
Forward: f[type.value] || 0,
date: f.date,
}))}
dataKey="Forward"
title="Forwards Report"
colorRange={[chartColors.purple]}
/>
</S.content>

View File

@ -1,5 +1,4 @@
import { useMemo, useState } from 'react';
import { BarChart } from '../../../../components/chart/BarChart';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { SmallSelectWithValue } from '../../../../components/select';
import { chartColors } from '../../../../styles/Themes';
@ -7,6 +6,7 @@ import styled from 'styled-components';
import { getByTime } from '../helpers';
import { useGetInvoicesQuery } from '../../../../graphql/queries/__generated__/getInvoices.generated';
import { differenceInDays } from 'date-fns';
import { BarChart } from '../../../../components/chart/BarChart';
const S = {
row: styled.div`
@ -108,13 +108,14 @@ export const InvoicesGraph = () => {
<Header />
<S.content>
<BarChart
priceLabel={type.value !== 'amount'}
data={invoicesByDate.map(f => {
return {
Invoices: f?.[type.value] || 0,
date: f.date,
};
})}
dataKey="Invoices"
title="Invoices"
colorRange={[chartColors.orange2]}
/>
</S.content>

View File

@ -62,9 +62,9 @@ export const LiquidityGraph = () => {
return (
<S.wrapper>
<HorizontalBarChart
priceLabel={true}
data={liquidity}
colorRange={[chartColors.green]}
dataKey="Value"
/>
</S.wrapper>
);

View File

@ -1,6 +1,4 @@
import { useMemo, useState } from 'react';
import { BarChart } from '../../../../components/chart/BarChart';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { SmallSelectWithValue } from '../../../../components/select';
import { chartColors } from '../../../../styles/Themes';
@ -8,6 +6,7 @@ import styled from 'styled-components';
import { getByTime } from '../helpers';
import { useGetPaymentsQuery } from '../../../../graphql/queries/__generated__/getPayments.generated';
import { differenceInDays } from 'date-fns';
import { BarChart } from '../../../../components/chart/BarChart';
const S = {
row: styled.div`
@ -108,7 +107,6 @@ export const PaymentsGraph = () => {
<Header />
<S.content>
<BarChart
priceLabel={type.value !== 'amount'}
data={paymentsByDate.map(f => {
return {
Payments: f?.[type.value] || 0,
@ -116,6 +114,8 @@ export const PaymentsGraph = () => {
};
})}
colorRange={[chartColors.darkyellow]}
title="Payments"
dataKey="Payments"
/>
</S.content>
</S.wrapper>

View File

@ -1,161 +0,0 @@
import React, { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import { Forward } from '../../graphql/types';
import { ParentSize } from '@visx/responsive';
import {
ChordGraph,
SingleGroupProps,
SingleChordProps,
} from '../../components/chord';
import styled from 'styled-components';
import { renderLine } from '../../components/generic/helpers';
import { DarkSubTitle } from '../../components/generic/Styled';
import { mediaWidths } from '../../styles/Themes';
import { usePriceState } from '../../context/PriceContext';
import { getPrice } from '../../components/price/Price';
import { useConfigState } from '../../context/ConfigContext';
import { useGetForwardsQuery } from '../../graphql/queries/__generated__/getForwards.generated';
import { ChannelAlias } from '../home/reports/forwardReport/ChannelAlias';
import { getChordMatrix } from './helpers';
const Wrapper = styled.div`
height: 800px;
width: 100%;
@media (${mediaWidths.mobile}) {
height: 300px;
}
`;
const Center = styled.div`
width: '100%';
text-align: center;
`;
type SelectedProps =
| { type: 'group'; group: SingleGroupProps }
| { type: 'chord'; chord: SingleChordProps }
| null;
const getTitle = (order: string) => {
switch (order) {
case 'fee':
return 'Total Fees (sats)';
case 'tokens':
return 'Total Tokens (sats)';
default:
return 'Total Amount (Forwards)';
}
};
export const ForwardChord = ({
days,
order,
}: {
days: number;
order: string;
}) => {
const [selected, setSelected] = useState<SelectedProps>();
useEffect(() => {
setSelected(null);
}, [order]);
const { data, loading } = useGetForwardsQuery({
ssr: false,
variables: { days },
onError: error => toast.error(getErrorContent(error)),
});
const { currency, displayValues } = useConfigState();
const priceContext = usePriceState();
const format = getPrice(currency, displayValues, priceContext);
if (loading || !data?.getForwards?.length) {
return null;
}
const { matrix, uniqueNodes } = getChordMatrix(
order,
data.getForwards as Forward[]
);
const handleGroupClick = (group: SingleGroupProps) => {
setSelected({ type: 'group', group });
};
const handleChordClick = (chord: SingleChordProps) => {
setSelected({ type: 'chord', chord });
};
const renderInfo = () => {
if (!selected) {
return (
<Center>
<DarkSubTitle>Click the graph for specific info!</DarkSubTitle>
</Center>
);
}
if (selected.type === 'group') {
return (
<>
{renderLine(
'With',
<ChannelAlias id={uniqueNodes[selected.group.index]} />
)}
{renderLine('Channel Id', uniqueNodes[selected.group.index])}
{renderLine(
getTitle(order),
format({ amount: selected.group.value, noUnit: order === 'amount' })
)}
</>
);
}
if (selected.type === 'chord') {
return (
<>
{renderLine(
'Between',
<>
<ChannelAlias id={uniqueNodes[selected.chord.source.index]} />
{` - `}
<ChannelAlias id={uniqueNodes[selected.chord.target.index]} />
</>
)}
{renderLine(
'Channel Ids',
`${uniqueNodes[selected.chord.source.index]} - ${
uniqueNodes[selected.chord.target.index]
}`
)}
{renderLine(
getTitle(order),
format({
amount: selected.chord.source.value,
noUnit: order === 'amount',
})
)}
</>
);
}
};
return (
<>
{renderInfo()}
<Wrapper>
<ParentSize>
{parent => (
<ChordGraph
matrix={matrix}
width={parent.width}
height={parent.height}
groupCallback={handleGroupClick}
chordCallback={handleChordClick}
/>
)}
</ParentSize>
</Wrapper>
</>
);
};

View File

@ -0,0 +1,100 @@
import React, { FC, useMemo } from 'react';
import { toast } from 'react-toastify';
import { getErrorContent } from '../../utils/error';
import styled from 'styled-components';
import { mediaWidths } from '../../styles/Themes';
import { useGetForwardsQuery } from '../../graphql/queries/__generated__/getForwards.generated';
import { Sankey, SankeyData } from '../../components/sankey';
import { groupBy, orderBy, reduce, uniq } from 'lodash';
const Wrapper = styled.div<{ $height: number }>`
height: ${props => props.$height}px;
max-height: ${props => props.$height}px;
width: 100%;
@media (${mediaWidths.mobile}) {
height: ${props => props.$height}px;
}
`;
export const ForwardSankey: FC<{
days: number;
type: 'amount' | 'fee' | 'tokens';
}> = ({ days, type }) => {
const { data, loading } = useGetForwardsQuery({
ssr: false,
variables: { days },
onError: error => toast.error(getErrorContent(error)),
});
const sankeyData: SankeyData = useMemo(() => {
if (loading || !data || !data.getForwards.length) {
return { links: [], nodes: [] };
}
const mapped = data.getForwards.map(d => ({
...d,
group: `${d.incoming_channel}-${d.outgoing_channel}`,
}));
const grouped = groupBy(mapped, 'group');
const aggregated: {
incoming_channel: string;
outgoing_channel: string;
fee: number;
tokens: number;
amount: number;
}[] = [];
Object.entries(grouped).forEach(([, value]) => {
const totalFees = value.map(v => v.fee).reduce((p, c) => p + c, 0);
const totalTokens = value.map(v => v.tokens).reduce((p, c) => p + c, 0);
const totalAmount = value.length;
const firstValue = value[0];
aggregated.push({
incoming_channel: firstValue.incoming_channel,
outgoing_channel: firstValue.outgoing_channel,
fee: totalFees,
tokens: totalTokens,
amount: totalAmount,
});
});
const finalData = reduce(
aggregated,
(p, c) => {
const source = `source: ${c.incoming_channel}`;
const target = `target: ${c.outgoing_channel}`;
return {
links: [...p.links, { source, target, value: c[type] || 0 }],
nodes: [...p.nodes, source, target],
};
},
{
links: [] as { source: string; target: string; value: number }[],
nodes: [] as string[],
}
);
return {
links: orderBy(finalData.links, 'value', 'desc'),
nodes: uniq(finalData.nodes).map(n => ({ name: n })),
};
}, [data, loading, type]);
if (loading || !data?.getForwards?.length) {
return null;
}
const graphHeight = 800 + 10 * sankeyData.links.length;
return (
<Wrapper $height={graphHeight}>
<Sankey data={sankeyData} width="100%" height={`${graphHeight}px`} />
</Wrapper>
);
};

View File

@ -1,52 +1,6 @@
import { sortBy } from 'lodash';
import { Forward } from '../../graphql/types';
export const getChordMatrix = (order: string, forwardArray: Forward[]) => {
const cleaned = forwardArray.map(f => {
let value = 1;
if (order === 'fee') {
value = f.fee;
} else if (order === 'tokens') {
value = f.tokens;
}
return {
incoming_channel: f.incoming_channel,
outgoing_channel: f.outgoing_channel,
value,
};
});
const incomingNodes = cleaned.map(f => f.incoming_channel);
const outgoingNodes = cleaned.map(f => f.outgoing_channel);
const uniqueNodes = [
...Array.from(new Set(incomingNodes)),
...Array.from(new Set(outgoingNodes)),
];
const nodeLength = uniqueNodes.length;
const matrix = new Array(nodeLength);
for (let i = 0; i < matrix.length; i++) {
matrix[i] = new Array(nodeLength).fill(0);
}
cleaned.forEach(f => {
const inIndex = uniqueNodes.indexOf(f.incoming_channel);
const outIndex = uniqueNodes.indexOf(f.outgoing_channel);
const previousValue = matrix[inIndex][outIndex];
const previousOutValue = matrix[outIndex][inIndex];
matrix[inIndex][outIndex] = previousValue + f.value;
matrix[outIndex][inIndex] = previousOutValue + f.value;
});
return { uniqueNodes, matrix };
};
export const sortByNode = (order: string, forwardArray: Forward[]) => {
const cleaned = forwardArray.map(f => {
let value = 1;

View File

@ -0,0 +1,93 @@
export function generateUniqueSourcesAndTargets(
numSources: number,
numTargets: number,
numRecords: number
) {
const sources: string[] = [];
const targets: string[] = [];
// Generate unique sources
for (let i = 0; i < numSources; i++) {
const randomSource = `${Math.floor(Math.random() * 10000) + 1}x1x1`;
sources.push(randomSource);
}
// Generate unique targets
for (let i = 0; i < numTargets; i++) {
const randomTarget = `${Math.floor(Math.random() * 10000) + 1}x1x1`;
targets.push(randomTarget);
}
// Build a graph to represent the relationships between sources and targets
const graph: { [key: string]: string[] } = {};
for (const target of targets) {
graph[target] = [];
}
const links = [];
// Generate items while ensuring no circular references
const orderedTargets = topologicalSort(graph);
for (const source of sources) {
for (const target of orderedTargets) {
const randomValue = Math.floor(Math.random() * 1000000) + 1;
const newItem = {
source: `source: ${source}`,
target: `target: ${target}`,
value: randomValue,
};
links.push(newItem);
graph[source] = graph[source] || [];
graph[source].push(target);
// If the desired number of records is reached, exit the loop early
if (links.length === numRecords) {
break;
}
}
// If the desired number of records is reached, exit the loop early
if (links.length === numRecords) {
break;
}
}
// Extract unique nodes from links
const uniqueNodesSet = new Set<string>();
for (const link of links) {
uniqueNodesSet.add(link.source);
uniqueNodesSet.add(link.target);
}
const nodes = Array.from(uniqueNodesSet).map(node => ({ name: node }));
return { links, nodes };
}
// Function to perform topological sorting
function topologicalSort(graph: { [key: string]: string[] }): string[] {
const visited = new Set<string>();
const stack: string[] = [];
function dfs(node: string) {
if (visited.has(node)) return;
visited.add(node);
const neighbors = graph[node];
if (neighbors) {
for (const neighbor of neighbors) {
dfs(neighbor);
}
}
stack.push(node);
}
for (const node of Object.keys(graph)) {
dfs(node);
}
return stack.reverse();
}

View File

@ -1,5 +1,4 @@
import { FC, useMemo } from 'react';
import { BarChart } from '../../../../components/chart/BarChart';
import { LoadingCard } from '../../../../components/loading/LoadingCard';
import { chartColors } from '../../../../styles/Themes';
import { getByTime } from '../../../../views/dashboard/widgets/helpers';
@ -7,6 +6,7 @@ import styled from 'styled-components';
import { useGetInvoicesQuery } from '../../../../graphql/queries/__generated__/getInvoices.generated';
import { differenceInDays } from 'date-fns';
import { useGetPaymentsQuery } from '../../../../graphql/queries/__generated__/getPayments.generated';
import { BarChart } from '../../../../components/chart/BarChart';
const S = {
row: styled.div`
@ -48,12 +48,29 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
showPay,
type,
}) => {
const { data, loading } = useGetInvoicesQuery();
const { data: invoiceData, loading } = useGetInvoicesQuery();
const { data: paymentsData, loading: paymentsLoading } =
useGetPaymentsQuery();
const labels = useMemo(() => {
switch (type) {
case 'amount':
return {
yAxisLabel: `Amount of ${showPay ? 'Payments' : 'Invoices'}`,
title: `Amount of ${showPay ? 'Payments' : 'Invoices'}`,
};
case 'tokens':
return {
yAxisLabel: `${showPay ? 'Payments' : 'Invoices'} Volume (sats)`,
title: `${showPay ? 'Payments' : 'Invoices'} Volume (sats)`,
};
default:
return {};
}
}, [type, showPay]);
const invoicesByDate = useMemo(() => {
const invoices = data?.getInvoices.invoices || [];
const invoices = invoiceData?.getInvoices.invoices || [];
const filtered = invoices.filter(i => !!i.is_confirmed);
if (!filtered.length) {
@ -70,7 +87,7 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
const invoicesByDate = getByTime(filtered, difference);
return invoicesByDate;
}, [data]);
}, [invoiceData, showPay]);
const paymentsByDate = useMemo(() => {
const payments = paymentsData?.getPayments.payments || [];
@ -90,7 +107,7 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
const paymentsByDate = getByTime(filtered, difference);
return paymentsByDate;
}, [paymentsData]);
}, [paymentsData, showPay]);
if (loading || paymentsLoading) {
return (
@ -102,10 +119,15 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
);
}
if (!data?.getInvoices.invoices.length) {
if (
(!showPay && !invoiceData?.getInvoices.invoices.length) ||
(showPay && !paymentsData?.getPayments.payments.length)
) {
return (
<S.wrapper>
<S.contentWrapper>No transactions for this period.</S.contentWrapper>
<S.contentWrapper>
No {showPay ? 'payments' : 'invoices'} for this period.
</S.contentWrapper>
</S.wrapper>
);
}
@ -117,7 +139,6 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
<S.wrapper>
<S.content>
<BarChart
priceLabel={type !== 'amount'}
data={finalArray.map(f => {
return {
[showPay ? 'Payments' : 'Invoices']: f?.[type] || 0,
@ -125,6 +146,8 @@ export const TransactionsGraph: FC<TransactionsGraphProps> = ({
};
})}
colorRange={finalColor}
title={labels.title || ''}
dataKey={showPay ? 'Payments' : 'Invoices'}
/>
</S.content>
</S.wrapper>

View File

@ -43,7 +43,7 @@ const options = [
const typeOptions = [
{ label: 'Count', value: 'amount' },
{ label: 'Amount', value: 'tokens' },
{ label: 'Volume', value: 'tokens' },
];
export const FlowBox = () => {

View File

@ -83,12 +83,13 @@ export const ForwardsGraph: FC<ForwardGraphProps> = ({ days, type }) => {
<S.wrapper>
<S.content>
<BarChart
priceLabel={type.value !== 'amount'}
title="Forwards"
data={forwards.map(f => ({
Forward: f[type.value] || 0,
date: f.date,
}))}
colorRange={[chartColors.purple]}
dataKey="Forward"
/>
</S.content>
</S.wrapper>

View File

@ -1,4 +1,3 @@
import { HorizontalBarChart } from '../../../../components/chart/HorizontalBarChart';
import {
Card,
CardWithTitle,
@ -10,6 +9,7 @@ import { useGetLiquidReportQuery } from '../../../../graphql/queries/__generated
import { chartColors } from '../../../../styles/Themes';
import styled from 'styled-components';
import { WarningText } from '../../../../views/stats/styles';
import { HorizontalBarChart } from '../../../../components/chart/HorizontalBarChart';
const S = {
row: styled.div`
@ -76,11 +76,11 @@ export const LiquidityGraph = () => {
} = data.getChannelReport;
const liquidity = [
{ label: 'Total Commit', Value: commit },
{ label: 'Max Outgoing', Value: maxOut },
{ label: 'Max Incoming', Value: maxIn },
{ label: 'Local Balance', Value: local },
{ label: 'Remote Balance', Value: remote },
{ label: 'Local Balance', Value: local },
{ label: 'Max Incoming', Value: maxIn },
{ label: 'Max Outgoing', Value: maxOut },
{ label: 'Total Commit', Value: commit },
];
const htlc = [
@ -96,7 +96,7 @@ export const LiquidityGraph = () => {
<Card mobileCardPadding={'8px 0'}>
<S.wrapper>
<HorizontalBarChart
priceLabel={true}
dataKey="Value"
data={liquidity}
colorRange={[chartColors.green]}
/>
@ -119,7 +119,7 @@ export const LiquidityGraph = () => {
) : (
<S.wrapper>
<HorizontalBarChart
priceLabel={false}
dataKey="Value"
data={htlc}
colorRange={[chartColors.green]}
/>