structure migration (Next.js)

This commit is contained in:
jejolare 2024-09-18 16:51:45 +07:00
parent 4c50c7447c
commit e78a515307
62 changed files with 779 additions and 521 deletions

1
.gitignore vendored
View file

@ -18,6 +18,7 @@ node_modules
.env.test.local
.env.production.local
.env
.next
npm-debug.log*
yarn-debug.log*

5
next-env.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

29
next.config.js Normal file
View file

@ -0,0 +1,29 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config) => {
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.(".svg")
);
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/ // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: ["@svgr/webpack"]
}
);
return config;
},
}
export default nextConfig;

321
package-lock.json generated
View file

@ -23,10 +23,10 @@
"highcharts-react-official": "^3.2.1",
"http-proxy-middleware": "^2.0.6",
"nanoid": "^5.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"next": "^14.2.12",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-json-view-lite": "^1.1.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"sass": "^1.68.0",
"socket.io-client": "^4.7.5",
@ -3519,6 +3519,146 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
"integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
},
"node_modules/@next/env": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.12.tgz",
"integrity": "sha512-3fP29GIetdwVIfIRyLKM7KrvJaqepv+6pVodEbx0P5CaMLYBtx+7eEg8JYO5L9sveJO87z9eCReceZLi0hxO1Q=="
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.12.tgz",
"integrity": "sha512-crHJ9UoinXeFbHYNok6VZqjKnd8rTd7K3Z2zpyzF1ch7vVNKmhjv/V7EHxep3ILoN8JB9AdRn/EtVVyG9AkCXw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.12.tgz",
"integrity": "sha512-JbEaGbWq18BuNBO+lCtKfxl563Uw9oy2TodnN2ioX00u7V1uzrsSUcg3Ep9ce+P0Z9es+JmsvL2/rLphz+Frcw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.12.tgz",
"integrity": "sha512-qBy7OiXOqZrdp88QEl2H4fWalMGnSCrr1agT/AVDndlyw2YJQA89f3ttR/AkEIP9EkBXXeGl6cC72/EZT5r6rw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.12.tgz",
"integrity": "sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.12.tgz",
"integrity": "sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.12.tgz",
"integrity": "sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.12.tgz",
"integrity": "sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.12.tgz",
"integrity": "sha512-FAj2hMlcbeCV546eU2tEv41dcJb4NeqFlSXU/xL/0ehXywHnNpaYajOUvn3P8wru5WyQe6cTZ8fvckj/2XN4Vw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.12.tgz",
"integrity": "sha512-yu8QvV53sBzoIVRHsxCHqeuS8jYq6Lrmdh0briivuh+Brsp6xjg80MAozUsBTAV9KNmY08KlX0KYTWz1lbPzEg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -3628,14 +3768,6 @@
}
}
},
"node_modules/@remix-run/router": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz",
"integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -3959,6 +4091,20 @@
"url": "https://github.com/sponsors/gregberge"
}
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
},
"node_modules/@swc/helpers": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
"integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
"dependencies": {
"@swc/counter": "^0.1.3",
"tslib": "^2.4.0"
}
},
"node_modules/@testing-library/dom": {
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz",
@ -5958,6 +6104,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@ -6026,9 +6183,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001546",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz",
"integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==",
"version": "1.0.30001660",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz",
"integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==",
"funding": [
{
"type": "opencollective",
@ -6161,6 +6318,11 @@
"node": ">=0.10.0"
}
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@ -12940,6 +13102,55 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"node_modules/next": {
"version": "14.2.12",
"resolved": "https://registry.npmjs.org/next/-/next-14.2.12.tgz",
"integrity": "sha512-cDOtUSIeoOvt1skKNihdExWMTybx3exnvbFbb9ecZDIxlvIbREQzt9A5Km3Zn3PfU+IFjyYGsHS+lN9VInAGKA==",
"dependencies": {
"@next/env": "14.2.12",
"@swc/helpers": "0.5.5",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"graceful-fs": "^4.2.11",
"postcss": "8.4.31",
"styled-jsx": "5.1.1"
},
"bin": {
"next": "dist/bin/next"
},
"engines": {
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.2.12",
"@next/swc-darwin-x64": "14.2.12",
"@next/swc-linux-arm64-gnu": "14.2.12",
"@next/swc-linux-arm64-musl": "14.2.12",
"@next/swc-linux-x64-gnu": "14.2.12",
"@next/swc-linux-x64-musl": "14.2.12",
"@next/swc-win32-arm64-msvc": "14.2.12",
"@next/swc-win32-ia32-msvc": "14.2.12",
"@next/swc-win32-x64-msvc": "14.2.12"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
"@playwright/test": "^1.41.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
},
"@playwright/test": {
"optional": true
},
"sass": {
"optional": true
}
}
},
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@ -15045,9 +15256,9 @@
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
@ -15194,15 +15405,15 @@
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^18.2.0"
"react": "^18.3.1"
}
},
"node_modules/react-error-overlay": {
@ -15234,36 +15445,6 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz",
"integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==",
"dependencies": {
"@remix-run/router": "1.9.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz",
"integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==",
"dependencies": {
"@remix-run/router": "1.9.0",
"react-router": "6.16.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@ -15915,9 +16096,9 @@
}
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"dependencies": {
"loose-envify": "^1.1.0"
}
@ -16486,6 +16667,14 @@
"node": ">= 0.4"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -16675,6 +16864,28 @@
"webpack": "^5.0.0"
}
},
"node_modules/styled-jsx": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
"integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
"dependencies": {
"client-only": "0.0.1"
},
"engines": {
"node": ">= 12.0.0"
},
"peerDependencies": {
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"babel-plugin-macros": {
"optional": true
}
}
},
"node_modules/stylehacks": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",

View file

@ -2,6 +2,7 @@
"name": "zano-explorer",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
@ -17,10 +18,10 @@
"highcharts-react-official": "^3.2.1",
"http-proxy-middleware": "^2.0.6",
"nanoid": "^5.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"next": "^14.2.12",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-json-view-lite": "^1.1.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"sass": "^1.68.0",
"socket.io-client": "^4.7.5",
@ -30,11 +31,11 @@
},
"scripts": {
"postinstall": "cd server && npm i",
"client": "react-scripts start",
"client": "next dev",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"dev": "concurrently \"npx nodemon --exec tsx ./server/server.ts\" \"npm run client\"",
"dev": "npx nodemon --exec tsx ./server/server.ts",
"start": "tsx ./server/server.ts"
},
"eslintConfig": {

View file

@ -22,13 +22,18 @@ import Pool from "./schemes/Pool";
import Asset, { IAsset } from "./schemes/Asset";
import { ITransaction } from "./schemes/Transaction";
import BigNumber from "bignumber.js";
import next from "next";
import { rateLimit } from 'express-rate-limit';
// @ts-ignore
const __dirname = import.meta.dirname;
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const handle = nextApp.getRequestHandler();
const app = express();
const server = http.createServer(app);
export const io = new Server(server, { transports: ['websocket', 'polling'] });
@ -40,6 +45,9 @@ const requestsLimiter = rateLimit({
});
(async () => {
await nextApp.prepare();
await initDB();
await sequelize.authenticate();
await sequelize.sync();
@ -50,7 +58,11 @@ const requestsLimiter = rateLimit({
io.engine.on('headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = config.frontend_api
})
});
app.all('*', (req, res) => {
return handle(req, res);
});
app.use(express.static('dist'));
app.use(function (req, res, next) {
@ -1121,9 +1133,7 @@ const requestsLimiter = rateLimit({
let localTr: any;
while (!!(localTr = bl.transactions_details.splice(0, 1)[0])) {
console.time('txs details call');
let response = await get_tx_details(localTr.id);
console.timeEnd('txs details call');
let tx_info = response.data.result.tx_info;

View file

@ -1,42 +0,0 @@
import React, { Suspense } from "react";
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import Block from "./pages/Block/Block";
import { NET_MODE } from "./config/config";
const Blockchain = React.lazy(() => import("./pages/Blockchain/Blockchain"));
const AltBlocks = React.lazy(() => import("./pages/AltBlocks/AltBlocks"));
const Aliases = React.lazy(() => import("./pages/Aliases/Aliases"));
const API = React.lazy(() => import("./pages/API/API"));
const Transaction = React.lazy(() => import("./pages/Transaction/Transaction"));
const Charts = React.lazy(() => import("./pages/Charts/Charts"));
const ChartsPage = React.lazy(() => import("./pages/ChartPage/ChartPage"));
const Assets = React.lazy(() => import("./pages/Assets/Assets"));
const Asset = React.lazy(() => import("./pages/Asset/Asset"));
function App() {
return (
<Router>
<Suspense fallback={<></>}>
<Routes>
<Route path="/" element={<Blockchain />} />
<Route path="/alt-blocks" element={<AltBlocks />} />
<Route path="/aliases" element={<Aliases />} />
<Route path="/zano_api" element={<API />} />
<Route path="/block/:hash" element={<Block />} />
<Route path="/transaction/:hash" element={<Transaction />} />
<Route path="/alt-blocks/:hash" element={<Block alt />} />
{/* <Route path="/charts" element={<Charts />} />
<Route path="/charts/:name" element={<ChartsPage />} /> */}
{/* {NET_MODE === "TEST" &&
<Route path="/assets" element={<Assets />} />
} */}
<Route path="/assets" element={<Assets />} />
<Route path="/*" element={<Navigate to="/" />} />
{/* <Route path="/assets/:id" element={<Asset />} /> */}
</Routes>
</Suspense>
</Router>
);
}
export default App;

View file

@ -1,4 +1,4 @@
import "./Button.scss";
import styles from "./Button.module.scss";
interface ButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
wrapper?: boolean
@ -9,7 +9,7 @@ function Button(props: ButtonProps) {
return (
<button
className={`button ${wrapper ? "button__wrapper" : ""} ${className || ""}`}
className={`${styles.button} ${wrapper ? styles["button__wrapper"] : ""} ${className || ""}`}
{...restProps}
>
{children}

View file

@ -1,6 +1,6 @@
import "./CopyButton.scss";
import Button from "../UI/Button/Button";
import { ReactComponent as CopyImg } from "../../assets/images/UI/copy.svg";
import styles from "@/components/UI/CopyButton/CopyButton.module.scss";
import Button from "@/components/UI/Button/Button";
import CopyImg from "@/assets/images/UI/copy.svg";
import { useState } from "react";
@ -18,7 +18,7 @@ export default function CopyButton({ text }: { text: string }) {
}
return (
<Button className={copied ? "copy-button_copied" : undefined} onClick={onClick} wrapper>
<Button className={copied ? styles["copy-button_copied"] : undefined} onClick={onClick} wrapper>
<CopyImg />
</Button>
);

View file

@ -1,4 +1,4 @@
import "./Input.scss";
import styles from "./Input.module.scss";
interface InputProps extends React.HTMLProps<HTMLInputElement> {
onEnterPress?: () => void;
@ -19,7 +19,7 @@ function Input(props: InputProps) {
<input
{...restProps}
onKeyDown={onKeyDown}
className={"input " + (props.className || "")}
className={`${styles["input"]} ${props.className ? styles[props.className] : ""}`}
/>
)
}

View file

@ -1,4 +1,4 @@
import "./JsonViewStyled.scss";
import styles from "./JsonViewStyled.module.scss";
import { JsonView, darkStyles, Props } from "react-json-view-lite";
import 'react-json-view-lite/dist/index.css';
@ -6,14 +6,12 @@ function JsonViewStyled(props: Props) {
return (
<JsonView
{...props}
style={
{
style={{
...darkStyles,
container: "json__container",
basicChildStyle: "json__element",
numberValue: "json__number"
}
}
container: styles["json__container"],
basicChildStyle: styles["json__element"],
numberValue: styles["json__number"]
}}
/>
)
}

View file

@ -1,11 +1,16 @@
import "./Preloader.scss";
import styles from "./Preloader.module.scss";
function Preloader(props: { className?: string }) {
const { className } = props;
return (
<div className={"lds-ellipsis " + (className || "")}><div></div><div></div><div></div><div></div></div>
)
<div className={styles["lds-ellipsis"] + " " + (className || "")}>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
);
}
export default Preloader;

View file

@ -1,5 +1,5 @@
import Button from "../Button/Button";
import "./Switch.scss";
import styles from "./Switch.module.scss";
import { Dispatch, SetStateAction } from "react";
interface SwitchProps {
@ -16,17 +16,17 @@ export default function Switch({
setIsFirstSelected
}: SwitchProps) {
return (
<div className="switch">
<div className={styles["switch"]}>
<Button
wrapper
className={`switch__item ${isFirstSelected ? "switch__item_selected" : ""}`}
className={`${styles["switch__item"]} ${isFirstSelected ? styles["switch__item_selected"] : ""}`}
onClick={() => setIsFirstSelected(true)}
>
<p>{firstTitle}</p>
</Button>
<Button
wrapper
className={`switch__item ${!isFirstSelected ? "switch__item_selected" : ""}`}
className={`${styles["switch__item"]} ${!isFirstSelected ? styles["switch__item_selected"] : ""}`}
onClick={() => setIsFirstSelected(false)}
>
<p>{secondTitle}</p>

View file

@ -1,15 +1,14 @@
import { Link, LinkProps } from "react-router-dom";
import "./AliasText.scss";
import styles from "./AliasText.module.scss";
import Link, { LinkProps } from "next/link";
interface AliasTextProps extends LinkProps {
children: React.ReactNode
children: React.ReactNode,
}
function AliasText(props: AliasTextProps) {
const { children, ...restProps } = props;
return (
<span className="alias__text">
<span className={styles["alias__text"]}>
<Link {...restProps}>
{children}
</Link>

View file

@ -1,10 +1,10 @@
import "./Header.scss";
import { ReactComponent as LogoImg } from "../../../assets/images/UI/logo.svg";
import { ReactComponent as BurgerImg } from "../../../assets/images/UI/burger.svg";
import styles from "./Header.module.scss";
import LogoImg from "../../../assets/images/UI/logo.svg";
import BurgerImg from "../../../assets/images/UI/burger.svg";
import HeaderProps from "./Header.props";
import Button from "../../UI/Button/Button";
import { NET_MODE } from "../../../config/config";
import { Link } from "react-router-dom";
import Link from "next/link";
function Header(props: HeaderProps) {
const { page, burgerOpened, setBurgerOpened } = props;
@ -14,19 +14,19 @@ function Header(props: HeaderProps) {
<nav className={className}>
<Link
className={page === "Blockchain" ? "selected" : undefined}
to="/"
href="/"
>
Blockchain
</Link>
<Link
className={page === "Alt-blocks" ? "selected" : undefined}
to="/alt-blocks"
href="/alt-blocks"
>
Alt-blocks
</Link>
<Link
className={page === "Aliases" ? "selected" : undefined}
to="/aliases"
href="/aliases"
>
Aliases
</Link>
@ -43,19 +43,19 @@ function Header(props: HeaderProps) {
} */}
<Link
className={page === "Assets" ? "selected" : undefined}
to="/assets"
href="/assets"
>
Assets
</Link>
{/* <Link
className={page === "Charts" ? "selected" : undefined}
to="/charts"
href="/charts"
>
Charts
</Link> */}
<Link
className={page === "API" ? "selected" : undefined}
to="/zano_api"
href="/zano_api"
>
API
</Link>
@ -69,11 +69,11 @@ function Header(props: HeaderProps) {
}
return (
<header className="header">
<div className="header__top">
<div className="header__top__main">
<Link to="/">
<div className="header__logo">
<header className={styles["header"]}>
<div className={styles["header__top"]}>
<div className={styles["header__top__main"]}>
<Link href="/">
<div className={styles["header__logo"]}>
<LogoImg />
<p>ZANO</p>
</div>
@ -81,9 +81,9 @@ function Header(props: HeaderProps) {
<Nav />
</div>
<div className="header__top__right">
<div className={styles["header__top__right"]}>
<Link
to={NET_MODE === "TEST" ? "https://explorer.zano.org/" : "https://testnet-explorer.zano.org/"}
href={NET_MODE === "TEST" ? "https://explorer.zano.org/" : "https://testnet-explorer.zano.org/"}
target="_blank"
rel="noreferrer"
>
@ -93,13 +93,13 @@ function Header(props: HeaderProps) {
</Link>
<Button
onClick={() => setBurgerOpened(!burgerOpened)}
className="header__burger__button"
className={styles["header__burger__button"]}
>
<BurgerImg />
</Button>
</div>
</div>
{burgerOpened && <Nav className="header__nav__mobile" />}
{burgerOpened && <Nav className={styles["header__nav__mobile"]} />}
</header>
)
}

View file

@ -1,17 +1,18 @@
import Button from "../../UI/Button/Button";
import Input from "../../UI/Input/Input";
import "./InfoTopPanel.scss";
import { ReactComponent as SearchImg } from "../../../assets/images/UI/search.svg";
import { ReactComponent as BackImg } from "../../../assets/images/UI/back.svg";
import Button from "@/components/UI/Button/Button";
import Input from "@/components/UI/Input/Input";
import styles from "./InfoTopPanel.module.scss";
import SearchImg from "@/assets/images/UI/search.svg";
import BackImg from "@/assets/images/UI/back.svg";
import { useState } from "react";
import Fetch from "../../../utils/methods";
import { Link, useNavigate } from "react-router-dom";
import Fetch from "@/utils/methods";
import Link from "next/link";
import InfoTopPanelProps from "./InfoTopPanel.props";
import { useRouter } from 'next/router'
function InfoTopPanel(props: InfoTopPanelProps) {
const { burgerOpened, title, content, back, className, inputParams, contentNotHiding, inputDefaultClosed } = props;
const navigate = useNavigate();
const router = useRouter();
const [inputClosed, setInputClosed] = useState(inputDefaultClosed || false);
const [inputState, setInputState] = useState("");
@ -27,23 +28,23 @@ function InfoTopPanel(props: InfoTopPanelProps) {
if (searchInfo && typeof searchInfo === "object") {
const result = searchInfo.result;
if (result === "tx") {
return navigate("/transaction/" + input);
return router.push("/transaction/" + input);
}
if (result === "block") {
return navigate("/block/" + input);
return router.push("/block/" + input);
}
const txByKeyimageRes = await Fetch.getTxByKeyimage(input);
if (txByKeyimageRes && typeof txByKeyimageRes === "object" && txByKeyimageRes.data) {
return navigate("/transaction/" + input);
return router.push("/transaction/" + input);
}
if (input.match(/^\d+$/)) {
try {
const parsedHash = await Fetch.getHashByHeight(parseInt(input, 10));
if (parsedHash) {
return navigate("/block/" + parsedHash);
return router.push("/block/" + parsedHash);
}
} catch {
return setNoMatch(true);
@ -57,33 +58,30 @@ function InfoTopPanel(props: InfoTopPanelProps) {
async function onBackClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
event.preventDefault();
if (document.referrer) {
navigate(-1);
router.back();
} else {
navigate("/");
router.push("/");
}
}
return (
<div
className={
"blockchain__info__top" + " " + className + " "
+ (inputClosed ? "blockchain__input__closed" : "") + " "
+ (burgerOpened ? "info__top__hidden" : "")
`${styles.blockchain__info__top} ${className} ${inputClosed ? styles.blockchain__input__closed : ""} ${burgerOpened ? styles.info__top__hidden : ""}`
}
>
{back &&
<Link to="/" onClick={onBackClick}>
<div className="info__back">
<Link href="/" onClick={onBackClick}>
<div className={styles.info__back}>
<BackImg />
<p>Back</p>
</div>
</Link>
}
<div className={"info__top__title"}>
<h4 className={contentNotHiding ? "hiding_element" : undefined}>{title}</h4>
<div className={styles.info__top__title}>
<h4 className={contentNotHiding ? styles.hiding_element : undefined}>{title}</h4>
{!back &&
<div className={"info__top__content " + (!contentNotHiding ? "hiding_element" : undefined)}>
<div className={`${styles.info__top__content} ${!contentNotHiding ? styles.hiding_element : ""}`}>
{
content || <></>
}
@ -93,7 +91,7 @@ function InfoTopPanel(props: InfoTopPanelProps) {
<div className="info__top__input">
<div className={styles.info__top__input}>
{noMatch && <p>No matching records found!</p> }
{!inputParams ?
<Input

View file

@ -1,9 +1,9 @@
import "./JSONPopup.scss";
import styles from "./JSONPopup.module.scss";
import Popup from "../Popup/Popup";
import JSONPopupProps from "./JSONPopup.props";
import JsonViewStyled from "../../UI/JsonViewStyled/JsonViewStyled";
import Button from "../../UI/Button/Button";
import { ReactComponent as CrossImg } from "../../../assets/images/UI/cross.svg";
import CrossImg from "../../../assets/images/UI/cross.svg";
const JSONPopup = (props: JSONPopupProps) => {
const {
@ -23,9 +23,9 @@ const JSONPopup = (props: JSONPopupProps) => {
function PopupContent({ close }: { close: () => void }) {
return (
<div className="json_popup__content">
<div className={styles["json_popup__content"]}>
{!hideJson &&
<div className="json_popup__content__json">
<div className={styles["json_popup__content__json"]}>
<JsonViewStyled
data={json}
/>
@ -34,7 +34,7 @@ const JSONPopup = (props: JSONPopupProps) => {
<Button
wrapper
onClick={close}
className="json_popup__close_btn"
className={styles["json_popup__close_btn"]}
>
<CrossImg />
</Button>

View file

@ -1,12 +1,11 @@
import { NET_MODE } from "../../../config/config";
import Info from "../../../interfaces/state/Info";
import VisibilityInfo from "../../../interfaces/state/VisibilityInfo";
import Fetch from "../../../utils/methods";
import Utils from "../../../utils/utils";
import "./StatsPanel.scss";
import styles from "./StatsPanel.module.scss";
import { useState, useEffect, ReactNode } from "react";
import { socket } from "../../../utils/socket";
import { ReactComponent as BurnImg } from "../../../assets/images/UI/flame_ico.svg";
import BurnImg from "../../../assets/images/UI/flame_ico.svg";
function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: boolean }) {
const { visibilityInfo } = props;
@ -30,7 +29,6 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
};
}, []);
const transactions = info ? info.height + info.tx_count : undefined;
const infoHeight = Utils.formatNumber(info?.height, 0) || "...";
@ -55,23 +53,23 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
const isAmountString = typeof amount === "string";
return (
<div className="main__top__item">
<div className={styles["main__top__item"]}>
<div>
<p className="main__top__item__header">{title}</p>
<p className={styles["main__top__item__header"]}>{title}</p>
</div>
<div className="item__value">
<div className={styles["item__value"]}>
{isAmountString
? <p>{amount + (!customCurrency ? " ZANO" : "")}</p>
: amount
}
{percent &&
<div className="item__precent">
<div className={styles["item__precent"]}>
<div>
<p>
{percent + "%"}
</p>
</div>
<p className="item__precent__label">from total supply</p>
<p className={styles["item__precent__label"]}>from total supply</p>
</div>
}
</div>
@ -79,16 +77,15 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
)
}
function BottomItem(props: { title: string, children: React.ReactNode, style?: React.CSSProperties }) {
const { title, children, style } = props;
return (
<div className="main__bottom__item" style={style}>
<div className={styles["main__bottom__item"]} style={style}>
<div>
<p>{title}</p>
</div>
<div className="bottom__item__value">
<div className={styles["bottom__item__value"]}>
{children}
</div>
</div>
@ -97,7 +94,7 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
function InfoTop() {
return (
<div className="info__main__top">
<div className={styles["info__main__top"]}>
<TopItem
title="Staked Coins (est)"
amount={stackedCoins}
@ -115,7 +112,7 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
<TopItem
title="Zano Burned"
amount={
<div className="item__value__burn">
<div className={styles["item__value__burn"]}>
<BurnImg />
<p>{zanoBurned} ZANO</p>
</div>
@ -127,14 +124,14 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
return (
<>
<div className="blockchain__info__main">
<div className={styles["blockchain__info__main"]}>
{NET_MODE === "MAIN" && !props.noStats && <InfoTop/>}
<div className="info__main__bottom">
<div className={styles["info__main__bottom"]}>
<BottomItem title="Height">
<p className="item__text__large">{infoHeight}</p>
<p className={styles["item__text__large"]}>{infoHeight}</p>
</BottomItem>
<BottomItem title="Difficulty">
<div className="item__difficulty">
<div className={styles["item__difficulty"]}>
<div>
<p>PoS: {posDiff}</p>
</div>
@ -145,13 +142,13 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div>
</BottomItem>
<BottomItem title="Coins Emitted">
<p className="item__text__small">{coinsEmitted}</p>
<p className={styles["item__text__small"]}>{coinsEmitted}</p>
</BottomItem>
<BottomItem title="Transactions">
<p className="item__text__large">{transactionsString}</p>
<p className={styles["item__text__large"]}>{transactionsString}</p>
</BottomItem>
<BottomItem title="Hash Rate (approx):">
<div className="item__difficulty">
<div className={styles["item__difficulty"]}>
<div>
<p>PoS: {posValue} block/day</p>
</div>
@ -164,16 +161,14 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div>
</div>
<div className="info__main__mobile">
<div className={styles["info__main__mobile"]}>
{NET_MODE === "MAIN" && <InfoTop/>}
<div className="info__main__bottom">
<div className={styles["info__main__bottom"]}>
<BottomItem title="Height">
<p className="item__text__large">{infoHeight}</p>
<p className={styles["item__text__large"]}>{infoHeight}</p>
</BottomItem>
<BottomItem title="Difficulty">
<div className="item__difficulty">
<div className={styles["item__difficulty"]}>
<div>
<p>PoS: {posDiff}</p>
</div>
@ -184,14 +179,13 @@ function StatsPanel(props: { visibilityInfo?: VisibilityInfo | null, noStats?: b
</div>
</BottomItem>
<BottomItem title="Coins Emitted">
<p className="item__text__small">{coinsEmitted}</p>
<p className={styles["item__text__small"]}>{coinsEmitted}</p>
</BottomItem>
<BottomItem title="Transactions">
<p className="item__text__large">{transactionsString}</p>
<p className={styles["item__text__large"]}>{transactionsString}</p>
</BottomItem>
<BottomItem title="Hash Rate (approx):">
<div className="item__difficulty">
<div className={styles["item__difficulty"]}>
<div>
<p>PoS: {posValue} block/day</p>
</div>

View file

@ -1,5 +1,5 @@
import "./Table.scss";
import { ReactComponent as ArrowImg } from "../../../assets/images/UI/arrow.svg";
import styles from "./Table.module.scss";
import ArrowImg from "../../../assets/images/UI/arrow.svg";
import { nanoid } from "nanoid";
import Input from "../../UI/Input/Input";
import TableProps from "./Table.props";
@ -42,7 +42,7 @@ function Table(props: TableProps) {
return (
<div className={className}>
<table className={"table " + (!textWrap ? "table__text_nowrap" : "")}>
<table className={`${styles.table} ${!textWrap ? styles["table__text_nowrap"] : ""}`}>
<thead>
<tr>
{
@ -69,12 +69,12 @@ function Table(props: TableProps) {
</tbody>
</table>
{pagination &&
<div className="table__pagination">
<div className="table__pagination__pages">
<div className={styles["table__pagination"]}>
<div className={styles["table__pagination__pages"]}>
<p>Pages: </p>
<div>
<button
className={page === "1" ? "disabled" : undefined}
className={page === "1" ? styles.disabled : undefined}
onClick={() => changePage(-1)}
>
<ArrowImg />
@ -92,7 +92,7 @@ function Table(props: TableProps) {
/>
</div>
{pagesTotal && <p>Pages total: {pagesTotal}</p>}
<div className="table__pagination__blocks">
<div className={styles["table__pagination__blocks"]}>
<div>
<p>Items on page: </p>
<Input

View file

@ -1,19 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.scss';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View file

@ -1,13 +1,12 @@
import AliasText from "../../../../components/default/AliasText/AliasText";
import Table from "../../../../components/default/Table/Table";
import Block from "../../../../interfaces/state/Block";
import Info from "../../../../interfaces/state/Info";
import Fetch from "../../../../utils/methods";
import Utils from "../../../../utils/utils";
import "./LatestBlocks.scss";
import AliasText from "@/components/default/AliasText/AliasText";
import Table from "@/components/default/Table/Table";
import Block from "@/interfaces/state/Block";
import Info from "@/interfaces/state/Info";
import Fetch from "@/utils/methods";
import Utils from "@/utils/utils";
import styles from "./LatestBlocks.module.scss";
import { useState, useEffect } from "react";
import { socket } from "../../../../utils/socket";
import { Link } from "react-router-dom";
import Link from "next/link";
function LatestBlocks() {
@ -83,19 +82,19 @@ function LatestBlocks() {
const hashLink = hash ? "/block/" + hash : "/";
return [
<p>
<Link to={hashLink}>{e.height}</Link>
<Link href={hashLink}>{e.height}</Link>
{` (${e.type})`}
</p>,
Utils.formatTimestampUTC(e.timestamp),
Utils.timeElapsedString(e.timestamp),
`${e.size} bytes`,
e.transactions?.toString() || "0",
<AliasText to={hashLink}>{hash}</AliasText>
<AliasText href={hashLink}>{hash}</AliasText>
]
});
return (
<div className="blockchain__latest_blocks custom-scroll">
<div className={styles["blockchain__latest_blocks"] + " " + styles["custom-scroll"]}>
<h3>Latest Blocks</h3>
<Table
pagination

View file

@ -1,9 +1,9 @@
import "./TransactionPool.scss";
import styles from "./TransactionPool.module.scss";
import { useState, useEffect } from "react";
import Button from "../../../../components/UI/Button/Button";
import Table from "../../../../components/default/Table/Table";
import AliasText from "../../../../components/default/AliasText/AliasText";
import { socket } from "../../../../utils/socket";
import Button from "@/components/UI/Button/Button";
import Table from "@/components/default/Table/Table";
import AliasText from "@/components/default/AliasText/AliasText";
import { socket } from "@/utils/socket";
function TransactionPool() {
@ -78,12 +78,12 @@ function TransactionPool() {
timeAgo(new Date(element.timestamp).getTime()),
element.blob_size + " bytes",
parseInt(element.fee, 10)/10**12,
<AliasText to={`/transaction/${element.tx_id}`}>{element.tx_id}</AliasText>
<AliasText href={`/transaction/${element.tx_id}`}>{element.tx_id}</AliasText>
]));
return (
<div className="transaction_pool custom-scroll">
<div className="transation_pool__title">
<div className={styles["transaction_pool"] + " custom-scroll"}>
<div className={styles["transation_pool__title"]}>
<h3>Transaction Pool</h3>
<Button
style={ turnedOn ? { color: "#ff5252" } : { color: "#00c853" }}
@ -93,11 +93,11 @@ function TransactionPool() {
</Button>
</div>
{!turnedOn || !tableElements[0] ?
<div className="transation_pool__empty">
<div className={styles["transation_pool__empty"]}>
<p>Pool is empty</p>
</div> :
<Table
className="transaction__table"
className={styles["transaction__table"]}
headers={tableHeaders}
elements={tableElements}
/>

View file

@ -1,12 +1,12 @@
import "../../styles/Blockchain.scss";
import Header from "../../components/default/Header/Header";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import styles from "@/styles/Blockchain.module.scss";
import Header from "@/components/default/Header/Header";
import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import LatestBlocks from "./components/LatestBlocks/LatestBlocks";
import TransactionPool from "./components/TransactionPool/TransactionPool";
import { useEffect, useState } from "react";
import Fetch from "../../utils/methods";
import VisibilityInfo from "../../interfaces/state/VisibilityInfo";
import Fetch from "@/utils/methods";
import VisibilityInfo from "@/interfaces/state/VisibilityInfo";
function Blockchain() {
const [burgerOpened, setBurgerOpened] = useState(false);
@ -41,7 +41,7 @@ function Blockchain() {
}, []);
return (
<div className="blockchain">
<div className={styles["blockchain"]}>
<Header
page="Blockchain"
burgerOpened={burgerOpened}
@ -51,7 +51,7 @@ function Blockchain() {
burgerOpened={burgerOpened}
title="Blockchain"
content={
<div className="info__top__daemon">
<div className={styles["info__top__daemon"]}>
<p>Daemon state: {isOnline ? 'Online' : 'Offline'}</p>
<p>Default network fee: 0,01</p>
<p>Minimum network fee: 0,01</p>

View file

@ -1,14 +1,14 @@
import "../../styles/ChartPage.scss";
import styles from "@/styles/ChartPage.module.scss";
import { useEffect, useState } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts/highstock";
import { chartFontColor, chartOptions, chartRequestNames } from "../../utils/constants";
import { useNavigate, useParams } from "react-router-dom";
import ChartSeriesElem from "../../interfaces/common/ChartSeriesElem";
import Utils from "../../utils/utils";
import Preloader from "../../components/UI/Preloader/Preloader";
import { chartFontColor, chartOptions, chartRequestNames } from "@/utils/constants";
import ChartSeriesElem from "@/interfaces/common/ChartSeriesElem";
import Utils from "@/utils/utils";
import Preloader from "@/components/UI/Preloader/Preloader";
import { useRouter } from "next/router";
interface ChartInfo {
title: string;
@ -18,7 +18,6 @@ interface ChartInfo {
function ChartPage() {
const chartsInfo: { [key: string]: ChartInfo } = {
"avg-block-size": {
title: "Average Block Size",
@ -58,16 +57,16 @@ function ChartPage() {
// The only values that chartId param can have are the keys of the chartRequestNames object.
// Other values will cause redirect to the home page.
const { name: chartId } = useParams();
const router = useRouter();
const { name } = router.query;
const chartId: string | undefined = Array.isArray(name) ? name[0] : name;
const [chartSeries, setChartSeries] = useState<ChartSeriesElem[][]>([]);
const chartSeriesTitles = [
chartsInfo[chartId || ""]?.seriesTitle || "",
"Hash Rate 400",
"Difficulty 120"
]
const navigate = useNavigate();
];
useEffect(() => {
async function fetchChart() {
@ -80,14 +79,14 @@ function ChartPage() {
}
if (!(chartId && chartRequestNames[chartId])) {
navigate("/");
router.push("/");
} else {
fetchChart();
}
}, [chartId]);
return (
<div className="chart_page">
<div className={styles["chart_page"]}>
<Header
page="Charts"
burgerOpened={burgerOpened}
@ -98,11 +97,11 @@ function ChartPage() {
title="Charts"
back
/>
<div className="chart_page__chart__wrapper">
<div className={styles["chart_page__chart__wrapper"]}>
{
loading ?
(
<div className="chart_page__preloader">
<div className={styles["chart_page__preloader"]}>
<Preloader />
</div>
)
@ -133,7 +132,7 @@ function ChartPage() {
},
chart: {
...chartOptions.chart,
className: "chart_page__chart",
className: styles["chart_page__chart"],
height: 700,
},
rangeSelector: {

View file

@ -1,14 +1,14 @@
import "../../styles/Charts.scss";
import styles from "@/styles/Charts.module.scss";
import { useState, useEffect } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import HighchartsReact from "highcharts-react-official";
import Highcharts from "highcharts";
import { chartOptions } from "../../utils/constants";
import Utils from "../../utils/utils";
import ChartSeriesElem from "../../interfaces/common/ChartSeriesElem";
import Preloader from "../../components/UI/Preloader/Preloader";
import { Link } from "react-router-dom";
import { chartOptions } from "@/utils/constants";
import Utils from "@/utils/utils";
import ChartSeriesElem from "@/interfaces/common/ChartSeriesElem";
import Preloader from "@/components/UI/Preloader/Preloader";
import Link from "next/link";
function Charts() {
const [burgerOpened, setBurgerOpened] = useState(false);
@ -66,8 +66,8 @@ function Charts() {
} = props;
return (
<Link to={"/charts/" + requestTitle} className="charts__chart__wrapper">
<div className="charts__chart__title">
<Link href={"/charts/" + requestTitle} className={styles["charts__chart__wrapper"]}>
<div className={styles["charts__chart__title"]}>
<p>{title}</p>
</div>
<HighchartsReact
@ -86,7 +86,7 @@ function Charts() {
chart: {
...chartOptions.chart,
height: 280,
className: "charts__chart"
className: styles["charts__chart"]
},
tooltip: {
enabled: false
@ -124,7 +124,7 @@ function Charts() {
}
return (
<div className="charts">
<div className={styles["charts"]}>
<Header
page="Charts"
burgerOpened={burgerOpened}
@ -135,7 +135,7 @@ function Charts() {
title="Charts"
/>
{loaded ?
<div className="charts__container">
<div className={styles["charts__container"]}>
<Chart
title="Average Block Size"
requestTitle="avg-block-size"
@ -161,7 +161,7 @@ function Charts() {
requestTitle="confirm-trans-per-day"
/>
</div> :
<div className="charts__preloader">
<div className={styles["charts__preloader"]}>
<Preloader />
</div>
}

32
src/pages/_app.tsx Normal file
View file

@ -0,0 +1,32 @@
import './index.scss';
import { default as NextApp } from 'next/app';
import Head from 'next/head';
function App(data: any) {
const { Component, pageProps } = data;
return (
<>
<Head>
<title>Zano Trade</title>
<meta name="description" content="Peer-to-Peer Trading App on Zano blockchain" />
<meta name="viewport" content="width=device-width, initial-scale=1 user-scalable=no" />
<link rel="icon" href="/favicon.ico" />
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://trade.zano.org/"/>
<meta property="og:title" content="Zano Trade"/>
<meta property="og:description" content="Peer-to-Peer Trading App on Zano blockchain"/>
<meta property="og:image" content="social-banner.png"/>
<meta property="twitter:card" content="summary_large_image"/>
<meta property="twitter:url" content="https://trade.zano.org/"/>
<meta property="twitter:title" content="Zano Trade"/>
<meta property="twitter:description" content="Peer-to-Peer Trading App on Zano blockchain"/>
<meta property="twitter:image" content="social-banner.png"/>
</Head>
<Component {...pageProps} />
</>
);
}
export default App;

15
src/pages/_document.js Normal file
View file

@ -0,0 +1,15 @@
import { Html, Head, Main, NextScript } from 'next/document';
function Document() {
return (
<Html lang="en">
<Head />
<body className='custom-scroll'>
<Main />
<NextScript />
</body>
</Html>
)
}
export default Document;

View file

@ -1,11 +1,11 @@
import "../../styles/Aliases.scss";
import styles from "@/styles/Aliases.module.scss";
import { useState, useEffect, useCallback } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table";
import Alias from "../../interfaces/state/Alias";
import Fetch from "../../utils/methods";
import crownImg from "../../assets/images/UI/crown.svg";
import CrownImg from "../../assets/images/UI/crown.svg";
import CommonStatsPanel from "../../components/UI/CommonStatsPanel/CommonStatsPanel";
function Aliases() {
@ -63,10 +63,10 @@ function Aliases() {
function ShortAlias({ alias }: { alias: string }) {
return (
<div>
<div className="short_alias">
<div className={styles["short_alias"]}>
{alias}
<div className="short_alias__crown">
<img src={crownImg} alt="" />
<div className={styles["short_alias__crown"]}>
<img src={CrownImg} alt="" />
</div>
</div>
</div>
@ -84,7 +84,7 @@ function Aliases() {
];
return (
<div className="aliases">
<div className={styles["aliases"]}>
<Header
page="Aliases"
burgerOpened={burgerOpened}
@ -99,8 +99,8 @@ function Aliases() {
setState: setSearchState
}}
/>
<CommonStatsPanel pairs={statsPanelData} className="aliases__stats" />
<div className="aliases__table custom-scroll">
<CommonStatsPanel pairs={statsPanelData} className={styles["aliases__stats"]} />
<div className={`${styles["aliases__table"]} custom-scroll`}>
<Table
headers={tableHeaders}
elements={tableElements}

View file

@ -1,14 +1,13 @@
import "../../styles/AltBlocks.scss";
import styles from "@/styles/AltBlocks.module.scss";
import { useState, useEffect } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table";
import AliasText from "../../components/default/AliasText/AliasText";
import Block from "../../interfaces/state/Block";
import Fetch from "../../utils/methods";
import Utils from "../../utils/utils";
import { Link } from "react-router-dom";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "@/components/default/Table/Table";
import AliasText from "@/components/default/AliasText/AliasText";
import Block from "@/interfaces/state/Block";
import Fetch from "@/utils/methods";
import Utils from "@/utils/utils";
import Link from "next/link";
function AltBlocks() {
const [burgerOpened, setBurgerOpened] = useState(false);
@ -46,20 +45,19 @@ function AltBlocks() {
return [
<p>
<Link to={hashLink}>{e.height}</Link>
<Link href={hashLink}>{e.height}</Link>
{` (${e.type})`}
</p>,
Utils.formatTimestampUTC(e.timestamp),
Utils.formatTimestampUTC(e.timestamp),
`${e.size} bytes`,
e.transactions?.toString() || "0",
<AliasText to={hashLink}>{e.hash}</AliasText>
<AliasText href={hashLink}>{e.hash}</AliasText>
]
});
return (
<div className="alt_blocks">
<div className={styles["alt_blocks"]}>
<Header
page="Alt-blocks"
burgerOpened={burgerOpened}
@ -69,7 +67,7 @@ function AltBlocks() {
burgerOpened={burgerOpened}
title="Alt-blocks"
/>
<div className="alt_blocks__table custom-scroll">
<div className={`${styles["alt_blocks__table"]} custom-scroll`}>
<Table
headers={tableHeaders}
elements={tableElements}

View file

@ -1,8 +1,8 @@
import "../../styles/Asset.scss";
import styles from "@/styles/Asset.module.scss";
import { useState } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "@/components/default/Table/Table";
export default function Asset() {
const [burgerOpened, setBurgerOpened] = useState(false);
@ -27,13 +27,13 @@ export default function Asset() {
burgerOpened={burgerOpened}
title=""
back
className="block__info__top"
className={styles["block__info__top"]}
/>
<Table
columnsWidth={[50, 50]}
headers={["NAME", "AMOUNT"]}
elements={assetsRows}
className="asset__table"
className={styles["asset__table"]}
/>
</div>
)

View file

@ -1,17 +1,18 @@
import "../../styles/Assets.scss";
import styles from "@/styles/Assets.module.scss";
import { useState, useEffect, useRef, memo, useCallback } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import Table from "../../components/default/Table/Table";
import Fetch from "../../utils/methods";
import AliasText from "../../components/default/AliasText/AliasText";
import JSONPopup from "../../components/default/JSONPopup/JSONPopup";
import Switch from "../../components/UI/Switch/Switch";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import Table from "@/components/default/Table/Table";
import Fetch from "@/utils/methods";
import AliasText from "@/components/default/AliasText/AliasText";
import JSONPopup from "@/components/default/JSONPopup/JSONPopup";
import Switch from "@/components/UI/Switch/Switch";
import { nanoid } from "nanoid";
import Button from "../../components/UI/Button/Button";
import { useSearchParams } from "react-router-dom";
import CopyButton from "../../components/CopyButton/CopyButton";
import CommonStatsPanel from "../../components/UI/CommonStatsPanel/CommonStatsPanel";
import Button from "@/components/UI/Button/Button";
import CopyButton from "@/components/UI/CopyButton/CopyButton";
import CommonStatsPanel from "@/components/UI/CommonStatsPanel/CommonStatsPanel";
import { useRouter } from "next/router";
import { useSearchParams } from "next/navigation";
const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
@ -35,11 +36,11 @@ const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
return (
assetLink ? (
<div className="asset_popup__bottom">
<div className={styles["asset_popup__bottom"]}>
<Button onClick={copy}>{clicked ? 'Copied' : 'Copy asset link'}</Button>
</div>
) : (
<div className="asset_popup__not_found">
<div className={styles["asset_popup__not_found"]}>
<h3>
Asset not found or does not exist. For newly created assets, please allow one minute to appear in the explorer
</h3>
@ -49,8 +50,8 @@ const AssetPopupBottom = memo(({ assetId }: { assetId?: string }) => {
});
function Assets() {
const [searchParams, setSearchParams] = useSearchParams();
const searchParams = useSearchParams();
const router = useRouter();
const ZANO_ID =
"d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a";
@ -119,7 +120,10 @@ function Assets() {
useEffect(() => {
async function fetchParamAsset() {
const assetId = decodeURIComponent(searchParams.get("asset_id") || '');
const assetId = decodeURIComponent(
searchParams.get('asset_id') || ""
);
if (assetId) {
const response = await Fetch.getAssetDetails(assetId);
@ -196,9 +200,9 @@ function Assets() {
const tableElements = assets.map(e => [
e?.full_name || "",
e?.ticker || "",
<div className="assets__table_asset-id">
<div className={styles["assets__table_asset-id"]}>
<CopyButton text={e?.asset_id || ""} />
<AliasText to="/" onClick={(event) => onAssetClick(event, e)}>
<AliasText href="/" onClick={(event) => onAssetClick(event, e)}>
{e?.asset_id || ""}
</AliasText>
@ -212,7 +216,7 @@ function Assets() {
];
return (
<div className="assets">
<div className={styles["assets"]}>
<Header
page="Assets"
burgerOpened={burgerOpened}
@ -237,8 +241,8 @@ function Assets() {
/>
}
/>
<CommonStatsPanel pairs={statsPanelData} className={"assets__stats"} />
<div className="assets__table">
<CommonStatsPanel pairs={statsPanelData} className={styles["assets__stats"]} />
<div className={styles["assets__table"]}>
<Table
headers={tableHeaders}
elements={tableElements}
@ -265,9 +269,15 @@ function Assets() {
/>
),
onClose: () => {
if (searchParams.get("asset_id")) {
searchParams.delete("asset_id");
setSearchParams(searchParams);
if (searchParams.get('asset_id')) {
const removeQueryParam = (param: string) => {
const updatedQuery = router.query;
delete updatedQuery[param];
router.push({ query: updatedQuery }, undefined, { shallow: true });
}
removeQueryParam('asset_id');
}
setNotFountPopupState(false);
}

View file

@ -1,15 +1,16 @@
import "../../styles/Block.scss";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel";
import styles from "@/styles/Block.module.scss";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import { useEffect, useState } from "react";
import Table from "../../components/default/Table/Table";
import { ReactComponent as ArrowImg } from "../../assets/images/UI/arrow.svg";
import BlockInfo from "../../interfaces/common/BlockInfo";
import Utils from "../../utils/utils";
import { Link, useParams } from "react-router-dom";
import Fetch from "../../utils/methods";
import Popup from "../../components/default/Popup/Popup";
import Table from "@/components/default/Table/Table";
import ArrowImg from "@/assets/images/UI/arrow.svg";
import BlockInfo from "@/interfaces/common/BlockInfo";
import Utils from "@/utils/utils";
import Link from "next/link";
import Fetch from "@/utils/methods";
import Popup from "@/components/default/Popup/Popup";
import { useRouter } from "next/router";
interface Transaction {
hash: string;
@ -24,26 +25,25 @@ function Block(props: { alt?: boolean }) {
const [burgerOpened, setBurgerOpened] = useState(false);
const [jsonPopupOpened, setJsonPopupOpened] = useState(false);
const { hash } = useParams();
const router = useRouter();
const { hashQuery } = router.query;
const hash = Array.isArray(hashQuery) ? hashQuery[0] : hashQuery;
const tableHeaders = ["HASH", "FEE", "TOTAL AMOUNT", "SIZE"];
const [blockInfo, setBlockInfo] = useState<BlockInfo | null>(null);
const [transactions, setTransactions] = useState<Transaction[]>([]);
const tableElements = transactions.map(e => [
!alt
?
(
<Link to={"/transaction/" + (e.hash || "")} className="block__table__hash">
? (
<Link href={"/transaction/" + (e.hash || "")} className={styles["block__table__hash"]}>
{e.hash}
</Link>
)
:
(
<p className="block__table__hash">{e.hash}</p>
: (
<p className={styles["block__table__hash"]}>{e.hash}</p>
),
e.fee,
e.amount,
@ -79,7 +79,6 @@ function Block(props: { alt?: boolean }) {
console.log(result);
setBlockInfo({
type: result.type === "1" ? "PoW" : "PoS",
timestamp: result.timestamp || undefined,
@ -138,9 +137,9 @@ function Block(props: { alt?: boolean }) {
function JsonPopup() {
return (
<div className="block__info__json">
<div className={styles["block__info__json"]}>
{/* <button>x</button> */}
<div className="block__info__json__content">
<div className={styles["block__info__json__content"]}>
{blockInfo?.object_in_json || ''}
</div>
</div>
@ -148,7 +147,7 @@ function Block(props: { alt?: boolean }) {
}
return (
<div className="block__info">
<div className={styles["block__info"]}>
{jsonPopupOpened &&
<Popup
Content={JsonPopup}
@ -158,34 +157,34 @@ function Block(props: { alt?: boolean }) {
}}
scroll
blur
classList={["block__json_popup"]}
classList={[styles["block__json_popup"]]}
/>
}
<div className="block__info__title">
<div className={styles["block__info__title"]}>
<h2>Zano Block</h2>
<div>
{!alt && prevHash !== "" &&
<Link to={prevHash ? "/block/" + prevHash : "/"}>
<Link href={prevHash ? "/block/" + prevHash : "/"}>
<ArrowImg />
</Link>
}
<h2>{height}</h2>
{!alt && nextHash !== "" &&
<Link to={nextHash ? "/block/" + nextHash : "/"}>
<Link href={nextHash ? "/block/" + nextHash : "/"}>
<ArrowImg />
</Link>
}
</div>
<p>{hash?.toUpperCase() || ""}</p>
</div>
<div className="block__info__table">
<div className={styles["block__info__table"]}>
<table>
<tbody>
<tr>
<td>Type:</td>
<td>
<span
className={`block__info__type ${blockInfo?.type === "PoS" ? "type__pos" : "type__pow"}`}
className={`${styles["block__info__type"]} ${blockInfo?.type === "PoS" ? styles["type__pos"] : styles["type__pow"]}`}
>
{blockInfo?.type ?? "-"}
</span>
@ -197,7 +196,7 @@ function Block(props: { alt?: boolean }) {
</tr>
<tr>
<td>ID</td>
<td><Link to="/">{Utils.shortenAddress(blockInfo?.tx_id ?? "-")}</Link></td>
<td><Link href="/">{Utils.shortenAddress(blockInfo?.tx_id ?? "-")}</Link></td>
</tr>
<tr>
<td>Actual Timestamp (UTC):</td>
@ -249,7 +248,7 @@ function Block(props: { alt?: boolean }) {
</tr>
<tr>
<td>Previous ID:</td>
<td><Link to={`/block/${blockInfo?.prev_id}`}>{Utils.shortenAddress(blockInfo?.prev_id ?? "-")}</Link></td>
<td><Link href={`/block/${blockInfo?.prev_id}`}>{Utils.shortenAddress(blockInfo?.prev_id ?? "-")}</Link></td>
</tr>
<tr>
<td>Total block size, bytes:</td>
@ -287,7 +286,7 @@ function Block(props: { alt?: boolean }) {
<td>JSON data:</td>
<td>
<Link
to="/"
href="/"
onClick={(e) => {
e.preventDefault();
setJsonPopupOpened(true);
@ -306,7 +305,7 @@ function Block(props: { alt?: boolean }) {
}
return (
<div className="block">
<div className={styles["block"]}>
<Header
page="Blockchain"
burgerOpened={burgerOpened}
@ -316,11 +315,11 @@ function Block(props: { alt?: boolean }) {
burgerOpened={burgerOpened}
title=""
back
className="block__info__top"
className={styles["block__info__top"]}
/>
<StatsPanel noStats={true} />
<BlockInfo />
<div className="block__transactions">
<div className={styles["block__transactions"]}>
<h2>Transactions</h2>
<Table
headers={tableHeaders}

View file

@ -10,7 +10,7 @@
color: #ffffff;
}
#root {
#__next {
max-width: 1200px;
padding: 0 15px;
margin: 0 auto;
@ -72,42 +72,42 @@ button {
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Light.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-Light.ttf) format("opentype");
font-weight: 300;
}
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Regular.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-Regular.ttf) format("opentype");
font-weight: 400;
}
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Medium.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-Medium.ttf) format("opentype");
font-weight: 500;
}
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-SemiBold.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-SemiBold.ttf) format("opentype");
font-weight: 600;
}
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-Bold.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-Bold.ttf) format("opentype");
font-weight: 700;
}
@font-face {
font-family: 'Open Sans';
src: url(./assets/fonts/OpenSans/OpenSans-ExtraBold.ttf) format("opentype");
src: url(../assets/fonts/OpenSans/OpenSans-ExtraBold.ttf) format("opentype");
font-weight: 800;
}
@font-face {
font-family: "Inconsolata";
src: url(./assets/fonts/Inconsolata/Inconsolata-Regular.ttf) format("opentype");
src: url(../assets/fonts/Inconsolata/Inconsolata-Regular.ttf) format("opentype");
font-weight: 400;
}

9
src/pages/index.tsx Normal file
View file

@ -0,0 +1,9 @@
import Blockchain from './Blockchain/index';
function App() {
return (
<Blockchain />
);
}
export default App;

View file

@ -1,17 +1,18 @@
import "../../styles/Transaction.scss";
import styles from "@/styles/Transaction.module.scss";
import { useEffect, useState } from "react";
import Header from "../../components/default/Header/Header";
import InfoTopPanel from "../../components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "../../components/default/StatsPanel/StatsPanel";
import Table from "../../components/default/Table/Table";
import TransactionInfo, { Input } from "../../interfaces/common/TransactionInfo";
import Fetch from "../../utils/methods";
import { Link, useNavigate, useParams } from "react-router-dom";
import Utils from "../../utils/utils";
import Header from "@/components/default/Header/Header";
import InfoTopPanel from "@/components/default/InfoTopPanel/InfoTopPanel";
import StatsPanel from "@/components/default/StatsPanel/StatsPanel";
import Table from "@/components/default/Table/Table";
import TransactionInfo, { Input } from "@/interfaces/common/TransactionInfo";
import Fetch from "@/utils/methods";
import Link from "next/link";
import Utils from "@/utils/utils";
import { nanoid } from "nanoid";
import Popup from "../../components/default/Popup/Popup";
import { ReactComponent as CrossImg } from "../../assets/images/UI/cross.svg";
import Button from "../../components/UI/Button/Button";
import Popup from "@/components/default/Popup/Popup";
import CrossImg from "@/assets/images/UI/cross.svg";
import Button from "@/components/UI/Button/Button";
import { useRouter } from "next/router";
interface Block {
hash: string;
@ -19,8 +20,6 @@ interface Block {
timestamp: string;
}
function Transaction() {
const [burgerOpened, setBurgerOpened] = useState(false);
@ -30,9 +29,11 @@ function Transaction() {
const [popupState, setPopupState] = useState(false);
const [selectedInput, setSelectedInput] = useState<Input | null>(null);
const { hash } = useParams();
const router = useRouter();
const navigate = useNavigate();
const { hashQuery } = router.query;
const hash = Array.isArray(hashQuery) ? hashQuery[0] : hashQuery;
useEffect(() => {
async function fetchTransaction() {
@ -81,7 +82,6 @@ function Transaction() {
e.convertedAmount = Utils.convertENotationToString(existingAmount?.toExponential());
}
return {
amount: e.convertedAmount,
keyimage: e?.kimage_or_ms_id || "",
@ -129,7 +129,7 @@ function Transaction() {
event.preventDefault();
const result = await Fetch.getOutInfo(amount, index);
if (result.tx_id && typeof result.tx_id === "string") {
navigate("/transaction/" + result.tx_id);
router.push("/transaction/" + result.tx_id);
setPopupState(false);
}
}
@ -141,12 +141,12 @@ function Transaction() {
e.globalIndexes.length,
e.globalIndexes.length > 1 ?
(
<Link to="/" onClick={event => showIndexesClick(event, e)}>Show all...</Link>
<Link href="/" onClick={event => showIndexesClick(event, e)}>Show all...</Link>
)
:
(
<Link
to="/"
href="/"
onClick={event => onIndexClick(event, e.amount, e.globalIndexes[0])}
>
{e.globalIndexes[0] ?? ""}
@ -158,7 +158,7 @@ function Transaction() {
const outsRows = transactionInfo ? (
transactionInfo.outs.map(e => [
e.amount,
<div className="transaction__outs__keys">
<div className={styles["transaction__outs__keys"]}>
{e.publicKeys.map(e => <p>{e}</p>)}
</div>,
e.globalIndex
@ -170,7 +170,7 @@ function Transaction() {
const amount = selectedInput?.amount || 0;
return (
<div className="transaction__indexes__popup">
<div className={styles["transaction__indexes__popup"]}>
<h3>
Input ring set ({popupIndexes.length})
</h3>
@ -178,7 +178,7 @@ function Transaction() {
{popupIndexes.map(e =>
(
<Link
to="/"
href="/"
key={e}
onClick={event => onIndexClick(event, amount, e)}
>
@ -189,7 +189,7 @@ function Transaction() {
</div>
<Button
wrapper
className="popup__cross"
className={styles["popup__cross"]}
onClick={close}
>
<CrossImg />
@ -199,7 +199,7 @@ function Transaction() {
}
return (
<div className="transaction">
<div className={styles["transaction"]}>
<Header
burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened}
@ -209,10 +209,10 @@ function Transaction() {
burgerOpened={burgerOpened}
title=""
back
className="block__info__top"
className={styles["block__info__top"]}
/>
<StatsPanel noStats={true}/>
<div className="transaction__info">
<div className={styles["transaction__info"]}>
<h2>Transaction</h2>
<table>
<tbody>
@ -232,7 +232,7 @@ function Transaction() {
<td>Size</td>
<td>{(transactionInfo?.size || "0") + " bytes"}</td>
</tr>
<tr className={"transaction__confirmation" + (!transactionInfo?.confirmations ? ' transaction__unconfirmed' : '')}>
<tr className={styles["transaction__confirmation"] + (!transactionInfo?.confirmations ? ` ${styles["transaction__unconfirmed"]}` : '')}>
<td>Confirmations</td>
<td>{transactionInfo?.confirmations || "Unconfirmed"}</td>
</tr>
@ -244,7 +244,7 @@ function Transaction() {
<td>Mixin</td>
<td>{transactionInfo?.mixin || "-"}</td>
</tr>
<tr className="transaction__extra_items">
<tr className={styles["transaction__extra_items"]}>
<td>Extra items</td>
<td>
{
@ -263,15 +263,15 @@ function Transaction() {
</tbody>
</table>
</div>
<div className="transaction__table__wrapper">
<div className={styles["transaction__table__wrapper"]}>
<h3>From Block</h3>
<Table
className="custom-scroll"
className={styles["custom-scroll"]}
headers={[ "HASH", "HEIGHT", "TIMESTAMP (UTC)" ]}
elements={[[
<Link
className="table__hash"
to={blockOrigin?.hash ? "/block/" + blockOrigin.hash : "/"}
className={styles["table__hash"]}
href={blockOrigin?.hash ? "/block/" + blockOrigin.hash : "/"}
>
{blockOrigin?.hash}
</Link>,
@ -281,19 +281,19 @@ function Transaction() {
columnsWidth={[ 65, 15, 20 ]}
/>
</div>
<div className="transaction__table__wrapper">
<div className={styles["transaction__table__wrapper"]}>
<h3>{`Inputs ( ${insRows.length} )`}</h3>
<Table
className="transaction__table__inputs custom-scroll"
className={`${styles["transaction__table__inputs"]} ${styles["custom-scroll"]}`}
headers={[ "AMOUNT", "IMAGE / MULTISIG ID", "DECOY COUNT", "GLOBAL INDEX" ]}
elements={insRows}
columnsWidth={[ 15, 50, 15, 20 ]}
/>
</div>
<div className="transaction__table__wrapper">
<div className={styles["transaction__table__wrapper"]}>
<h3>{`Outputs ( ${outsRows.length} )`}</h3>
<Table
className="custom-scroll"
className={styles["custom-scroll"]}
headers={[ "AMOUNT", "KEY", "GLOBAL INDEX / MULTISIG ID" ]}
elements={outsRows}
columnsWidth={[ 15, 65, 20 ]}

View file

@ -1,10 +1,10 @@
import { useState } from "react";
import Header from "../../components/default/Header/Header";
import "../../styles/API.scss";
import APIItemValue from "../../interfaces/common/APIItemValue";
import Header from "@/components/default/Header/Header";
import styles from "@/styles/API.module.scss";
import APIItemValue from "@/interfaces/common/APIItemValue";
import examples from "./examples";
import { nanoid } from "nanoid";
import JsonViewStyled from "../../components/UI/JsonViewStyled/JsonViewStyled";
import JsonViewStyled from "@/components/UI/JsonViewStyled/JsonViewStyled";
interface APIEndpointItemProps {
title: string,
@ -92,14 +92,14 @@ function API() {
const { title, values, json } = props;
return (
<div className="api__item">
<div className="api__item__title">
<div className={styles["api__item"]}>
<div className={styles["api__item__title"]}>
<h3>{title}</h3>
</div>
<div className="api__item__units">
<div className={styles["api__item__units"]}>
{
values?.map(e => (
<div key={e.key} className="api__item__unit">
<div key={e.key} className={styles["api__item__unit"]}>
<div>
<p>{e.key}</p>
</div>
@ -111,7 +111,7 @@ function API() {
}
</div>
{json &&
<div className="api__item__json">
<div className={styles["api__item__json"]}>
<p>JSON Response</p>
<JsonViewStyled
data={json}
@ -137,16 +137,16 @@ function API() {
}
return (
<div className="api">
<div className={styles["api"]}>
<Header
page="API"
burgerOpened={burgerOpened}
setBurgerOpened={setBurgerOpened}
/>
<div className="api__title">
<div className={styles["api__title"]}>
<p>API Documentation</p>
</div>
<div className="api__items">
<div className={styles["api__items"]}>
<APIItem title="How to use" values={howToUseValues} />
{
endpoints.map(e => (

View file

@ -16,15 +16,22 @@
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
// "isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
"jsx": "preserve",
"incremental": true,
"isolatedModules": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"include": [
"src"
],
"exclude": ["src/setupProxy.js"],
"exclude": [
"src/setupProxy.js"
],
"ts-node": {
"esm": true
},
}
}