mirror of
https://github.com/status-im/market-proxy.git
synced 2026-08-27 11:51:11 +00:00
feat(market_chart)_: add test-api for cryptocompare chart
This commit is contained in:
+101
-1
@@ -1,2 +1,102 @@
|
||||
# Node.js dependencies
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Production builds
|
||||
build/
|
||||
dist/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Dependency directories
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
public
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
Generated
+294
-4
@@ -17,6 +17,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"recharts": "^2.10.3",
|
||||
"styled-components": "^6.1.8"
|
||||
}
|
||||
},
|
||||
@@ -3903,6 +3904,69 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
|
||||
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
|
||||
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
||||
@@ -6697,6 +6761,127 @@
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
|
||||
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"internmap": "1 - 2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
|
||||
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2.10.0 - 3",
|
||||
"d3-format": "1 - 3",
|
||||
"d3-interpolate": "1.2.0 - 3",
|
||||
"d3-time": "2.1.1 - 3",
|
||||
"d3-time-format": "2 - 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-shape": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
|
||||
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-path": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
|
||||
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-array": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-time-format": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
|
||||
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-time": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||
@@ -6791,6 +6976,12 @@
|
||||
"integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/decimal.js-light": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
|
||||
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
|
||||
@@ -8249,6 +8440,15 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
|
||||
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -9599,6 +9799,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
|
||||
@@ -12515,9 +12724,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -12534,7 +12743,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.8",
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
@@ -14333,6 +14542,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-smooth": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-equals": "^5.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
@@ -14384,6 +14608,44 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "2.15.4",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
|
||||
"integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"eventemitter3": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"react-is": "^18.3.1",
|
||||
"react-smooth": "^4.0.4",
|
||||
"recharts-scale": "^0.4.4",
|
||||
"tiny-invariant": "^1.3.1",
|
||||
"victory-vendor": "^36.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts-scale": {
|
||||
"version": "0.4.5",
|
||||
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
|
||||
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decimal.js-light": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/recursive-readdir": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
|
||||
@@ -16516,6 +16778,12 @@
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
@@ -17036,6 +17304,28 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "36.9.2",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
|
||||
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
|
||||
"license": "MIT AND ISC",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "^3.0.3",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-interpolate": "^3.0.1",
|
||||
"@types/d3-scale": "^4.0.2",
|
||||
"@types/d3-shape": "^3.1.0",
|
||||
"@types/d3-time": "^3.0.0",
|
||||
"@types/d3-timer": "^3.0.0",
|
||||
"d3-array": "^3.1.6",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-shape": "^3.1.0",
|
||||
"d3-time": "^3.0.0",
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-hr-time": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"recharts": "^2.10.3",
|
||||
"styled-components": "^6.1.8"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -4,6 +4,7 @@ import CryptoDataTable from './components/CryptoDataTable';
|
||||
import Layout from './components/Layout';
|
||||
import Tabs from './components/Tabs';
|
||||
import Stats from './components/Stats';
|
||||
import TokenDetails from './components/TokenDetails';
|
||||
import { Loading, Error } from './components/LoadingAndErrors';
|
||||
import useCoinGeckoData from './hooks/useCoinGeckoData';
|
||||
import useCoinGeckoPriceData from './hooks/useCoinGeckoPriceData';
|
||||
@@ -21,6 +22,9 @@ function App() {
|
||||
// Endpoint state for token data
|
||||
const [tokenEndpoint, setTokenEndpoint] = useState('leaderboard');
|
||||
|
||||
// Selected token state
|
||||
const [selectedToken, setSelectedToken] = useState(null);
|
||||
|
||||
// CoinGecko data with token endpoint parameter
|
||||
const {
|
||||
coinGeckoData,
|
||||
@@ -46,6 +50,15 @@ function App() {
|
||||
|
||||
const error = (activeMainTab === 'CoinGecko' && (coinGeckoError || coinGeckoPriceError));
|
||||
|
||||
// Handle token selection
|
||||
const handleTokenClick = (token) => {
|
||||
setSelectedToken(token);
|
||||
};
|
||||
|
||||
const handleCloseTokenDetails = () => {
|
||||
setSelectedToken(null);
|
||||
};
|
||||
|
||||
// Get the current active data and price data based on active tab
|
||||
const getCurrentData = () => {
|
||||
if (activeMainTab === 'CoinGecko') {
|
||||
@@ -198,8 +211,17 @@ function App() {
|
||||
priceData={activePrice}
|
||||
source={source}
|
||||
priceEndpoint={priceEndpoint}
|
||||
onTokenClick={handleTokenClick}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
|
||||
{/* Token details modal */}
|
||||
{selectedToken && (
|
||||
<TokenDetails
|
||||
token={selectedToken}
|
||||
onClose={handleCloseTokenDetails}
|
||||
/>
|
||||
)}
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,15 @@ const SwapButton = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
const TableRow = styled.tr`
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
`;
|
||||
|
||||
const PriceContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -138,7 +147,7 @@ const PageInfo = styled.div`
|
||||
color: #616E85;
|
||||
`;
|
||||
|
||||
function CryptoDataTable({ data, priceData, source, priceEndpoint }) {
|
||||
function CryptoDataTable({ data, priceData, source, priceEndpoint, onTokenClick }) {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
||||
@@ -257,7 +266,7 @@ function CryptoDataTable({ data, priceData, source, priceEndpoint }) {
|
||||
const tokenPriceData = priceData[symbol] || priceData[id];
|
||||
|
||||
return (
|
||||
<tr key={id}>
|
||||
<TableRow key={id} onClick={() => onTokenClick && onTokenClick(item)}>
|
||||
<Td>{startIndex + index + 1}</Td>
|
||||
<Td>
|
||||
<TokenCell>
|
||||
@@ -316,9 +325,9 @@ function CryptoDataTable({ data, priceData, source, priceEndpoint }) {
|
||||
</PriceContainer>
|
||||
</Td>
|
||||
<Td>
|
||||
<SwapButton>Swap</SwapButton>
|
||||
<SwapButton onClick={(e) => e.stopPropagation()}>Swap</SwapButton>
|
||||
</Td>
|
||||
</tr>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import useTokenHistory, { getApiRequestCounter, subscribeToApiCounter } from '../hooks/useTokenHistory';
|
||||
|
||||
const Container = styled.div`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
`;
|
||||
|
||||
const Modal = styled.div`
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding: 24px;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const CloseButton = styled.button`
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
|
||||
&:hover {
|
||||
color: #333;
|
||||
}
|
||||
`;
|
||||
|
||||
const TokenHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
`;
|
||||
|
||||
const TokenImage = styled.img`
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
`;
|
||||
|
||||
const TokenInfo = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const TokenName = styled.h2`
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const TokenSymbol = styled.span`
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const ApiCounter = styled.div`
|
||||
margin-left: auto;
|
||||
background: #f0f8ff;
|
||||
border: 1px solid #3861FB;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
color: #3861FB;
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
const PriceInfo = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
`;
|
||||
|
||||
const PriceCard = styled.div`
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
`;
|
||||
|
||||
const PriceLabel = styled.div`
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const PriceValue = styled.div`
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: ${props => props.$isPositive ? '#16C784' : props.$isNegative ? '#EA3943' : '#333'};
|
||||
`;
|
||||
|
||||
const ChartContainer = styled.div`
|
||||
margin-bottom: 24px;
|
||||
`;
|
||||
|
||||
const ChartTitle = styled.h3`
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const TimeRangeSelector = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const TimeRangeButton = styled.button`
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #ddd;
|
||||
background: ${props => props.$active ? '#3861FB' : 'white'};
|
||||
color: ${props => props.$active ? 'white' : '#333'};
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background: ${props => props.$active ? '#3861FB' : '#f5f5f5'};
|
||||
}
|
||||
`;
|
||||
|
||||
const ChartWrapper = styled.div`
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const LoadingChart = styled.div`
|
||||
height: 400px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
color: #666;
|
||||
`;
|
||||
|
||||
const ErrorChart = styled.div`
|
||||
height: 400px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
color: #EA3943;
|
||||
`;
|
||||
|
||||
const timeRanges = [
|
||||
{ key: 'week', label: '7D', limit: 7 * 24, aggregate: 1, allData: false },
|
||||
{ key: 'month', label: '1M', limit: 30 * 24, aggregate: 2, allData: false },
|
||||
{ key: 'halfyear', label: '6M', limit: 180, aggregate: 1, allData: false },
|
||||
{ key: 'year', label: '1Y', limit: 365, aggregate: 1, allData: false },
|
||||
{ key: 'all', label: 'ALL', limit: 1, aggregate: 12, allData: true }
|
||||
];
|
||||
|
||||
function TokenDetails({ token, onClose }) {
|
||||
const [selectedTimeRange, setSelectedTimeRange] = useState('week');
|
||||
const [apiRequestCount, setApiRequestCount] = useState(getApiRequestCounter());
|
||||
|
||||
// Subscribe to API request counter changes
|
||||
useEffect(() => {
|
||||
const unsubscribe = subscribeToApiCounter(setApiRequestCount);
|
||||
return unsubscribe;
|
||||
}, []);
|
||||
|
||||
// Use hook to get historical data
|
||||
const { data: chartData, isLoading: isLoadingChart, error: chartError } = useTokenHistory(token.id, selectedTimeRange);
|
||||
|
||||
const formatNumber = (num) => {
|
||||
if (!num && num !== 0) return '—';
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(num);
|
||||
};
|
||||
|
||||
const formatPercentage = (value) => {
|
||||
if (!value && value !== 0) return '—';
|
||||
return `${value >= 0 ? '+' : ''}${value.toFixed(2)}%`;
|
||||
};
|
||||
|
||||
const handleTimeRangeChange = (timeRange) => {
|
||||
setSelectedTimeRange(timeRange);
|
||||
};
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
// Extract token data depending on the source
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
symbol,
|
||||
image,
|
||||
current_price,
|
||||
market_cap,
|
||||
total_volume,
|
||||
price_change_percentage_24h,
|
||||
high_24h,
|
||||
low_24h,
|
||||
circulating_supply,
|
||||
total_supply
|
||||
} = token;
|
||||
|
||||
return (
|
||||
<Container onClick={handleOverlayClick}>
|
||||
<Modal>
|
||||
<CloseButton onClick={onClose}>×</CloseButton>
|
||||
|
||||
<TokenHeader>
|
||||
<TokenImage src={image} alt={name} />
|
||||
<TokenInfo>
|
||||
<TokenName>{name}</TokenName>
|
||||
<TokenSymbol>{symbol}</TokenSymbol>
|
||||
</TokenInfo>
|
||||
<ApiCounter>
|
||||
API Requests: {apiRequestCount}
|
||||
</ApiCounter>
|
||||
</TokenHeader>
|
||||
|
||||
<PriceInfo>
|
||||
<PriceCard>
|
||||
<PriceLabel>Current Price</PriceLabel>
|
||||
<PriceValue>{formatNumber(current_price)}</PriceValue>
|
||||
</PriceCard>
|
||||
|
||||
<PriceCard>
|
||||
<PriceLabel>24h Change</PriceLabel>
|
||||
<PriceValue
|
||||
$isPositive={price_change_percentage_24h >= 0}
|
||||
$isNegative={price_change_percentage_24h < 0}
|
||||
>
|
||||
{formatPercentage(price_change_percentage_24h)}
|
||||
</PriceValue>
|
||||
</PriceCard>
|
||||
|
||||
<PriceCard>
|
||||
<PriceLabel>Market Cap</PriceLabel>
|
||||
<PriceValue>{formatNumber(market_cap)}</PriceValue>
|
||||
</PriceCard>
|
||||
|
||||
<PriceCard>
|
||||
<PriceLabel>24h Volume</PriceLabel>
|
||||
<PriceValue>{formatNumber(total_volume)}</PriceValue>
|
||||
</PriceCard>
|
||||
|
||||
<PriceCard>
|
||||
<PriceLabel>24h High</PriceLabel>
|
||||
<PriceValue>{formatNumber(high_24h)}</PriceValue>
|
||||
</PriceCard>
|
||||
|
||||
<PriceCard>
|
||||
<PriceLabel>24h Low</PriceLabel>
|
||||
<PriceValue>{formatNumber(low_24h)}</PriceValue>
|
||||
</PriceCard>
|
||||
</PriceInfo>
|
||||
|
||||
<ChartContainer>
|
||||
<ChartTitle>Price History</ChartTitle>
|
||||
|
||||
<TimeRangeSelector>
|
||||
{timeRanges.map(range => (
|
||||
<TimeRangeButton
|
||||
key={range.key}
|
||||
$active={selectedTimeRange === range.key}
|
||||
onClick={() => handleTimeRangeChange(range.key)}
|
||||
>
|
||||
{range.label}
|
||||
</TimeRangeButton>
|
||||
))}
|
||||
</TimeRangeSelector>
|
||||
|
||||
<ChartWrapper>
|
||||
{isLoadingChart ? (
|
||||
<LoadingChart>Loading chart data...</LoadingChart>
|
||||
) : chartError ? (
|
||||
<ErrorChart>{chartError}</ErrorChart>
|
||||
) : (
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={chartData}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tick={{ fontSize: 12 }}
|
||||
/>
|
||||
<YAxis
|
||||
domain={['dataMin', 'dataMax']}
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
/>
|
||||
<Tooltip
|
||||
formatter={(value) => [`$${value.toFixed(2)}`, 'Price']}
|
||||
labelFormatter={(label) => `Date: ${label}`}
|
||||
/>
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey="price"
|
||||
stroke="#3861FB"
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
/>
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
)}
|
||||
</ChartWrapper>
|
||||
</ChartContainer>
|
||||
</Modal>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
export default TokenDetails;
|
||||
@@ -0,0 +1,87 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
// Global counter for CoinGecko API requests
|
||||
let globalApiRequestCounter = 0;
|
||||
const apiRequestListeners = new Set();
|
||||
|
||||
// Function to increment the counter
|
||||
const incrementApiCounter = () => {
|
||||
globalApiRequestCounter++;
|
||||
// Notify all listeners about the change
|
||||
apiRequestListeners.forEach(listener => listener(globalApiRequestCounter));
|
||||
};
|
||||
|
||||
// Function to get current counter value
|
||||
export const getApiRequestCounter = () => globalApiRequestCounter;
|
||||
|
||||
// Function to subscribe to counter changes
|
||||
export const subscribeToApiCounter = (listener) => {
|
||||
apiRequestListeners.add(listener);
|
||||
return () => apiRequestListeners.delete(listener);
|
||||
};
|
||||
|
||||
const useTokenHistory = (tokenId, timeRange) => {
|
||||
const [data, setData] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
const fetchHistoricalData = useCallback(async () => {
|
||||
if (!tokenId || !timeRange) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// Parameters for different time ranges
|
||||
const timeRanges = {
|
||||
week: { days: 7, interval: 'hourly' },
|
||||
month: { days: 30, interval: 'hourly' },
|
||||
halfyear: { days: 180, interval: 'daily' },
|
||||
year: { days: 365, interval: 'daily' },
|
||||
all: { days: 'max', interval: 'daily' }
|
||||
};
|
||||
|
||||
const range = timeRanges[timeRange];
|
||||
if (!range) {
|
||||
throw new Error('Invalid time range');
|
||||
}
|
||||
|
||||
// Increment counter before making the request
|
||||
incrementApiCounter();
|
||||
|
||||
// Use CoinGecko API to get historical data
|
||||
const response = await fetch(
|
||||
`https://api.coingecko.com/api/v3/coins/${tokenId}/market_chart?vs_currency=usd&days=${range.days}&interval=${range.interval}`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// Transform data into chart format
|
||||
const formattedData = result.prices?.map(([timestamp, price]) => ({
|
||||
date: new Date(timestamp).toLocaleDateString(),
|
||||
fullDate: new Date(timestamp).toLocaleString(),
|
||||
price: price,
|
||||
timestamp: timestamp
|
||||
})) || [];
|
||||
|
||||
setData(formattedData);
|
||||
} catch (err) {
|
||||
console.error('Error fetching historical data:', err);
|
||||
setError(err.message || 'Failed to fetch historical data');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [tokenId, timeRange]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchHistoricalData();
|
||||
}, [fetchHistoricalData]);
|
||||
|
||||
return { data, isLoading, error, refetch: fetchHistoricalData };
|
||||
};
|
||||
|
||||
export default useTokenHistory;
|
||||
Reference in New Issue
Block a user