diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..ad93c14a0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + } +} diff --git a/package-lock.json b/package-lock.json index 7058bb35a..8956237d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "stellarexplorer", - "version": "1.0.10", + "version": "1.1.0-beta", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2110,6 +2110,39 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "chart.js": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.3.tgz", + "integrity": "sha512-3+7k/DbR92m6BsMUYP6M0dMsMVZpMnwkUyNSAbqolHKsbIzH2Q4LWVEHHYq7v0fmEV8whXE0DrjANulw9j2K5g==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", + "requires": { + "chartjs-color-string": "^0.5.0", + "color-convert": "^0.5.3" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "http://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", + "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", + "requires": { + "color-name": "^1.0.0" + } + }, "cheerio": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", @@ -2677,8 +2710,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "0.3.0", @@ -8953,25 +8985,6 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json2csv": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-4.1.6.tgz", - "integrity": "sha512-pmitnvvuX9OC6lL6T6F0sl6NsoXG94xLHKnKwdRSEpASFd3xdKHvsksPpNFZtDULP0AwOA0MGKrm1I0c7Enaxg==", - "requires": { - "commander": "^2.15.1", - "jsonparse": "^1.3.1", - "lodash.clonedeep": "^4.5.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2" - }, - "dependencies": { - "commander": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", - "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" - } - } - }, "json3": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", @@ -8999,11 +9012,6 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -9205,11 +9213,6 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -9228,21 +9231,11 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9601,6 +9594,11 @@ "minimist": "0.0.8" } }, + "moment": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz", + "integrity": "sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA==" + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", diff --git a/package.json b/package.json index fde2a72cb..e82d2c372 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stellarexplorer", "description": "Ledger explorer for the Stellar network", - "version": "1.0.10", + "version": "1.1.0-beta", "license": "Apache-2.0", "author": "Chris Hatch", "homepage": "https://steexp.com", @@ -20,6 +20,7 @@ "node": "10.x.x" }, "dependencies": { + "chart.js": "^2.7.3", "fetch-ponyfill": "^4.1.0", "node-localstorage": "^1.3.0", "promise": "8.0.1", diff --git a/src/App.css b/src/App.css index 94fec56c4..d8890c827 100644 --- a/src/App.css +++ b/src/App.css @@ -2,7 +2,7 @@ body, .navbar { color: #96a2b4; background-color: #3c4452 !important; - font-family: 'Noto Sans', 'Noto Sans CJK {SC, TC}', sans-serif; + font-family: "Noto Sans", "Noto Sans CJK {SC, TC}", sans-serif; } hr { @@ -35,7 +35,7 @@ table { } .monospace { - font-family: 'Noto Sans Mono', monospace; + font-family: "Noto Sans Mono", monospace; word-break: break-all; } @@ -61,7 +61,7 @@ table { .modal-body img { margin-top: 10px; } -.modal-body input[type=text] { +.modal-body input[type="text"] { width: 100%; } .modal-body input { @@ -115,19 +115,19 @@ table { overflow-wrap: break-word; } -[id*='-table'] { +[id*="-table"] { font-size: 14px !important; } -[id*='-table'] thead { +[id*="-table"] thead { color: #dce2ec; } -[id*='-table'] tbody { +[id*="-table"] tbody { color: #96a2b4; } -[id*='-table'] > tbody > tr > td { +[id*="-table"] > tbody > tr > td { padding-top: 10px; padding-bottom: 10px; border-top: 1px solid #4c5667; @@ -363,7 +363,8 @@ ul.navbar-nav .divider-vertical { outline: none; } -.Network-Selector button:not(:first-child), .Network-Selector span:not(:first-child) { +.Network-Selector button:not(:first-child), +.Network-Selector span:not(:first-child) { margin-left: 5px; } @@ -443,15 +444,15 @@ ul.navbar-nav .divider-vertical { border: none; } -div[id|='account-tabs-pane'] { +div[id|="account-tabs-pane"] { padding: 15px; } -a[id|='account-tabs-tab'] { +a[id|="account-tabs-tab"] { color: #96a2b4; } -a[id|='account-tabs-tab']:hover { +a[id|="account-tabs-tab"]:hover { color: #01c0c8 !important; background-color: #3c4452 !important; border: 1px solid #3c4452; @@ -459,7 +460,7 @@ a[id|='account-tabs-tab']:hover { border-color: #3c4452 !important; } -a[id|='account-tabs-tab'][aria-selected='true'] { +a[id|="account-tabs-tab"][aria-selected="true"] { color: #01c0c8 !important; } @@ -573,6 +574,14 @@ a:hover { transition: all 0.18s ease-in-out; } +.c3-axis-y .tick, +.c3-axis-x .tick, +.c3-axis-x-label, +.c3-axis-y-label, +.c3-legend-item text { + stroke: white !important; +} + /* Mobile Rules */ @@ -591,7 +600,7 @@ Mobile Rules table-layout: fixed; } - [id*='-table'] { + [id*="-table"] { font-size: 12px !important; } diff --git a/src/App.js b/src/App.js index af4912883..f578f90f8 100644 --- a/src/App.js +++ b/src/App.js @@ -86,6 +86,7 @@ const Anchors = Loadable('Anchors') const Assets = Loadable('Assets') const Effects = Loadable('Effects') const Exchanges = Loadable('Exchanges') +const Graphs = Loadable('Graphs') const InflationPools = Loadable('InflationPools') const Ledger = Loadable('Ledger') const Ledgers = Loadable('Ledgers') @@ -166,6 +167,7 @@ class App extends Component { + diff --git a/src/components/Graphs.js b/src/components/Graphs.js new file mode 100644 index 000000000..1cc6d8a0e --- /dev/null +++ b/src/components/Graphs.js @@ -0,0 +1,240 @@ +import React from 'react' +import Col from 'react-bootstrap/lib/Col' +import Grid from 'react-bootstrap/lib/Grid' +import Row from 'react-bootstrap/lib/Row' +import Tab from 'react-bootstrap/lib/Tab' +import Tabs from 'react-bootstrap/lib/Tabs' +import Chart from 'chart.js' + +class OpPie extends React.Component { + config = { + type: 'pie', + data: { + datasets: [ + { + data: [12, 40, 1, 20, 9], + backgroundColor: ['red', 'orange', 'yellow', 'green', 'blue'], + label: 'Op Type', + }, + ], + labels: ['Create', 'Offer', 'Set Options', 'Payment', 'Inflation'], + }, + options: { + responsive: true, + }, + } + + componentDidMount() { + var ctx = document.getElementById('opPieChart').getContext('2d') + new Chart(ctx, this.config) + } + + render() { + return + } +} + +class RecentActivity extends React.Component { + activityData = { + labels: ['1', '2', '3', '4', '5', '6', '7', 8, 9, 10, 11, 12], + datasets: [ + { + type: 'bar', + label: 'Tx Count', + backgroundColor: 'red', + data: [4, 1, 0, 20, 5, 3, 2, 1, 9, 1, 0, 3], + }, + { + type: 'bar', + label: 'Op Count', + backgroundColor: 'blue', + data: [22, 1, 0, 100, 17, 4, 2, 1, 18, 5, 0, 9], + }, + { + type: 'line', + label: 'Close Times', + borderColor: 'green', + data: [10, 7, 9, 2, 3, 5, 7, 1, 7, 8, 6], + yAxisID: 'y2', + }, + ], + } + + config = { + type: 'bar', + data: this.activityData, + options: { + title: { + display: true, + text: 'Recent Ledger Activity', + }, + tooltips: { + mode: 'index', + intersect: false, + }, + responsive: true, + scales: { + xAxes: [ + { + stacked: true, + }, + ], + yAxes: [ + { + id: 'y1', + stacked: true, + }, + { + id: 'y2', + stacked: true, + position: 'right', + }, + ], + }, + }, + } + + componentDidMount() { + var ctx = document.getElementById('recentActivityChart').getContext('2d') + new Chart(ctx, this.config) + } + + render() { + return + } +} + +class RecentTxCounts extends React.Component { + config = { + type: 'line', + data: { + labels: [ + '12/04', + '12/05', + '12/06', + '12/07', + '12/08', + '12/09', + '12/10', + '12/11', + '12/12', + '12/13', + '12/14', + '12/15', + '12/16', + '12/17', + ], + datasets: [ + { + label: 'Tx Count', + backgroundColor: 'blue', + borderColor: 'blue', + data: [ + 24591, + 26782, + 57697, + 35375, + 28487, + 21839, + 29707, + 89790, + 62123, + 64793, + 27525, + 9501, + 9530, + 43663, + ], + fill: false, + }, + ], + }, + options: { + responsive: true, + title: { + display: true, + text: 'Tx Count Chart', + }, + tooltips: { + mode: 'index', + intersect: false, + }, + hover: { + mode: 'nearest', + intersect: true, + }, + scales: { + xAxes: [ + { + display: true, + scaleLabel: { + display: true, + labelString: 'Date', + }, + }, + ], + yAxes: [ + { + display: true, + scaleLabel: { + display: true, + labelString: 'No. of Transactions', + }, + }, + ], + }, + }, + } + + componentDidMount() { + var ctx = document.getElementById('txCountChart').getContext('2d') + new Chart(ctx, this.config) + } + + render() { + return + } +} + +class Graphs extends React.Component { + state = { + key: undefined, // 'bar-simple', + } + + constructor(props, context) { + super(props, context) + this.handleSelect = this.handleSelect.bind(this) + } + + handleSelect(key) { + this.setState({key}) + } + render() { + return ( + + + + + + + + + + + + + + + + + + ) + } +} + +export default Graphs diff --git a/src/components/layout/Header.js b/src/components/layout/Header.js index 52b49678f..3542bfe26 100644 --- a/src/components/layout/Header.js +++ b/src/components/layout/Header.js @@ -46,9 +46,9 @@ class Header extends React.Component { />